schemadoc_diff/checker/
mod.rs

1pub mod added_required_body_property_check;
2pub mod added_required_parameter_check;
3pub mod added_required_request_body_check;
4pub mod removed_media_type_check;
5pub mod removed_operation_check;
6pub mod removed_response_property_check;
7pub mod removed_schema_enum_value_check;
8pub mod updated_schema_type_check;
9
10use crate::path_pointer::PathPointer;
11use crate::schema_diff::HttpSchemaDiff;
12
13use crate::visitor::{DiffVisitor, MergedVisitor};
14
15use crate::checker::added_required_body_property_check::AddedRequiredBodyPropertyCheck;
16use crate::checker::added_required_parameter_check::AddedRequiredParameterCheck;
17use crate::checker::added_required_request_body_check::AddedRequiredRequestBodyCheck;
18use crate::checker::removed_media_type_check::RemovedMediaTypeCheck;
19use crate::checker::removed_operation_check::RemovedOperationCheck;
20use crate::checker::removed_response_property_check::RemovedResponsePropertyCheck;
21use crate::checker::removed_schema_enum_value_check::RemovedSchemaEnumValueCheck;
22use crate::checker::updated_schema_type_check::UpdatedSchemaTypeCheck;
23
24#[derive(Debug)]
25pub struct ValidationIssue {
26    pub path: PathPointer,
27    pub breaking: bool,
28    pub kind: &'static str,
29}
30
31impl ValidationIssue {
32    pub fn new(path: PathPointer, kind: &'static str, breaking: bool) -> Self {
33        Self {
34            path,
35            kind,
36            breaking,
37        }
38    }
39}
40
41pub trait HasBreakingChange {
42    fn has_breaking_changes(&self) -> bool;
43}
44
45impl HasBreakingChange for &[ValidationIssue] {
46    fn has_breaking_changes(&self) -> bool {
47        self.iter().any(|x| x.breaking)
48    }
49}
50
51impl HasBreakingChange for &[&ValidationIssue] {
52    fn has_breaking_changes(&self) -> bool {
53        self.iter().any(|x| x.breaking)
54    }
55}
56
57trait ValidationIssuer<'s> {
58    fn id(&self) -> &'static str;
59    fn visitor(&self) -> &dyn DiffVisitor<'s>;
60    fn issues(&self) -> Option<Vec<ValidationIssue>>;
61}
62
63pub fn validate(
64    diff: &HttpSchemaDiff,
65    checkers: &[&str],
66) -> Vec<ValidationIssue> {
67    let removed_operation = Box::<RemovedOperationCheck>::default();
68    let removed_media_type = Box::<RemovedMediaTypeCheck>::default();
69    let changed_schema_type = Box::<UpdatedSchemaTypeCheck>::default();
70    let removed_schema_enum_value =
71        Box::<RemovedSchemaEnumValueCheck>::default();
72    let added_required_parameter =
73        Box::<AddedRequiredParameterCheck>::default();
74    let removed_response_property =
75        Box::<RemovedResponsePropertyCheck>::default();
76    let added_required_request_body =
77        Box::<AddedRequiredRequestBodyCheck>::default();
78    let added_required_body_property =
79        Box::<AddedRequiredBodyPropertyCheck>::default();
80
81    let available_issuers: Vec<&dyn ValidationIssuer> = vec![
82        &*removed_operation,
83        &*removed_media_type,
84        &*changed_schema_type,
85        &*added_required_parameter,
86        &*removed_response_property,
87        &*removed_schema_enum_value,
88        &*added_required_request_body,
89        &*added_required_body_property,
90    ];
91
92    let issuers: Vec<_> = if checkers.contains(&"*") {
93        available_issuers
94    } else {
95        available_issuers
96            .into_iter()
97            .filter(|issuer| checkers.contains(&issuer.id()))
98            .collect()
99    };
100
101    let visitors: Vec<_> = issuers.iter().map(|v| v.visitor()).collect();
102
103    {
104        let visitor = MergedVisitor::new(visitors.as_slice());
105        crate::visitor::dispatch_visitor(diff, &visitor);
106    }
107
108    let results: Vec<_> = issuers
109        .into_iter()
110        .filter_map(|v| v.issues())
111        .flatten()
112        .collect();
113
114    results
115}