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}
111
112impl ChangeKind {
113 pub fn is_breaking(&self) -> bool {
121 match self {
122 Self::TypeAdd { .. } => false,
123 Self::TypeRemove { .. } => true,
124 Self::ConstAdd { .. } => true,
125 Self::ConstRemove { .. } => false,
126 Self::PropertyAdd {
127 lhs_additional_properties,
128 ..
129 } => *lhs_additional_properties,
130 Self::PropertyRemove {
131 lhs_additional_properties,
132 ..
133 } => !*lhs_additional_properties,
134 Self::RangeAdd { .. } => true,
135 Self::RangeRemove { .. } => false,
136 Self::RangeChange {
137 old_value,
138 new_value,
139 } => match (old_value, new_value) {
140 (Range::ExclusiveMinimum(exc), Range::Minimum(min)) if exc >= min => false,
141 (Range::ExclusiveMaximum(exc), Range::Maximum(max)) if exc <= max => false,
142 (Range::Minimum(l), Range::Minimum(r)) if l >= r => false,
143 (Range::ExclusiveMinimum(l), Range::ExclusiveMinimum(r)) if l >= r => false,
144 (Range::Maximum(l), Range::Maximum(r)) if l <= r => false,
145 (Range::ExclusiveMaximum(l), Range::ExclusiveMaximum(r)) if l <= r => false,
146 _ => true,
147 },
148 Self::TupleToArray { .. } => false,
149 Self::ArrayToTuple { .. } => true,
150 Self::TupleChange { .. } => true,
151 Self::RequiredRemove { .. } => false,
152 Self::RequiredAdd { .. } => true,
153 }
154 }
155}
156
157#[derive(Error, Debug)]
159pub enum Error {
160 #[error("failed to parse schema")]
165 Serde(#[from] serde_json::Error),
166}
167
168#[derive(Serialize, Clone, Ord, Eq, PartialEq, PartialOrd, Debug)]
170#[allow(missing_docs)]
171pub enum JsonSchemaType {
172 #[serde(rename = "string")]
173 String,
174 #[serde(rename = "number")]
175 Number,
176 #[serde(rename = "integer")]
177 Integer,
178 #[serde(rename = "object")]
179 Object,
180 #[serde(rename = "array")]
181 Array,
182 #[serde(rename = "boolean")]
183 Boolean,
184 #[serde(rename = "null")]
185 Null,
186}
187
188impl From<JsonSchemaType> for InstanceType {
189 fn from(t: JsonSchemaType) -> Self {
190 match t {
191 JsonSchemaType::String => InstanceType::String,
192 JsonSchemaType::Number => InstanceType::Number,
193 JsonSchemaType::Integer => InstanceType::Integer,
194 JsonSchemaType::Object => InstanceType::Object,
195 JsonSchemaType::Array => InstanceType::Array,
196 JsonSchemaType::Boolean => InstanceType::Boolean,
197 JsonSchemaType::Null => InstanceType::Null,
198 }
199 }
200}
201
202impl From<InstanceType> for JsonSchemaType {
203 fn from(t: InstanceType) -> Self {
204 match t {
205 InstanceType::String => JsonSchemaType::String,
206 InstanceType::Number => JsonSchemaType::Number,
207 InstanceType::Integer => JsonSchemaType::Integer,
208 InstanceType::Object => JsonSchemaType::Object,
209 InstanceType::Array => JsonSchemaType::Array,
210 InstanceType::Boolean => JsonSchemaType::Boolean,
211 InstanceType::Null => JsonSchemaType::Null,
212 }
213 }
214}
215
216#[derive(Serialize, Clone, PartialEq, PartialOrd, Debug)]
218#[serde(rename_all = "camelCase")]
219#[allow(missing_docs)]
220pub enum Range {
221 Minimum(f64),
222 Maximum(f64),
223 ExclusiveMinimum(f64),
224 ExclusiveMaximum(f64),
225}
226
227#[cfg(test)]
228mod tests {
229 use super::*;
230 #[test]
231 fn is_range_change_breaking() {
232 assert!(!ChangeKind::RangeChange {
233 old_value: Range::Minimum(1.0),
234 new_value: Range::Minimum(1.0),
235 }
236 .is_breaking());
237
238 assert!(ChangeKind::RangeChange {
239 old_value: Range::Minimum(1.0),
240 new_value: Range::Minimum(2.0),
241 }
242 .is_breaking());
243
244 assert!(!ChangeKind::RangeChange {
245 old_value: Range::Minimum(2.0),
246 new_value: Range::Minimum(1.0),
247 }
248 .is_breaking());
249
250 assert!(ChangeKind::RangeChange {
251 old_value: Range::Minimum(1.0),
252 new_value: Range::ExclusiveMinimum(1.0),
253 }
254 .is_breaking());
255
256 assert!(ChangeKind::RangeChange {
257 old_value: Range::Minimum(1.0),
258 new_value: Range::ExclusiveMinimum(2.0),
259 }
260 .is_breaking());
261
262 assert!(ChangeKind::RangeChange {
263 old_value: Range::Minimum(2.0),
264 new_value: Range::ExclusiveMinimum(1.0),
265 }
266 .is_breaking());
267
268 assert!(!ChangeKind::RangeChange {
269 old_value: Range::ExclusiveMinimum(1.0),
270 new_value: Range::ExclusiveMinimum(1.0),
271 }
272 .is_breaking());
273
274 assert!(ChangeKind::RangeChange {
275 old_value: Range::ExclusiveMinimum(1.0),
276 new_value: Range::ExclusiveMinimum(2.0),
277 }
278 .is_breaking());
279
280 assert!(!ChangeKind::RangeChange {
281 old_value: Range::ExclusiveMinimum(2.0),
282 new_value: Range::ExclusiveMinimum(1.0),
283 }
284 .is_breaking());
285
286 assert!(!ChangeKind::RangeChange {
287 old_value: Range::Maximum(1.0),
288 new_value: Range::Maximum(1.0),
289 }
290 .is_breaking());
291
292 assert!(!ChangeKind::RangeChange {
293 old_value: Range::Maximum(1.0),
294 new_value: Range::Maximum(2.0),
295 }
296 .is_breaking());
297
298 assert!(ChangeKind::RangeChange {
299 old_value: Range::Maximum(2.0),
300 new_value: Range::Maximum(1.0),
301 }
302 .is_breaking());
303
304 assert!(ChangeKind::RangeChange {
305 old_value: Range::Maximum(1.0),
306 new_value: Range::ExclusiveMaximum(1.0),
307 }
308 .is_breaking());
309
310 assert!(ChangeKind::RangeChange {
311 old_value: Range::Maximum(1.0),
312 new_value: Range::ExclusiveMaximum(2.0),
313 }
314 .is_breaking());
315
316 assert!(ChangeKind::RangeChange {
317 old_value: Range::Maximum(2.0),
318 new_value: Range::ExclusiveMaximum(1.0),
319 }
320 .is_breaking());
321
322 assert!(!ChangeKind::RangeChange {
323 old_value: Range::ExclusiveMaximum(1.0),
324 new_value: Range::ExclusiveMaximum(1.0),
325 }
326 .is_breaking());
327
328 assert!(!ChangeKind::RangeChange {
329 old_value: Range::ExclusiveMaximum(1.0),
330 new_value: Range::ExclusiveMaximum(2.0),
331 }
332 .is_breaking());
333
334 assert!(ChangeKind::RangeChange {
335 old_value: Range::ExclusiveMaximum(2.0),
336 new_value: Range::ExclusiveMaximum(1.0),
337 }
338 .is_breaking());
339 }
340}