Skip to main content

valico/json_schema/validators/
mod.rs

1use serde::{Serialize, Serializer};
2use serde_json::{to_value, Value};
3use std::borrow::Cow;
4use std::collections::HashSet;
5use std::fmt;
6
7use super::scope;
8
9#[macro_export]
10macro_rules! strict_process {
11    ($val:expr, $path:ident, $err:expr) => {{
12        let maybe_val = $val;
13        if maybe_val.is_none() {
14            return val_error!($crate::json_schema::errors::WrongType {
15                path: $path.to_string(),
16                detail: $err.to_string()
17            });
18        }
19
20        maybe_val.unwrap()
21    }};
22}
23
24macro_rules! nonstrict_process {
25    ($val:expr, $path:ident) => {{
26        let maybe_val = $val;
27        if maybe_val.is_none() {
28            return $crate::json_schema::validators::ValidationState::new();
29        }
30
31        maybe_val.unwrap()
32    }};
33}
34
35macro_rules! val_error {
36    ($err:expr) => {
37        $crate::json_schema::validators::ValidationState {
38            errors: vec![Box::new($err)],
39            missing: vec![],
40            replacement: None,
41            evaluated: Default::default(),
42        }
43    };
44}
45
46pub use self::conditional::Conditional;
47pub use self::const_::Const;
48pub use self::contains::Contains;
49pub use self::content_media::ContentMedia;
50pub use self::dependencies::Dependencies;
51pub use self::enum_::Enum;
52pub use self::items::Items;
53pub use self::maxmin::{ExclusiveMaximum, ExclusiveMinimum, Maximum, Minimum};
54pub use self::maxmin_items::{MaxItems, MinItems};
55pub use self::maxmin_length::{MaxLength, MinLength};
56pub use self::maxmin_properties::{MaxProperties, MinProperties};
57pub use self::multiple_of::MultipleOf;
58pub use self::not::Not;
59pub use self::of::{AllOf, AnyOf, OneOf};
60pub use self::pattern::Pattern;
61pub use self::properties::Properties;
62pub use self::property_names::PropertyNames;
63pub use self::ref_::Ref;
64pub use self::required::Required;
65pub use self::type_::Type;
66pub use self::unevaluated::Unevaluated;
67pub use self::unique_items::UniqueItems;
68
69mod conditional;
70mod const_;
71mod contains;
72pub mod content_media;
73pub mod dependencies;
74mod enum_;
75pub mod formats;
76pub mod items;
77mod maxmin;
78mod maxmin_items;
79mod maxmin_length;
80mod maxmin_properties;
81mod multiple_of;
82mod not;
83mod of;
84mod pattern;
85pub mod properties;
86mod property_names;
87mod ref_;
88mod required;
89pub mod type_;
90pub mod unevaluated;
91mod unique_items;
92
93#[derive(Debug, Default)]
94pub struct ValidationState {
95    pub errors: super::super::common::error::ValicoErrors,
96    pub missing: Vec<url::Url>,
97    pub replacement: Option<Value>,
98    /// Set of paths that have been evaluated so far. Once a path has been evaluated, it should be added
99    /// here so that `unevaluatedItems` and `unevaluatedProperties` work.
100    pub evaluated: HashSet<String>,
101}
102
103impl ValidationState {
104    pub fn new() -> ValidationState {
105        ValidationState {
106            errors: vec![],
107            missing: vec![],
108            replacement: None,
109            evaluated: Default::default(),
110        }
111    }
112
113    pub fn is_valid(&self) -> bool {
114        self.errors.is_empty()
115    }
116
117    pub fn is_strictly_valid(&self) -> bool {
118        self.errors.is_empty() && self.missing.is_empty()
119    }
120
121    pub fn append(&mut self, second: ValidationState) {
122        self.errors.extend(second.errors);
123        self.missing.extend(second.missing);
124        self.evaluated.extend(second.evaluated);
125    }
126
127    pub fn set_replacement<T: Clone + Into<Value>>(&mut self, data: Cow<T>) {
128        if !self.is_valid() {
129            return;
130        }
131        if let Cow::Owned(data) = data {
132            self.replacement = Some(data.into());
133        }
134    }
135}
136
137impl Serialize for ValidationState {
138    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
139    where
140        S: Serializer,
141    {
142        let mut map = ::serde_json::Map::new();
143        map.insert(
144            "errors".to_string(),
145            Value::Array(
146                self.errors
147                    .iter()
148                    .map(|err| to_value(err).unwrap())
149                    .collect::<Vec<Value>>(),
150            ),
151        );
152        map.insert(
153            "missing".to_string(),
154            Value::Array(
155                self.missing
156                    .iter()
157                    .map(|url| to_value(url.to_string()).unwrap())
158                    .collect::<Vec<Value>>(),
159            ),
160        );
161        Value::Object(map).serialize(serializer)
162    }
163}
164
165pub trait Validator {
166    fn validate(
167        &self,
168        item: &Value,
169        _: &str,
170        _: &scope::Scope,
171        prev_state: &ValidationState,
172    ) -> ValidationState;
173}
174
175impl fmt::Debug for dyn Validator + 'static + Send + Sync {
176    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
177        fmt.write_str("<validator>")
178    }
179}
180
181pub type BoxedValidator = Box<dyn Validator + 'static + Send + Sync>;
182pub type Validators = Vec<BoxedValidator>;
183
184impl<T> Validator for T
185where
186    T: Fn(&Value, &str, &scope::Scope, &super::ValidationState) -> ValidationState,
187{
188    fn validate(
189        &self,
190        val: &Value,
191        path: &str,
192        scope: &scope::Scope,
193        state: &super::ValidationState,
194    ) -> ValidationState {
195        self(val, path, scope, state)
196    }
197}