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 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}