valico/json_dsl/
builder.rs

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