1use serde::{Deserialize, Serialize};
2use std::fmt::Debug;
3
4use schemadoc_diff_derive::{Diff, DiffOwnChanges, Empty};
5use serde_json::Value;
6
7use crate::core::{
8 Diff, DiffContext, DiffResult, EitherDiff, Empty, MapDiff,
9 MayBeRefCoreDiff, Referencable, VecDiff,
10};
11
12use crate::schema::HttpSchemaRef;
13use crate::schema_diff_utils::{PathsMapPathResolver, TypeVecDiffSorter};
14
15pub type MayBeRefDiff<T> = MayBeRefCoreDiff<T, HttpSchemaRef>;
16
17fn check_custom_fields(fields: &DiffResult<MapDiff<Value>>) -> bool {
18 if fields.is_same() {
19 let fields = fields.get().expect("Must always be set");
20 fields.is_empty()
21 } else {
22 fields.is_none()
23 }
24}
25
26#[derive(
27 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
28)]
29#[serde(rename_all = "camelCase")]
30pub struct HttpSchemaDiff {
31 pub version: String,
32
33 pub schema_version: String,
34 pub schema_source: String,
35 pub schema_source_version: String,
36
37 pub info: DiffResult<InfoDiff>,
38 pub servers: DiffResult<VecDiff<ServerDiff>>,
39 pub paths:
40 DiffResult<MapDiff<MayBeRefDiff<PathDiff>, PathsMapPathResolver>>,
41 pub components: DiffResult<ComponentsDiff>,
42 pub tags: DiffResult<VecDiff<TagDiff>>,
43 pub external_docs: DiffResult<ExternalDocDiff>,
44}
45
46impl HttpSchemaDiff {
47 pub fn get_diff_version(&self) -> String {
48 format!(
49 "{}-{}-{}",
50 self.schema_version,
51 self.schema_source,
52 self.schema_source_version
53 )
54 }
55}
56
57#[derive(
58 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
59)]
60#[serde(rename_all = "camelCase")]
61pub struct InfoDiff {
62 #[serde(skip_serializing_if = "DiffResult::is_none")]
63 pub title: DiffResult<String>,
64 #[serde(skip_serializing_if = "DiffResult::is_none")]
65 pub description: DiffResult<String>,
66 #[serde(skip_serializing_if = "DiffResult::is_none")]
67 pub terms_of_service: DiffResult<String>,
68 #[serde(skip_serializing_if = "DiffResult::is_none")]
69 pub contact: DiffResult<ContactDiff>,
70 #[serde(skip_serializing_if = "DiffResult::is_none")]
71 pub license: DiffResult<LicenseDiff>,
72 #[serde(skip_serializing_if = "DiffResult::is_none")]
73 pub version: DiffResult<String>,
74}
75
76#[derive(
77 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
78)]
79pub struct ContactDiff {
80 #[serde(skip_serializing_if = "DiffResult::is_none")]
81 pub name: DiffResult<String>,
82 #[serde(skip_serializing_if = "DiffResult::is_none")]
83 pub url: DiffResult<String>,
84 #[serde(skip_serializing_if = "DiffResult::is_none")]
85 pub email: DiffResult<String>,
86}
87
88#[derive(
89 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
90)]
91pub struct LicenseDiff {
92 #[serde(skip_serializing_if = "DiffResult::is_none")]
93 pub name: DiffResult<String>,
94 #[serde(skip_serializing_if = "DiffResult::is_none")]
95 pub url: DiffResult<String>,
96}
97
98#[derive(
99 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
100)]
101pub struct ServerDiff {
102 #[serde(skip_serializing_if = "DiffResult::is_none")]
103 pub url: DiffResult<String>,
104 #[serde(skip_serializing_if = "DiffResult::is_none")]
105 pub description: DiffResult<String>,
106 #[serde(skip_serializing_if = "DiffResult::is_none")]
107 pub variables: DiffResult<MapDiff<ServerVariableDiff>>,
108}
109
110#[derive(
111 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
112)]
113pub struct ServerVariableDiff {
114 #[serde(skip_serializing_if = "DiffResult::is_none")]
115 pub r#enum: DiffResult<VecDiff<String>>,
116 #[serde(skip_serializing_if = "DiffResult::is_none")]
117 pub default: DiffResult<Value>,
118 #[serde(skip_serializing_if = "DiffResult::is_none")]
119 pub description: DiffResult<String>,
120}
121
122#[derive(
123 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
124)]
125#[serde(rename_all = "camelCase")]
126pub struct ComponentsDiff {
127 #[serde(skip_serializing_if = "DiffResult::is_none")]
128 pub schemas: DiffResult<MapDiff<MayBeRefDiff<SchemaDiff>>>,
129 #[serde(skip_serializing_if = "DiffResult::is_none")]
130 pub responses: DiffResult<MapDiff<MayBeRefDiff<ResponseDiff>>>,
131 #[serde(skip_serializing_if = "DiffResult::is_none")]
132 pub parameters: DiffResult<MapDiff<MayBeRefDiff<ParameterDiff>>>,
133 #[serde(skip_serializing_if = "DiffResult::is_none")]
134 pub examples: DiffResult<MapDiff<MayBeRefDiff<ExampleDiff>>>,
135 #[serde(skip_serializing_if = "DiffResult::is_none")]
136 pub request_bodies: DiffResult<MapDiff<MayBeRefDiff<RequestBodyDiff>>>,
137 #[serde(skip_serializing_if = "DiffResult::is_none")]
138 pub headers: DiffResult<MapDiff<MayBeRefDiff<HeaderDiff>>>,
139 #[serde(skip_serializing_if = "DiffResult::is_none")]
140 pub security_schemes:
141 DiffResult<MapDiff<MayBeRefDiff<SecuritySchemeDiff>>>,
142 #[serde(skip_serializing_if = "DiffResult::is_none")]
143 pub links: DiffResult<MapDiff<MayBeRefDiff<LinkDiff>>>,
144}
145
146#[derive(
147 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
148)]
149#[serde(rename_all = "camelCase")]
150pub struct ExternalDocDiff {
151 #[serde(skip_serializing_if = "DiffResult::is_none")]
152 pub url: DiffResult<String>,
153 #[serde(skip_serializing_if = "DiffResult::is_none")]
154 pub description: DiffResult<String>,
155}
156
157#[derive(
158 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
159)]
160#[serde(rename_all = "camelCase")]
161pub struct ParameterDiff {
162 pub name: String,
163 pub r#in: String,
164 #[serde(skip_serializing_if = "DiffResult::is_none")]
165 pub description: DiffResult<String>,
166 #[serde(skip_serializing_if = "DiffResult::is_none")]
167 pub required: DiffResult<bool>,
168 #[serde(skip_serializing_if = "DiffResult::is_none")]
169 pub deprecated: DiffResult<bool>,
170 #[serde(skip_serializing_if = "DiffResult::is_none")]
171 pub allow_empty_value: DiffResult<bool>,
172 #[serde(skip_serializing_if = "DiffResult::is_none")]
173 pub style: DiffResult<String>,
174 #[serde(skip_serializing_if = "DiffResult::is_none")]
175 pub explode: DiffResult<bool>,
176 #[serde(skip_serializing_if = "DiffResult::is_none")]
177 pub allow_reserved: DiffResult<bool>,
178
179 #[serde(skip_serializing_if = "DiffResult::is_none")]
180 pub schema: DiffResult<MayBeRefDiff<SchemaDiff>>,
181
182 #[serde(skip_serializing_if = "DiffResult::is_none")]
183 pub examples: DiffResult<MapDiff<MayBeRefDiff<Value>>>,
184 #[serde(skip_serializing_if = "DiffResult::is_none")]
185 pub content: DiffResult<MapDiff<MediaTypeDiff>>,
186
187 #[serde(skip_serializing_if = "check_custom_fields")]
188 pub custom_fields: DiffResult<MapDiff<Value>>,
189}
190
191impl Referencable for ParameterDiff {}
192
193#[derive(
194 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
195)]
196#[serde(rename_all = "camelCase")]
197pub struct RequestBodyDiff {
198 #[serde(skip_serializing_if = "DiffResult::is_none")]
199 pub description: DiffResult<String>,
200 #[serde(skip_serializing_if = "DiffResult::is_none")]
201 pub content: DiffResult<MapDiff<MediaTypeDiff>>,
202 #[serde(skip_serializing_if = "DiffResult::is_none")]
203 pub required: DiffResult<bool>,
204}
205
206impl Referencable for RequestBodyDiff {}
207
208#[derive(
209 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
210)]
211#[serde(rename_all = "camelCase")]
212pub struct MediaTypeDiff {
213 #[serde(skip_serializing_if = "DiffResult::is_none")]
214 pub schema: DiffResult<MayBeRefDiff<SchemaDiff>>,
215
216 #[serde(skip_serializing_if = "DiffResult::is_none")]
217 pub examples: DiffResult<MapDiff<MayBeRefDiff<ExampleDiff>>>,
218 #[serde(skip_serializing_if = "DiffResult::is_none")]
219 pub encoding: DiffResult<MapDiff<EncodingDiff>>,
220}
221
222#[derive(
223 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
224)]
225#[serde(rename_all = "camelCase")]
226pub struct EncodingDiff {
227 #[serde(skip_serializing_if = "DiffResult::is_none")]
228 pub content_type: DiffResult<String>,
229 #[serde(skip_serializing_if = "DiffResult::is_none")]
230 pub headers: DiffResult<MapDiff<MayBeRefDiff<HeaderDiff>>>,
231 #[serde(skip_serializing_if = "DiffResult::is_none")]
232 pub style: DiffResult<String>,
233 #[serde(skip_serializing_if = "DiffResult::is_none")]
234 pub explode: DiffResult<bool>,
235 #[serde(skip_serializing_if = "DiffResult::is_none")]
236 pub allow_reserved: DiffResult<bool>,
237}
238
239impl Referencable for EncodingDiff {}
240
241#[derive(
242 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
243)]
244#[serde(rename_all = "camelCase")]
245pub struct LinkDiff {
246 #[serde(skip_serializing_if = "DiffResult::is_none")]
247 pub operation_ref: DiffResult<String>,
248 #[serde(skip_serializing_if = "DiffResult::is_none")]
249 pub operation_id: DiffResult<String>,
250 #[serde(skip_serializing_if = "DiffResult::is_none")]
251 pub parameters: DiffResult<MapDiff<Value>>,
252 #[serde(skip_serializing_if = "DiffResult::is_none")]
253 pub request_body: DiffResult<Value>,
254 #[serde(skip_serializing_if = "DiffResult::is_none")]
255 pub description: DiffResult<String>,
256 #[serde(skip_serializing_if = "DiffResult::is_none")]
257 pub server: DiffResult<ServerDiff>,
258}
259
260impl Referencable for LinkDiff {}
261
262#[derive(
263 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
264)]
265pub struct ResponseDiff {
266 #[serde(skip_serializing_if = "DiffResult::is_none")]
267 pub description: DiffResult<String>,
268
269 #[serde(skip_serializing_if = "DiffResult::is_none")]
270 pub content: DiffResult<MapDiff<MediaTypeDiff>>,
271 #[serde(skip_serializing_if = "DiffResult::is_none")]
272 pub links: DiffResult<MapDiff<MayBeRefDiff<LinkDiff>>>,
273 #[serde(skip_serializing_if = "DiffResult::is_none")]
274 pub headers: DiffResult<MapDiff<MayBeRefDiff<HeaderDiff>>>,
275}
276
277impl Referencable for ResponseDiff {}
278
279#[derive(
280 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
281)]
282#[serde(rename_all = "camelCase")]
283pub struct ExampleDiff {
284 #[serde(skip_serializing_if = "DiffResult::is_none")]
285 pub summary: DiffResult<String>,
286 #[serde(skip_serializing_if = "DiffResult::is_none")]
287 pub description: DiffResult<String>,
288 #[serde(skip_serializing_if = "DiffResult::is_none")]
289 pub value: DiffResult<Value>,
290 #[serde(skip_serializing_if = "DiffResult::is_none")]
291 pub external_value: DiffResult<String>,
292}
293
294impl Referencable for ExampleDiff {}
295
296#[derive(
297 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
298)]
299#[serde(rename_all = "camelCase")]
300pub struct DiscriminatorDiff {
301 #[serde(skip_serializing_if = "DiffResult::is_none")]
302 pub property_name: DiffResult<String>,
303 #[serde(skip_serializing_if = "DiffResult::is_none")]
304 pub mapping: DiffResult<MapDiff<String>>,
305}
306
307#[derive(
308 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
309)]
310#[serde(rename_all = "camelCase")]
311pub struct XmlDiff {
312 #[serde(skip_serializing_if = "DiffResult::is_none")]
313 pub name: DiffResult<String>,
314 #[serde(skip_serializing_if = "DiffResult::is_none")]
315 pub namespace: DiffResult<String>,
316 #[serde(skip_serializing_if = "DiffResult::is_none")]
317 pub prefix: DiffResult<String>,
318 #[serde(skip_serializing_if = "DiffResult::is_none")]
319 pub attribute: DiffResult<bool>,
320 #[serde(skip_serializing_if = "DiffResult::is_none")]
321 pub wrapped: DiffResult<bool>,
322}
323
324#[derive(
325 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
326)]
327#[serde(rename_all = "camelCase")]
328pub struct SecuritySchemeDiff {
329 #[serde(skip_serializing_if = "DiffResult::is_none")]
330 pub r#type: DiffResult<String>,
331 #[serde(skip_serializing_if = "DiffResult::is_none")]
332 pub description: DiffResult<String>,
333 #[serde(skip_serializing_if = "DiffResult::is_none")]
334 pub name: DiffResult<String>,
335 #[serde(skip_serializing_if = "DiffResult::is_none")]
336 pub r#in: DiffResult<String>,
337 #[serde(skip_serializing_if = "DiffResult::is_none")]
338 pub scheme: DiffResult<String>,
339 #[serde(skip_serializing_if = "DiffResult::is_none")]
340 pub bearer_format: DiffResult<String>,
341 #[serde(skip_serializing_if = "DiffResult::is_none")]
342 pub flows: DiffResult<OAuthFlowsDiff>,
343 #[serde(skip_serializing_if = "DiffResult::is_none")]
344 pub open_id_connect_url: DiffResult<String>,
345}
346
347impl Referencable for SecuritySchemeDiff {}
348
349#[derive(
350 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
351)]
352#[serde(rename_all = "camelCase")]
353pub struct OAuthFlowsDiff {
354 #[serde(skip_serializing_if = "DiffResult::is_none")]
355 pub implicit: DiffResult<OAuthFlowDiff>,
356 #[serde(skip_serializing_if = "DiffResult::is_none")]
357 pub password: DiffResult<OAuthFlowDiff>,
358 #[serde(skip_serializing_if = "DiffResult::is_none")]
359 pub client_credentials: DiffResult<OAuthFlowDiff>,
360 #[serde(skip_serializing_if = "DiffResult::is_none")]
361 pub authorization_code: DiffResult<OAuthFlowDiff>,
362}
363
364#[derive(
365 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
366)]
367#[serde(rename_all = "camelCase")]
368pub struct OAuthFlowDiff {
369 #[serde(skip_serializing_if = "DiffResult::is_none")]
370 pub authorization_url: DiffResult<String>,
371 #[serde(skip_serializing_if = "DiffResult::is_none")]
372 pub token_url: DiffResult<String>,
373 #[serde(skip_serializing_if = "DiffResult::is_none")]
374 pub refresh_url: DiffResult<String>,
375 #[serde(skip_serializing_if = "DiffResult::is_none")]
376 pub scopes: DiffResult<MapDiff<String>>,
377}
378
379#[derive(
380 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
381)]
382#[serde(rename_all = "camelCase")]
383pub struct TagDiff {
384 #[serde(skip_serializing_if = "DiffResult::is_none")]
385 pub name: DiffResult<String>,
386 #[serde(skip_serializing_if = "DiffResult::is_none")]
387 pub description: DiffResult<String>,
388 #[serde(skip_serializing_if = "DiffResult::is_none")]
389 pub external_doc: DiffResult<ExternalDocDiff>,
390}
391
392#[derive(
393 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
394)]
395#[serde(rename_all = "camelCase")]
396pub struct SchemaDiff {
397 #[serde(skip_serializing_if = "DiffResult::is_none")]
398 pub title: DiffResult<String>,
399 #[serde(skip_serializing_if = "DiffResult::is_none")]
400 pub multiple_of: DiffResult<f32>,
401 #[serde(skip_serializing_if = "DiffResult::is_none")]
402 pub maximum: DiffResult<f32>,
403 #[serde(skip_serializing_if = "DiffResult::is_none")]
404 pub exclusive_maximum: DiffResult<bool>,
405 #[serde(skip_serializing_if = "DiffResult::is_none")]
406 pub minimum: DiffResult<f32>,
407 #[serde(skip_serializing_if = "DiffResult::is_none")]
408 pub exclusive_minimum: DiffResult<bool>,
409 #[serde(skip_serializing_if = "DiffResult::is_none")]
410 pub max_length: DiffResult<usize>,
411 #[serde(skip_serializing_if = "DiffResult::is_none")]
412 pub min_length: DiffResult<usize>,
413 #[serde(skip_serializing_if = "DiffResult::is_none")]
414 pub pattern: DiffResult<String>,
415 #[serde(skip_serializing_if = "DiffResult::is_none")]
416 pub max_items: DiffResult<usize>,
417 #[serde(skip_serializing_if = "DiffResult::is_none")]
418 pub min_items: DiffResult<usize>,
419 #[serde(skip_serializing_if = "DiffResult::is_none")]
420 pub unique_items: DiffResult<bool>,
421
422 #[serde(skip_serializing_if = "DiffResult::is_none")]
423 pub max_properties: DiffResult<usize>,
424 #[serde(skip_serializing_if = "DiffResult::is_none")]
425 pub min_properties: DiffResult<usize>,
426
427 #[serde(skip_serializing_if = "DiffResult::is_none")]
428 pub required: DiffResult<VecDiff<String>>,
429 #[serde(skip_serializing_if = "DiffResult::is_none")]
430 pub r#enum: DiffResult<VecDiff<Value>>,
431
432 #[serde(skip_serializing_if = "DiffResult::is_none")]
433 pub r#type:
434 DiffResult<EitherDiff<String, VecDiff<String, TypeVecDiffSorter>>>,
435 #[serde(skip_serializing_if = "DiffResult::is_none")]
436 pub all_of: DiffResult<VecDiff<MayBeRefDiff<SchemaDiff>>>,
437 #[serde(skip_serializing_if = "DiffResult::is_none")]
438 pub one_of: DiffResult<VecDiff<MayBeRefDiff<SchemaDiff>>>,
439 #[serde(skip_serializing_if = "DiffResult::is_none")]
440 pub any_of: DiffResult<VecDiff<MayBeRefDiff<SchemaDiff>>>,
441 #[serde(skip_serializing_if = "DiffResult::is_none")]
442 pub not: DiffResult<VecDiff<MayBeRefDiff<SchemaDiff>>>,
443 #[serde(skip_serializing_if = "DiffResult::is_none")]
444 pub items: Box<DiffResult<MayBeRefDiff<SchemaDiff>>>,
445 #[serde(skip_serializing_if = "DiffResult::is_none")]
446 pub properties: DiffResult<MapDiff<MayBeRefDiff<SchemaDiff>>>,
447 #[serde(skip_serializing_if = "DiffResult::is_none")]
448 pub additional_properties:
449 DiffResult<EitherDiff<bool, MayBeRefDiff<SchemaDiff>>>,
450 #[serde(skip_serializing_if = "DiffResult::is_none")]
451 pub description: DiffResult<String>,
452 #[serde(skip_serializing_if = "DiffResult::is_none")]
453 pub format: DiffResult<String>,
454 #[serde(skip_serializing_if = "DiffResult::is_none")]
455 pub default: DiffResult<Value>,
456
457 #[serde(skip_serializing_if = "DiffResult::is_none")]
458 pub discriminator: DiffResult<DiscriminatorDiff>,
459 #[serde(skip_serializing_if = "DiffResult::is_none")]
460 pub read_only: DiffResult<bool>,
461 #[serde(skip_serializing_if = "DiffResult::is_none")]
462 pub write_only: DiffResult<bool>,
463 #[serde(skip_serializing_if = "DiffResult::is_none")]
464 pub xml: DiffResult<XmlDiff>,
465 #[serde(skip_serializing_if = "DiffResult::is_none")]
466 pub external_docs: DiffResult<ExternalDocDiff>,
467 #[serde(skip_serializing_if = "DiffResult::is_none")]
468 pub example: DiffResult<Value>,
469 #[serde(skip_serializing_if = "DiffResult::is_none")]
470 pub deprecated: DiffResult<bool>,
471
472 #[serde(skip_serializing_if = "check_custom_fields")]
473 pub custom_fields: DiffResult<MapDiff<Value>>,
474}
475
476impl Referencable for SchemaDiff {}
477
478#[derive(
479 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
480)]
481#[serde(rename_all = "camelCase")]
482pub struct HeaderDiff {
483 #[serde(skip_serializing_if = "DiffResult::is_none")]
484 pub description: DiffResult<String>,
485 #[serde(skip_serializing_if = "DiffResult::is_none")]
486 pub required: DiffResult<bool>,
487 #[serde(skip_serializing_if = "DiffResult::is_none")]
488 pub deprecated: DiffResult<bool>,
489 #[serde(skip_serializing_if = "DiffResult::is_none")]
490 pub allow_empty_value: DiffResult<bool>,
491 #[serde(skip_serializing_if = "DiffResult::is_none")]
492 pub style: DiffResult<String>,
493 #[serde(skip_serializing_if = "DiffResult::is_none")]
494 pub explode: DiffResult<bool>,
495 #[serde(skip_serializing_if = "DiffResult::is_none")]
496 pub allow_reserved: DiffResult<bool>,
497
498 #[serde(skip_serializing_if = "DiffResult::is_none")]
499 pub schema: DiffResult<MayBeRefDiff<SchemaDiff>>,
500
501 #[serde(skip_serializing_if = "DiffResult::is_none")]
502 pub examples: DiffResult<MapDiff<MayBeRefDiff<Value>>>,
503 #[serde(skip_serializing_if = "DiffResult::is_none")]
504 pub content: DiffResult<MapDiff<MediaTypeDiff>>,
505
506 #[serde(skip_serializing_if = "check_custom_fields")]
507 pub custom_fields: DiffResult<MapDiff<Value>>,
508}
509
510impl Referencable for HeaderDiff {}
511
512#[derive(
513 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
514)]
515#[serde(rename_all = "camelCase")]
516pub struct OperationDiff {
517 #[serde(skip_serializing_if = "DiffResult::is_none")]
518 pub tags: DiffResult<VecDiff<String>>,
519 #[serde(skip_serializing_if = "DiffResult::is_none")]
520 pub summary: DiffResult<String>,
521 #[serde(skip_serializing_if = "DiffResult::is_none")]
522 pub description: DiffResult<String>,
523
524 #[serde(skip_serializing_if = "DiffResult::is_none")]
525 pub external_docs: DiffResult<ExternalDocDiff>,
526
527 pub operation_id: DiffResult<String>,
528
529 #[serde(skip_serializing_if = "DiffResult::is_none")]
530 pub parameters: DiffResult<VecDiff<MayBeRefDiff<ParameterDiff>>>,
531 #[serde(skip_serializing_if = "DiffResult::is_none")]
532 pub responses: DiffResult<MapDiff<MayBeRefDiff<ResponseDiff>>>,
533
534 #[serde(skip_serializing_if = "DiffResult::is_none")]
535 pub request_body: DiffResult<MayBeRefDiff<RequestBodyDiff>>,
536 #[serde(skip_serializing_if = "DiffResult::is_none")]
537 pub servers: DiffResult<VecDiff<ServerDiff>>,
538
539 #[serde(skip_serializing_if = "DiffResult::is_none")]
540 pub security: DiffResult<VecDiff<MapDiff<VecDiff<String>>>>,
541
542 #[serde(skip_serializing_if = "DiffResult::is_none")]
543 pub deprecated: DiffResult<bool>,
544}
545
546#[derive(
547 Debug, Clone, Serialize, Deserialize, Empty, Diff, DiffOwnChanges,
548)]
549pub struct PathDiff {
550 #[serde(skip_serializing_if = "DiffResult::is_none")]
551 pub get: DiffResult<OperationDiff>,
552 #[serde(skip_serializing_if = "DiffResult::is_none")]
553 pub put: DiffResult<OperationDiff>,
554 #[serde(skip_serializing_if = "DiffResult::is_none")]
555 pub post: DiffResult<OperationDiff>,
556 #[serde(skip_serializing_if = "DiffResult::is_none")]
557 pub delete: DiffResult<OperationDiff>,
558 #[serde(skip_serializing_if = "DiffResult::is_none")]
559 pub options: DiffResult<OperationDiff>,
560 #[serde(skip_serializing_if = "DiffResult::is_none")]
561 pub head: DiffResult<OperationDiff>,
562 #[serde(skip_serializing_if = "DiffResult::is_none")]
563 pub patch: DiffResult<OperationDiff>,
564 #[serde(skip_serializing_if = "DiffResult::is_none")]
565 pub trace: DiffResult<OperationDiff>,
566
567 #[serde(skip_serializing_if = "DiffResult::is_none")]
568 pub servers: DiffResult<VecDiff<ServerDiff>>,
569
570 #[serde(skip_serializing_if = "DiffResult::is_none")]
571 pub summary: DiffResult<String>,
572 #[serde(skip_serializing_if = "DiffResult::is_none")]
573 pub description: DiffResult<String>,
574}
575
576impl Referencable for PathDiff {}
577
578pub(crate) fn deref_schema_diff<'a>(
579 diff: &'a HttpSchemaDiff,
580 may_be_ref: &'a MayBeRefDiff<SchemaDiff>,
581) -> Option<&'a DiffResult<SchemaDiff>> {
582 match may_be_ref {
583 MayBeRefDiff::Value(value) => return Some(value),
584 MayBeRefDiff::Ref(value) => {
585 if value.reference.starts_with("#/components/schemas/") {
586 let key = value.reference.replace("#/components/schemas/", "");
587 if diff.components.exists() {
588 if let Some(components) = diff.components.get() {
589 if let Some(schemas) = components.schemas.get() {
590 if let Some(may_be_schema) = schemas.get(&key) {
591 if let Some(MayBeRefDiff::Value(schema)) =
592 may_be_schema.get()
593 {
594 return Some(schema);
595 }
596 }
597 }
598 }
599 }
600 }
601 }
602 };
603 None
604}
605
606pub(crate) fn deref_parameter_diff<'a>(
607 diff: &'a HttpSchemaDiff,
608 may_be_ref: &'a MayBeRefDiff<ParameterDiff>,
609) -> Option<&'a DiffResult<ParameterDiff>> {
610 match may_be_ref {
611 MayBeRefDiff::Value(value) => return Some(value),
612 MayBeRefDiff::Ref(value) => {
613 if value.reference.starts_with("#/components/parameters/") {
614 let key =
615 value.reference.replace("#/components/parameters/", "");
616 if diff.components.exists() {
617 if let Some(components) = diff.components.get() {
618 if let Some(parameters) = components.parameters.get() {
619 if let Some(may_be_parameter) =
620 parameters.get(&key)
621 {
622 if let Some(MayBeRefDiff::Value(parameter)) =
623 may_be_parameter.get()
624 {
625 return Some(parameter);
626 }
627 }
628 }
629 }
630 }
631 }
632 }
633 };
634 None
635}
636
637pub(crate) fn deref_request_body_diff<'a>(
638 diff: &'a HttpSchemaDiff,
639 may_be_ref: &'a MayBeRefDiff<RequestBodyDiff>,
640) -> Option<&'a DiffResult<RequestBodyDiff>> {
641 match may_be_ref {
642 MayBeRefDiff::Value(value) => return Some(value),
643 MayBeRefDiff::Ref(value) => {
644 if value.reference.starts_with("#/components/requestBodies/") {
645 let key =
646 value.reference.replace("#/components/requestBodies/", "");
647 if diff.components.exists() {
648 if let Some(components) = diff.components.get() {
649 if let Some(request_bodies) =
650 components.request_bodies.get()
651 {
652 if let Some(request_body) =
653 request_bodies.get(&key)
654 {
655 if let Some(MayBeRefDiff::Value(
656 request_body,
657 )) = request_body.get()
658 {
659 return Some(request_body);
660 }
661 }
662 }
663 }
664 }
665 }
666 }
667 };
668 None
669}
670
671pub(crate) fn deref_response_diff<'a>(
672 diff: &'a HttpSchemaDiff,
673 may_be_ref: &'a MayBeRefDiff<ResponseDiff>,
674) -> Option<&'a DiffResult<ResponseDiff>> {
675 match may_be_ref {
676 MayBeRefDiff::Value(value) => return Some(value),
677 MayBeRefDiff::Ref(value) => {
678 if value.reference.starts_with("#/components/responses/") {
679 let key =
680 value.reference.replace("#/components/responses/", "");
681 if diff.components.exists() {
682 if let Some(components) = diff.components.get() {
683 if let Some(responses) = components.responses.get() {
684 if let Some(response) = responses.get(&key) {
685 if let Some(MayBeRefDiff::Value(response)) =
686 response.get()
687 {
688 return Some(response);
689 }
690 }
691 }
692 }
693 }
694 }
695 }
696 };
697 None
698}
699
700#[cfg(test)]
701mod tests {
702 use crate::core::Either;
703 use crate::schema::*;
704
705 #[test]
706 fn check_operation() {
707 let op_def = r#"{
708 "post": {
709 "tags": ["Nodes"],
710 "summary": "Export Xlsx Template",
711 "description": "Generate XLSX-template for aggregated node data editing",
712 "operationId": "export_xlsx_template_api_v2_nodes__path__template_generate__post",
713 "parameters": [
714 {
715 "required": true,
716 "schema": { "title": "Path", "type": "string" },
717 "name": "path",
718 "in": "path"
719 },
720 {
721 "required": false,
722 "schema": { "title": "Update Sender", "type": "string" },
723 "name": "update_sender",
724 "in": "query"
725 },
726 {
727 "required": false,
728 "schema": { "title": "Force", "type": "boolean", "default": false },
729 "name": "force",
730 "in": "query"
731 },
732 {
733 "required": false,
734 "schema": { "title": "Compound Amount", "type": "integer" },
735 "name": "compound_amount",
736 "in": "query"
737 },
738 {
739 "required": false,
740 "schema": {
741 "allOf": [{ "$ref": "/components/schemas/ExportFmt" }],
742 "default": "xlsx"
743 },
744 "name": "export_format",
745 "in": "query"
746 }
747 ],
748 "requestBody": {
749 "content": {
750 "application/json": {
751 "schema": {
752 "$ref": "/components/schemas/Body_export_xlsx_template_api_v2_nodes__path__template_generate__post"
753 }
754 }
755 }
756 },
757 "responses": {
758 "200": {
759 "description": "Successful Response",
760 "content": {
761 "application/json": { "schema": {} },
762 "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet": {}
763 }
764 },
765 "422": {
766 "description": "Validation Error",
767 "content": {
768 "application/json": {
769 "schema": { "$ref": "/components/schemas/HTTPValidationError" }
770 }
771 }
772 }
773 },
774 "security": [{ "OAuth2PasswordBearer": [] }]
775 }
776 }"#;
777
778 let _: Path = serde_json::from_str(op_def).unwrap();
779 }
780
781 #[test]
782 fn check_schema_additional_properties() {
783 let op_def = r#"{
784 "title": "AdditionalProperties",
785 "type": "object",
786 "additionalProperties": {
787 "$ref": "/components/schemas/AdditionalProperties"
788 }
789 }"#;
790
791 let op: Schema = serde_json::from_str(op_def).unwrap();
792 assert!(matches!(op.additional_properties, Some(Either::Right(_))));
793
794 let op_def = r#"{
795 "title": "AdditionalProperties",
796 "type": "object",
797 "additionalProperties": false
798 }"#;
799
800 let op: Schema = serde_json::from_str(op_def).unwrap();
801 assert!(matches!(op.additional_properties, Some(Either::Left(_))));
802
803 let sc_def = r#"
804 {
805 "type": "object",
806 "discriminator": { "propertyName": "type" },
807 "properties": {
808 "type": {
809 "type": "string",
810 "description": "The type of context being attached to the entity.",
811 "enum": ["link", "text", "image"]
812 }
813 },
814 "required": ["type"]
815 }
816 "#;
817 let op: Schema = serde_json::from_str(sc_def).unwrap();
818 assert!(matches!(op.discriminator, Some(_)))
819 }
820}