valico/json_dsl/
coercers.rs

1use serde_json::{Value, to_string, to_value};
2
3use super::errors;
4
5#[allow(dead_code)]
6#[derive(Copy, Clone)]
7pub enum PrimitiveType {
8    String,
9    I64,
10    U64,
11    F64,
12    Boolean,
13    Null,
14    Array,
15    Object,
16    // Reserved for future use in Rustless
17    File
18}
19
20pub type CoercerResult<T> = Result<T, super::super::ValicoErrors>;
21
22pub trait Coercer: Send + Sync {
23    fn get_primitive_type(&self) -> PrimitiveType;
24    fn coerce(&self, &mut Value, &str) -> CoercerResult<Option<Value>>;
25}
26
27#[derive(Copy, Clone)]
28pub struct StringCoercer;
29
30impl Coercer for StringCoercer {
31    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::String }
32    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
33        if val.is_string() {
34            Ok(None)
35        } else if val.is_number() {
36            Ok(Some(to_value(&to_string(&val).unwrap()).unwrap()))
37        } else {
38            Err(vec![
39                Box::new(errors::WrongType {
40                    path: path.to_string(),
41                    detail: "Can't coerce value to string".to_string()
42                })
43            ])
44        }
45    }
46}
47
48#[derive(Copy, Clone)]
49pub struct I64Coercer;
50
51impl Coercer for I64Coercer {
52    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::I64 }
53    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
54        if val.is_i64() {
55            return Ok(None)
56        } else if val.is_u64() {
57            let val = val.as_u64().unwrap();
58            return Ok(Some(to_value(&(val as i64)).unwrap()));
59        } else if val.is_f64() {
60            let val = val.as_f64().unwrap();
61            return Ok(Some(to_value(&(val as i64)).unwrap()));
62        } else if val.is_string() {
63            let val = val.as_str().unwrap();
64            let converted: Option<i64> = val.parse().ok();
65            match converted {
66                Some(num) => Ok(Some(to_value(&num).unwrap())),
67                None => Err(vec![
68                    Box::new(errors::WrongType {
69                        path: path.to_string(),
70                        detail: "Can't coerce string value to i64".to_string()
71                    })
72                ])
73            }
74        } else {
75            Err(vec![
76                Box::new(errors::WrongType {
77                    path: path.to_string(),
78                    detail: "Can't coerce object value to i64".to_string()
79                })
80            ])
81        }
82    }
83}
84
85#[derive(Copy, Clone)]
86pub struct U64Coercer;
87
88impl Coercer for U64Coercer {
89    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::U64 }
90    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
91        if val.is_u64() {
92            return Ok(None)
93        } else if val.is_i64() {
94            let val = val.as_i64().unwrap();
95            return Ok(Some(to_value(&(val as u64)).unwrap()));
96        } else if val.is_f64() {
97            let val = val.as_f64().unwrap();
98            return Ok(Some(to_value(&(val as u64)).unwrap()));
99        } else if val.is_string() {
100            let val = val.as_str().unwrap();
101            let converted: Option<u64> = val.parse().ok();
102            match converted {
103                Some(num) => Ok(Some(to_value(&num).unwrap())),
104                None => Err(vec![
105                    Box::new(errors::WrongType {
106                        path: path.to_string(),
107                        detail: "Can't coerce string value to u64".to_string()
108                    })
109                ])
110            }
111        } else {
112            Err(vec![
113                Box::new(errors::WrongType {
114                    path: path.to_string(),
115                    detail: "Can't coerce object value to u64".to_string()
116                })
117            ])
118        }
119    }
120}
121
122#[derive(Copy, Clone)]
123pub struct F64Coercer;
124
125impl Coercer for F64Coercer {
126    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::F64 }
127    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
128        if val.is_f64() {
129            return Ok(None)
130        } else if val.is_i64() {
131            let val = val.as_i64().unwrap();
132            return Ok(Some(to_value(&(val as f64)).unwrap()));
133        } else if val.is_u64() {
134            let val = val.as_u64().unwrap();
135            return Ok(Some(to_value(&(val as f64)).unwrap()));
136        } else if val.is_string() {
137            let val = val.as_str().unwrap();
138            let converted: Option<f64> = val.parse().ok();
139            match converted {
140                Some(num) => Ok(Some(to_value(&num).unwrap())),
141                None => Err(vec![
142                    Box::new(errors::WrongType {
143                        path: path.to_string(),
144                        detail: "Can't coerce string value to f64".to_string()
145                    })
146                ])
147            }
148        } else {
149            Err(vec![
150                Box::new(errors::WrongType {
151                    path: path.to_string(),
152                    detail: "Can't coerce object value to f64".to_string()
153                })
154            ])
155        }
156    }
157}
158
159#[derive(Copy, Clone)]
160pub struct BooleanCoercer;
161
162impl Coercer for BooleanCoercer {
163    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::Boolean }
164    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
165        if val.is_boolean() {
166            Ok(None)
167        } else if val.is_string() {
168            let val = val.as_str().unwrap();
169            if val == "true" {
170                Ok(Some(Value::Bool(true)))
171            } else if val == "false" {
172                Ok(Some(Value::Bool(false)))
173            } else {
174                Err(vec![
175                    Box::new(errors::WrongType {
176                        path: path.to_string(),
177                        detail: "Can't coerce this string value to boolean. Correct values are 'true' and 'false'".to_string()
178                    })
179                ])
180            }
181        } else {
182            Err(vec![
183                Box::new(errors::WrongType {
184                    path: path.to_string(),
185                    detail: "Can't coerce object to boolean".to_string()
186                })
187            ])
188        }
189    }
190}
191
192#[derive(Copy, Clone)]
193pub struct NullCoercer;
194
195impl Coercer for NullCoercer {
196    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::Null }
197    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
198        if val.is_null() {
199            Ok(None)
200        } else if val.is_string() {
201            let val = val.as_str().unwrap();
202            if val == "" {
203                Ok(Some(Value::Null))
204            } else {
205                Err(vec![
206                    Box::new(errors::WrongType {
207                        path: path.to_string(),
208                        detail: "Can't coerce this string value to null. Correct value is only empty string".to_string()
209                    })
210                ])
211            }
212        } else {
213            Err(vec![
214                Box::new(errors::WrongType {
215                    path: path.to_string(),
216                    detail: "Can't coerce object to null".to_string()
217                })
218            ])
219        }
220    }
221}
222
223pub struct ArrayCoercer {
224    sub_coercer: Option<Box<Coercer + Send + Sync>>,
225    separator: Option<String>
226}
227
228impl ArrayCoercer {
229    pub fn new() -> ArrayCoercer {
230        ArrayCoercer {
231            sub_coercer: None,
232            separator: None
233        }
234    }
235
236    pub fn encoded(separator: String) -> ArrayCoercer {
237        ArrayCoercer {
238            separator: Some(separator),
239            sub_coercer: None
240        }
241    }
242
243    pub fn encoded_of(separator: String, sub_coercer: Box<Coercer + Send + Sync>) -> ArrayCoercer {
244        ArrayCoercer {
245            separator: Some(separator),
246            sub_coercer: Some(sub_coercer)
247        }
248    }
249
250    pub fn of_type(sub_coercer: Box<Coercer + Send + Sync>) -> ArrayCoercer {
251        ArrayCoercer {
252            separator: None,
253            sub_coercer: Some(sub_coercer)
254        }
255    }
256
257    fn coerce_array(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
258        let array = val.as_array_mut().unwrap();
259        if self.sub_coercer.is_some() {
260            let sub_coercer = self.sub_coercer.as_ref().unwrap();
261            let mut errors = vec![];
262            for i in 0..array.len() {
263                let item_path = [path, i.to_string().as_ref()].join("/");
264                match sub_coercer.coerce(&mut array[i], item_path.as_ref()) {
265                    Ok(Some(value)) => {
266                        array.remove(i);
267                        array.insert(i, value);
268                    },
269                    Ok(None) => (),
270                    Err(err) => {
271                        errors.extend(err);
272                    }
273                }
274            }
275
276            if errors.len() == 0 {
277                Ok(None)
278            } else {
279                Err(errors)
280            }
281        } else {
282            Ok(None)
283        }
284    }
285}
286
287impl Coercer for ArrayCoercer {
288    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::Array }
289
290    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
291        if val.is_array() {
292            self.coerce_array(val, path)
293        } else if val.is_string() && self.separator.is_some() {
294            let separator = self.separator.as_ref().unwrap();
295            let string = val.as_str().unwrap();
296            let mut array = Value::Array(
297                string
298                    .split(&separator[..])
299                    .map(|s| Value::String(s.to_string()))
300                    .collect::<Vec<Value>>()
301            );
302            try!(self.coerce_array(&mut array, path));
303            Ok(Some(array))
304        } else {
305            Err(vec![
306                Box::new(errors::WrongType {
307                    path: path.to_string(),
308                    detail: "Can't coerce object to array".to_string()
309                })
310            ])
311        }
312    }
313}
314
315#[derive(Copy, Clone)]
316pub struct ObjectCoercer;
317
318impl Coercer for ObjectCoercer {
319    fn get_primitive_type(&self) -> PrimitiveType { PrimitiveType::Object }
320    fn coerce(&self, val: &mut Value, path: &str) -> CoercerResult<Option<Value>> {
321        if val.is_object() {
322            Ok(None)
323        } else {
324            Err(vec![
325                Box::new(errors::WrongType {
326                    path: path.to_string(),
327                    detail: "Can't coerce non-object value to the object type".to_string()
328                })
329            ])
330        }
331    }
332}