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