1use schemars::schema::InstanceType;
2use serde::Serialize;
3use thiserror::Error;
4
5#[derive(Debug, PartialEq, Serialize)]
9pub struct Change {
10 pub path: String,
12 pub change: ChangeKind,
14}
15
16#[derive(Debug, PartialEq, Serialize)]
18pub enum ChangeKind {
19 TypeAdd {
21 added: JsonSchemaType,
23 },
24 TypeRemove {
26 removed: JsonSchemaType,
28 },
29 ConstAdd {
31 added: serde_json::Value,
33 },
34 ConstRemove {
36 removed: serde_json::Value,
38 },
39 PropertyAdd {
42 lhs_additional_properties: bool,
44 added: String,
46 },
47 PropertyRemove {
50 lhs_additional_properties: bool,
52 removed: String,
54 },
55 RangeAdd {
57 added: Range,
59 },
60 RangeRemove {
62 removed: Range,
64 },
65 RangeChange {
67 old_value: Range,
69 new_value: Range,
71 },
72 TupleToArray {
78 old_length: usize,
80 },
81 ArrayToTuple {
87 new_length: usize,
89 },
90 TupleChange {
97 new_length: usize,
99 },
100 RequiredRemove {
102 property: String,
104 },
105 RequiredAdd {
107 property: String,
109 },
110 FormatAdd {
112 added: String,
114 },
115 FormatRemove {
117 removed: String,
119 },
120 FormatChange {
122 old_format: String,
124 new_format: String,
126 },
127 EnumAdd {
129 added: serde_json::Value,
131 lhs_has_no_enum: bool,
134 },
135 EnumRemove {
137 removed: serde_json::Value,
139 rhs_has_no_enum: bool,
142 },
143 PatternAdd {
145 added: String,
147 },
148 PatternRemove {
150 removed: String,
152 },
153 PatternChange {
155 old_pattern: String,
157 new_pattern: String,
159 },
160 MinLengthAdd {
162 added: u32,
164 },
165 MinLengthRemove {
167 removed: u32,
169 },
170 MinLengthChange {
172 old_value: u32,
174 new_value: u32,
176 },
177 MaxLengthAdd {
179 added: u32,
181 },
182 MaxLengthRemove {
184 removed: u32,
186 },
187 MaxLengthChange {
189 old_value: u32,
191 new_value: u32,
193 },
194}
195
196impl ChangeKind {
197 pub fn is_breaking(&self) -> bool {
205 match self {
206 Self::TypeAdd { .. } => false,
207 Self::TypeRemove { .. } => true,
208 Self::ConstAdd { .. } => true,
209 Self::ConstRemove { .. } => false,
210 Self::PropertyAdd {
211 lhs_additional_properties,
212 ..
213 } => *lhs_additional_properties,
214 Self::PropertyRemove {
215 lhs_additional_properties,
216 ..
217 } => !*lhs_additional_properties,
218 Self::RangeAdd { .. } => true,
219 Self::RangeRemove { .. } => false,
220 Self::RangeChange {
221 old_value,
222 new_value,
223 } => match (old_value, new_value) {
224 (Range::ExclusiveMinimum(exc), Range::Minimum(min)) if exc >= min => false,
225 (Range::ExclusiveMaximum(exc), Range::Maximum(max)) if exc <= max => false,
226 (Range::Minimum(l), Range::Minimum(r)) if l >= r => false,
227 (Range::ExclusiveMinimum(l), Range::ExclusiveMinimum(r)) if l >= r => false,
228 (Range::Maximum(l), Range::Maximum(r)) if l <= r => false,
229 (Range::ExclusiveMaximum(l), Range::ExclusiveMaximum(r)) if l <= r => false,
230 _ => true,
231 },
232 Self::TupleToArray { .. } => false,
233 Self::ArrayToTuple { .. } => true,
234 Self::TupleChange { .. } => true,
235 Self::RequiredRemove { .. } => false,
236 Self::RequiredAdd { .. } => true,
237 Self::FormatAdd { .. } => true,
238 Self::FormatRemove { .. } => false,
239 Self::FormatChange { .. } => true,
240 Self::EnumAdd {
243 lhs_has_no_enum, ..
244 } => *lhs_has_no_enum,
245 Self::EnumRemove {
248 rhs_has_no_enum, ..
249 } => !rhs_has_no_enum,
250 Self::PatternAdd { .. } => true,
253 Self::PatternRemove { .. } => false,
254 Self::PatternChange { .. } => true,
255 Self::MinLengthAdd { .. } => true,
257 Self::MinLengthRemove { .. } => false,
258 Self::MinLengthChange {
259 old_value,
260 new_value,
261 } => new_value > old_value,
262 Self::MaxLengthAdd { .. } => true,
264 Self::MaxLengthRemove { .. } => false,
265 Self::MaxLengthChange {
266 old_value,
267 new_value,
268 } => new_value < old_value,
269 }
270 }
271}
272
273#[derive(Error, Debug)]
275pub enum Error {
276 #[error("failed to parse schema")]
281 Serde(#[from] serde_json::Error),
282}
283
284#[derive(Serialize, Clone, Ord, Eq, PartialEq, PartialOrd, Debug)]
286#[allow(missing_docs)]
287pub enum JsonSchemaType {
288 #[serde(rename = "string")]
289 String,
290 #[serde(rename = "number")]
291 Number,
292 #[serde(rename = "integer")]
293 Integer,
294 #[serde(rename = "object")]
295 Object,
296 #[serde(rename = "array")]
297 Array,
298 #[serde(rename = "boolean")]
299 Boolean,
300 #[serde(rename = "null")]
301 Null,
302}
303
304impl From<JsonSchemaType> for InstanceType {
305 fn from(t: JsonSchemaType) -> Self {
306 match t {
307 JsonSchemaType::String => InstanceType::String,
308 JsonSchemaType::Number => InstanceType::Number,
309 JsonSchemaType::Integer => InstanceType::Integer,
310 JsonSchemaType::Object => InstanceType::Object,
311 JsonSchemaType::Array => InstanceType::Array,
312 JsonSchemaType::Boolean => InstanceType::Boolean,
313 JsonSchemaType::Null => InstanceType::Null,
314 }
315 }
316}
317
318impl From<InstanceType> for JsonSchemaType {
319 fn from(t: InstanceType) -> Self {
320 match t {
321 InstanceType::String => JsonSchemaType::String,
322 InstanceType::Number => JsonSchemaType::Number,
323 InstanceType::Integer => JsonSchemaType::Integer,
324 InstanceType::Object => JsonSchemaType::Object,
325 InstanceType::Array => JsonSchemaType::Array,
326 InstanceType::Boolean => JsonSchemaType::Boolean,
327 InstanceType::Null => JsonSchemaType::Null,
328 }
329 }
330}
331
332#[derive(Serialize, Clone, PartialEq, PartialOrd, Debug)]
334#[serde(rename_all = "camelCase")]
335#[allow(missing_docs)]
336pub enum Range {
337 Minimum(f64),
338 Maximum(f64),
339 ExclusiveMinimum(f64),
340 ExclusiveMaximum(f64),
341}
342
343#[cfg(test)]
344mod tests {
345 use super::*;
346 #[test]
347 fn is_range_change_breaking() {
348 assert!(!ChangeKind::RangeChange {
349 old_value: Range::Minimum(1.0),
350 new_value: Range::Minimum(1.0),
351 }
352 .is_breaking());
353
354 assert!(ChangeKind::RangeChange {
355 old_value: Range::Minimum(1.0),
356 new_value: Range::Minimum(2.0),
357 }
358 .is_breaking());
359
360 assert!(!ChangeKind::RangeChange {
361 old_value: Range::Minimum(2.0),
362 new_value: Range::Minimum(1.0),
363 }
364 .is_breaking());
365
366 assert!(ChangeKind::RangeChange {
367 old_value: Range::Minimum(1.0),
368 new_value: Range::ExclusiveMinimum(1.0),
369 }
370 .is_breaking());
371
372 assert!(ChangeKind::RangeChange {
373 old_value: Range::Minimum(1.0),
374 new_value: Range::ExclusiveMinimum(2.0),
375 }
376 .is_breaking());
377
378 assert!(ChangeKind::RangeChange {
379 old_value: Range::Minimum(2.0),
380 new_value: Range::ExclusiveMinimum(1.0),
381 }
382 .is_breaking());
383
384 assert!(!ChangeKind::RangeChange {
385 old_value: Range::ExclusiveMinimum(1.0),
386 new_value: Range::ExclusiveMinimum(1.0),
387 }
388 .is_breaking());
389
390 assert!(ChangeKind::RangeChange {
391 old_value: Range::ExclusiveMinimum(1.0),
392 new_value: Range::ExclusiveMinimum(2.0),
393 }
394 .is_breaking());
395
396 assert!(!ChangeKind::RangeChange {
397 old_value: Range::ExclusiveMinimum(2.0),
398 new_value: Range::ExclusiveMinimum(1.0),
399 }
400 .is_breaking());
401
402 assert!(!ChangeKind::RangeChange {
403 old_value: Range::Maximum(1.0),
404 new_value: Range::Maximum(1.0),
405 }
406 .is_breaking());
407
408 assert!(!ChangeKind::RangeChange {
409 old_value: Range::Maximum(1.0),
410 new_value: Range::Maximum(2.0),
411 }
412 .is_breaking());
413
414 assert!(ChangeKind::RangeChange {
415 old_value: Range::Maximum(2.0),
416 new_value: Range::Maximum(1.0),
417 }
418 .is_breaking());
419
420 assert!(ChangeKind::RangeChange {
421 old_value: Range::Maximum(1.0),
422 new_value: Range::ExclusiveMaximum(1.0),
423 }
424 .is_breaking());
425
426 assert!(ChangeKind::RangeChange {
427 old_value: Range::Maximum(1.0),
428 new_value: Range::ExclusiveMaximum(2.0),
429 }
430 .is_breaking());
431
432 assert!(ChangeKind::RangeChange {
433 old_value: Range::Maximum(2.0),
434 new_value: Range::ExclusiveMaximum(1.0),
435 }
436 .is_breaking());
437
438 assert!(!ChangeKind::RangeChange {
439 old_value: Range::ExclusiveMaximum(1.0),
440 new_value: Range::ExclusiveMaximum(1.0),
441 }
442 .is_breaking());
443
444 assert!(!ChangeKind::RangeChange {
445 old_value: Range::ExclusiveMaximum(1.0),
446 new_value: Range::ExclusiveMaximum(2.0),
447 }
448 .is_breaking());
449
450 assert!(ChangeKind::RangeChange {
451 old_value: Range::ExclusiveMaximum(2.0),
452 new_value: Range::ExclusiveMaximum(1.0),
453 }
454 .is_breaking());
455 }
456}