Skip to main content

valico/json_dsl/
builder.rs

1use serde_json::{to_value, Value};
2
3use super::super::json_schema;
4use super::coercers;
5use super::errors;
6use super::param;
7use super::validators;
8
9pub type DynBuilder = Box<dyn Fn(&mut json_schema::Builder) + Send + Sync>;
10
11pub struct Builder {
12    requires: Vec<param::Param>,
13    optional: Vec<param::Param>,
14    validators: validators::Validators,
15    schema_builder: Option<DynBuilder>,
16    schema_id: Option<url::Url>,
17}
18
19unsafe impl Send for Builder {}
20
21impl Builder {
22    pub fn new() -> Builder {
23        Builder {
24            requires: vec![],
25            optional: vec![],
26            validators: vec![],
27            schema_builder: None,
28            schema_id: None,
29        }
30    }
31
32    pub fn build<F>(rules: F) -> Builder
33    where
34        F: FnOnce(&mut Builder),
35    {
36        let mut builder = Builder::new();
37        rules(&mut builder);
38
39        builder
40    }
41
42    pub fn get_required(&self) -> &Vec<param::Param> {
43        &self.requires
44    }
45
46    pub fn get_optional(&self) -> &Vec<param::Param> {
47        &self.optional
48    }
49
50    pub fn get_validators(&self) -> &validators::Validators {
51        &self.validators
52    }
53
54    pub fn req_defined(&mut self, name: &str) {
55        let params = param::Param::new(name);
56        self.requires.push(params);
57    }
58
59    pub fn req_typed(&mut self, name: &str, coercer: Box<dyn coercers::Coercer + Send + Sync>) {
60        let params = param::Param::new_with_coercer(name, coercer);
61        self.requires.push(params);
62    }
63
64    pub fn req_nested<F>(
65        &mut self,
66        name: &str,
67        coercer: Box<dyn coercers::Coercer + Send + Sync>,
68        nest_def: F,
69    ) where
70        F: FnOnce(&mut Builder),
71    {
72        let nest_builder = Builder::build(nest_def);
73        let params = param::Param::new_with_nest(name, coercer, nest_builder);
74        self.requires.push(params);
75    }
76
77    pub fn req<F>(&mut self, name: &str, param_builder: F)
78    where
79        F: FnOnce(&mut param::Param),
80    {
81        let params = param::Param::build(name, param_builder);
82        self.requires.push(params);
83    }
84
85    pub fn opt_defined(&mut self, name: &str) {
86        let params = param::Param::new(name);
87        self.optional.push(params);
88    }
89
90    pub fn opt_typed(&mut self, name: &str, coercer: Box<dyn coercers::Coercer + Send + Sync>) {
91        let params = param::Param::new_with_coercer(name, coercer);
92        self.optional.push(params);
93    }
94
95    pub fn opt_nested<F>(
96        &mut self,
97        name: &str,
98        coercer: Box<dyn coercers::Coercer + Send + Sync>,
99        nest_def: F,
100    ) where
101        F: FnOnce(&mut Builder),
102    {
103        let nest_builder = Builder::build(nest_def);
104        let params = param::Param::new_with_nest(name, coercer, nest_builder);
105        self.optional.push(params);
106    }
107
108    pub fn opt<F>(&mut self, name: &str, param_builder: F)
109    where
110        F: FnOnce(&mut param::Param),
111    {
112        let params = param::Param::build(name, param_builder);
113        self.optional.push(params);
114    }
115
116    pub fn validate(&mut self, validator: Box<dyn validators::Validator + 'static + Send + Sync>) {
117        self.validators.push(validator);
118    }
119
120    pub fn validate_with<F>(&mut self, validator: F)
121    where
122        F: Fn(&Value, &str) -> validators::ValidatorResult + 'static + Send + Sync,
123    {
124        self.validators.push(Box::new(validator));
125    }
126
127    pub fn mutually_exclusive(&mut self, params: &[&str]) {
128        let validator = Box::new(validators::MutuallyExclusive::new(params));
129        self.validators.push(validator);
130    }
131
132    pub fn exactly_one_of(&mut self, params: &[&str]) {
133        let validator = Box::new(validators::ExactlyOneOf::new(params));
134        self.validators.push(validator);
135    }
136
137    pub fn at_least_one_of(&mut self, params: &[&str]) {
138        let validator = Box::new(validators::AtLeastOneOf::new(params));
139        self.validators.push(validator);
140    }
141
142    pub fn schema_id(&mut self, id: url::Url) {
143        self.schema_id = Some(id);
144    }
145
146    pub fn schema<F>(&mut self, build: F)
147    where
148        F: Fn(&mut json_schema::Builder) + 'static + Send + Sync,
149    {
150        self.schema_builder = Some(Box::new(build));
151    }
152
153    pub fn build_schemes(
154        &mut self,
155        scope: &mut json_schema::Scope,
156    ) -> Result<(), json_schema::SchemaError> {
157        for param in self.requires.iter_mut().chain(self.optional.iter_mut()) {
158            if param.schema_builder.is_some() {
159                let json_schema =
160                    json_schema::builder::schema_box(param.schema_builder.take().unwrap());
161                let id = scope.compile(to_value(&json_schema).unwrap(), true)?;
162                param.schema_id = Some(id);
163            }
164
165            if param.nest.is_some() {
166                param.nest.as_mut().unwrap().build_schemes(scope)?;
167            }
168        }
169
170        if self.schema_builder.is_some() {
171            let json_schema = json_schema::builder::schema_box(self.schema_builder.take().unwrap());
172            let id = scope.compile(to_value(json_schema).unwrap(), true)?;
173            self.schema_id = Some(id);
174        }
175
176        Ok(())
177    }
178
179    pub fn process(
180        &self,
181        val: &mut Value,
182        scope: Option<&json_schema::Scope>,
183    ) -> json_schema::ValidationState {
184        self.process_nest(val, "", scope)
185    }
186
187    pub fn process_nest(
188        &self,
189        val: &mut Value,
190        path: &str,
191        scope: Option<&json_schema::Scope>,
192    ) -> json_schema::ValidationState {
193        let mut state = if val.is_array() {
194            let mut state = json_schema::ValidationState::new();
195            let array = val.as_array_mut().unwrap();
196            for (idx, item) in array.iter_mut().enumerate() {
197                let item_path = [path, idx.to_string().as_ref()].join("/");
198                if item.is_object() {
199                    let process_state = self.process_object(item, item_path.as_ref(), scope);
200                    state.append(process_state);
201                } else {
202                    state.errors.push(Box::new(errors::WrongType {
203                        path: item_path.to_string(),
204                        detail: "List value is not and object".to_string(),
205                    }))
206                }
207            }
208
209            state
210        } else if val.is_object() {
211            self.process_object(val, path, scope)
212        } else {
213            let mut state = json_schema::ValidationState::new();
214            state.errors.push(Box::new(errors::WrongType {
215                path: path.to_string(),
216                detail: "Value is not an object or an array".to_string(),
217            }));
218
219            state
220        };
221
222        let path = if path.is_empty() { "/" } else { path };
223
224        if let Some(ref id) = self.schema_id {
225            if let Some(scope) = scope {
226                let schema = scope.resolve(id);
227                match schema {
228                    Some(schema) => state.append(schema.validate_in(val, path)),
229                    None => state.missing.push(id.clone()),
230                }
231            }
232        }
233
234        state
235    }
236
237    fn process_object(
238        &self,
239        val: &mut Value,
240        path: &str,
241        scope: Option<&json_schema::Scope>,
242    ) -> json_schema::ValidationState {
243        let mut state = json_schema::ValidationState::new();
244
245        {
246            let object = val.as_object_mut().expect("We expect object here");
247            for param in self.requires.iter() {
248                let name = &param.name;
249                let present = object.contains_key(name);
250                let param_path = [path, name.as_ref()].join("/");
251                if present {
252                    let process_result =
253                        param.process(object.get_mut(name).unwrap(), param_path.as_ref(), scope);
254                    if let Some(new_value) = process_result.value {
255                        object.insert(name.clone(), new_value);
256                    }
257                    state.append(process_result.state);
258                } else {
259                    state.errors.push(Box::new(errors::Required {
260                        path: param_path.clone(),
261                    }))
262                }
263            }
264
265            for param in self.optional.iter() {
266                let name = &param.name;
267                let present = object.contains_key(name);
268                let param_path = [path, name.as_ref()].join("/");
269                if present {
270                    let process_result =
271                        param.process(object.get_mut(name).unwrap(), param_path.as_ref(), scope);
272                    if let Some(new_value) = process_result.value {
273                        object.insert(name.clone(), new_value);
274                    }
275                    state.append(process_result.state);
276                }
277            }
278        }
279
280        let path = if path.is_empty() { "/" } else { path };
281
282        for validator in self.validators.iter() {
283            match validator.validate(val, path) {
284                Ok(()) => (),
285                Err(err) => {
286                    state.errors.extend(err);
287                }
288            };
289        }
290
291        {
292            if state.is_valid() {
293                let object = val.as_object_mut().expect("We expect object here");
294
295                // second pass we need to validate without default values in optionals
296                for param in self.optional.iter() {
297                    let name = &param.name;
298                    let present = object.contains_key(name);
299                    if !present {
300                        if let Some(val) = param.default.as_ref() {
301                            object.insert(name.clone(), val.clone());
302                        }
303                    }
304                }
305            }
306        }
307
308        state
309    }
310}