teo_runtime/coder/
json_to_teon.rs

1use std::collections::{BTreeMap, BTreeSet};
2use std::str::FromStr;
3use bigdecimal::{BigDecimal, FromPrimitive};
4use bson::oid::ObjectId;
5use chrono::{DateTime, NaiveDate, Utc};
6use indexmap::IndexMap;
7use key_path::KeyPath;
8use maplit::btreemap;
9use teo_parser::ast::schema::Schema;
10use teo_parser::r#type::synthesized_enum::SynthesizedEnum;
11use teo_parser::r#type::synthesized_enum_reference::SynthesizedEnumReference;
12use teo_parser::r#type::synthesized_interface_enum::SynthesizedInterfaceEnum;
13use teo_parser::r#type::synthesized_interface_enum_reference::SynthesizedInterfaceEnumReference;
14use teo_parser::r#type::synthesized_shape::SynthesizedShape;
15use teo_parser::r#type::synthesized_shape_reference::SynthesizedShapeReference;
16use teo_parser::r#type::Type;
17use teo_parser::traits::resolved::Resolve;
18use crate::value::Value;
19use crate::value::file::File;
20use crate::interface::Interface;
21use crate::namespace::Namespace;
22use teo_result::Error;
23use crate::namespace;
24use crate::utils::ContainsStr;
25
26
27pub fn fetch_synthesized_interface_enum<'a>(reference: &SynthesizedInterfaceEnumReference, schema: &'a Schema) -> &'a SynthesizedInterfaceEnum {
28    let model = schema.find_top_by_path(reference.owner.as_model_object().unwrap().path()).unwrap().as_model().unwrap();
29    model.resolved().interface_enums.get(&reference.kind).unwrap()
30}
31
32pub fn fetch_synthesized_enum<'a>(reference: &SynthesizedEnumReference, main_namespace: &'a namespace::Builder) -> SynthesizedEnum {
33    let model = main_namespace.model_at_path(&reference.owner.as_model_object().unwrap().string_path()).unwrap();
34    model.cache().shape.enums.get(&reference.kind).unwrap().clone()
35}
36
37pub fn fetch_synthesized_enum_from_namespace<'a>(reference: &SynthesizedEnumReference, main_namespace: &'a Namespace) -> &'a SynthesizedEnum {
38    let model = main_namespace.model_at_path(&reference.owner.as_model_object().unwrap().string_path()).unwrap();
39    model.cache().shape.enums.get(&reference.kind).unwrap()
40}
41
42pub fn fetch_input<'a>(reference: &SynthesizedShapeReference, main_namespace: &'a Namespace) -> &'a Type {
43    let model = main_namespace.model_at_path(&reference.owner.as_model_object().unwrap().string_path()).unwrap();
44    if reference.kind.requires_without() {
45        model.cache().shape.get_without(reference.kind, reference.without.as_ref().unwrap()).unwrap()
46    } else {
47        model.cache().shape.get(reference.kind).unwrap()
48    }
49}
50
51pub fn json_to_teon_with_type(json: &serde_json::Value, path: &KeyPath, t: &Type, main_namespace: &Namespace) -> teo_result::Result<Value> {
52    match t {
53        Type::Undetermined => Ok(Value::from(json)),
54        Type::Ignored => Ok(Value::from(json)),
55        Type::Any => Ok(Value::from(json)),
56        Type::Null => if json.is_null() { Ok(Value::Null) } else { Err(Error::invalid_request_pathed(path.clone(), "expect null")) },
57        Type::Bool => if json.is_boolean() { Ok(Value::from(json)) } else { Err(Error::invalid_request_pathed(path.clone(), "expect bool")) },
58        Type::Int => if json.is_i64() { Ok(Value::Int(json.as_i64().unwrap() as i32)) } else { Err(Error::invalid_request_pathed(path.clone(), "expect int")) },
59        Type::Int64 => if json.is_i64() { Ok(Value::Int64(json.as_i64().unwrap())) } else { Err(Error::invalid_request_pathed(path.clone(), "expect int 64")) },
60        Type::Float32 => if json.is_f64() { Ok(Value::Float32(json.as_f64().unwrap() as f32)) } else if json.is_i64() { Ok(Value::Float32(json.as_i64().unwrap() as f32)) } else { Err(Error::invalid_request_pathed(path.clone(), "expect float 32")) },
61        Type::Float => if json.is_f64() { Ok(Value::Float(json.as_f64().unwrap())) } else if json.is_i64() { Ok(Value::Float(json.as_i64().unwrap() as f64)) } else { Err(Error::invalid_request_pathed(path.clone(), "expect float")) },
62        Type::Decimal => if json.is_string() {
63            Ok(Value::Decimal(match BigDecimal::from_str(json.as_str().unwrap()) {
64                Ok(s) => s,
65                Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid decimal"))?,
66            }))
67        } else if json.is_number() {
68            if let Some(f) = json.as_f64() {
69                Ok(Value::Decimal(match BigDecimal::from_f64(f) {
70                    Some(s) => s,
71                    None => Err(Error::invalid_request_pathed(path.clone(), "number is not valid decimal"))?,
72                }))
73            } else if let Some(i) = json.as_i64() {
74                Ok(Value::Decimal(match BigDecimal::from_i64(i) {
75                    Some(s) => s,
76                    None => Err(Error::invalid_request_pathed(path.clone(), "number is not valid decimal"))?,
77                }))
78            } else {
79                unreachable!()
80            }
81        } else if let Some(object) = json.as_object() {
82            if object.keys().len() > 1 {
83                Err(Error::invalid_request_pathed(path.clone(), "wrong decimal object format"))?
84            }
85            if let Some(decimal) = object.get("$decimal") {
86                if decimal.is_string() {
87                    Ok(Value::Decimal(match BigDecimal::from_str(decimal.as_str().unwrap()) {
88                        Ok(s) => s,
89                        Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid decimal"))?,
90                    }))
91                } else {
92                    Err(Error::invalid_request_pathed(path.clone(), "wrong decimal object format"))?
93                }
94            } else {
95                Err(Error::invalid_request_pathed(path.clone(), "wrong decimal object format"))?
96            }
97        } else {
98            Err(Error::invalid_request_pathed(path.clone(), "expect string or number which represents decimal"))
99        }
100        Type::String => if json.is_string() { Ok(Value::String(json.as_str().unwrap().to_owned())) } else { Err(Error::invalid_request_pathed(path.clone(), "expect string")) },
101        Type::ObjectId => if json.is_string() {
102            Ok(Value::ObjectId(match ObjectId::parse_str(json.as_str().unwrap()) {
103                Ok(s) => s,
104                Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid object id"))?,
105            }))
106        } else {
107            Err(Error::invalid_request_pathed(path.clone(), "expect string represents object id"))
108        }
109        Type::Date => if json.is_string() {
110            Ok(Value::Date(match NaiveDate::parse_from_str(json.as_str().unwrap(), "%Y-%m-%d") {
111                Ok(s) => s,
112                Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid date"))?,
113            }))
114        } else if let Some(object) = json.as_object() {
115            if object.keys().len() > 1 {
116                Err(Error::invalid_request_pathed(path.clone(), "wrong date object format"))?
117            }
118            if let Some(date) = object.get("$date") {
119                if date.is_string() {
120                    Ok(Value::Date(match NaiveDate::parse_from_str(date.as_str().unwrap(), "%Y-%m-%d") {
121                        Ok(s) => s,
122                        Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid date"))?,
123                    }))
124                } else {
125                    Err(Error::invalid_request_pathed(path.clone(), "wrong date object format"))?
126                }
127            } else {
128                Err(Error::invalid_request_pathed(path.clone(), "wrong date object format"))?
129            }
130        } else {
131            Err(Error::invalid_request_pathed(path.clone(), "expect string represents date"))
132        }
133        Type::DateTime => if json.is_string() {
134            Ok(Value::DateTime(match DateTime::parse_from_rfc3339(json.as_str().unwrap()) {
135                Ok(d) => d.with_timezone(&Utc),
136                Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid datetime"))?,
137            }))
138        } else if let Some(object) = json.as_object() {
139            if object.keys().len() > 1 {
140                Err(Error::invalid_request_pathed(path.clone(), "wrong datetime object format"))?
141            }
142            if let Some(datetime) = object.get("$datetime") {
143                if datetime.is_string() {
144                    Ok(Value::DateTime(match DateTime::parse_from_rfc3339(datetime.as_str().unwrap()) {
145                        Ok(d) => d.with_timezone(&Utc),
146                        Err(_) => Err(Error::invalid_request_pathed(path.clone(), "string is not valid datetime"))?,
147                    }))
148                } else {
149                    Err(Error::invalid_request_pathed(path.clone(), "wrong datetime object format"))?
150                }
151            } else {
152                Err(Error::invalid_request_pathed(path.clone(), "wrong datetime object format"))?
153            }
154        } else {
155            Err(Error::invalid_request_pathed(path.clone(), "expect string or object represents datetime"))
156        }
157        Type::File => {
158            Ok(Value::File(match File::try_from(json) {
159                Ok(f) => f,
160                Err(err) => Err(Error::invalid_request_pathed(path.clone(), err.message()))?,
161            }))
162        },
163        Type::Enumerable(inner) => {
164            if let Some(json_array) = json.as_array() {
165                let values: Vec<Value> = json_array.iter().enumerate().map(|(i, j)| Ok(json_to_teon_with_type(j, &(path + i), inner.as_ref(), main_namespace)?)).collect::<teo_result::Result<Vec<Value>>>()?;
166                Ok(Value::Array(values))
167            } else {
168                Ok(Value::Array(vec![json_to_teon_with_type(json, path, inner.as_ref(), main_namespace)?]))
169            }
170        }
171        Type::Array(inner) => {
172            if let Some(json_array) = json.as_array() {
173                let values: Vec<Value> = json_array.iter().enumerate().map(|(i, j)| json_to_teon_with_type(j, &(path + i), inner.as_ref(), main_namespace)).collect::<teo_result::Result<Vec<Value>>>()?;
174                Ok(Value::Array(values))
175            } else {
176                Err(Error::invalid_request_pathed(path.clone(), "expect array"))
177            }
178        }
179        Type::Dictionary(inner) => {
180            if let Some(json_object) = json.as_object() {
181                let values: IndexMap<String, Value> = json_object.iter().map(|(k, j)| Ok((k.clone(), json_to_teon_with_type(j, &(path + k), inner.as_ref(), main_namespace)?))).collect::<teo_result::Result<IndexMap<String, Value>>>()?;
182                Ok(Value::Dictionary(values))
183            } else {
184                Err(Error::invalid_request_pathed(path.clone(), "expect dictionary"))
185            }
186        }
187        Type::Tuple(_) => Err(Error::invalid_request_pathed(path.clone(), "unexpected type"))?,
188        Type::Range(_) => Err(Error::invalid_request_pathed(path.clone(), "unexpected type"))?,
189        Type::Union(inners) => {
190            for inner in inners {
191                if let Ok(result) = json_to_teon_with_type(json, path, inner, main_namespace) {
192                    return Ok(result);
193                }
194            }
195            Err(Error::invalid_request_pathed(path.clone(), "unexpected value"))
196        }
197        Type::EnumVariant(reference) => if json.is_string() {
198            let e = main_namespace.enum_at_path(reference.string_path()).unwrap();
199            if e.member_names().contains_str(json.as_str().unwrap()) {
200                Ok(Value::String(json.as_str().unwrap().to_owned()))
201            } else {
202                Err(Error::invalid_request_pathed(path.clone(), "expect enum member"))
203            }
204        } else {
205            Err(Error::invalid_request_pathed(path.clone(), "expect string represents enum member"))
206        }
207        Type::InterfaceObject(reference, gens) => {
208            let i = main_namespace.interface_at_path(&reference.string_path()).unwrap();
209            let shape = collect_interface_shape(i, gens);
210            json_to_teon_with_shape(json, path, &shape, main_namespace)
211        }
212        Type::Optional(inner) => {
213            if json.is_null() {
214                Ok(Value::Null)
215            } else {
216                json_to_teon_with_type(json, path, inner.as_ref(), main_namespace)
217            }
218        },
219        Type::SynthesizedShapeReference(shape_reference) => {
220            let input = fetch_input(shape_reference, main_namespace);
221            json_to_teon(json, path, input, main_namespace)
222        },
223        Type::SynthesizedEnumReference(enum_reference) => {
224            let synthesized_enum = fetch_synthesized_enum_from_namespace(enum_reference, main_namespace);
225            json_to_teon_with_synthesized_enum(json, path, synthesized_enum)
226        },
227        Type::SynthesizedEnum(synthesized_enum) => json_to_teon_with_synthesized_enum(json, path, synthesized_enum),
228        Type::SynthesizedShape(synthesized_shape) => json_to_teon_with_shape(json, path, synthesized_shape, main_namespace),
229        Type::DeclaredSynthesizedShape(synthesized_shape_reference, model_type) => {
230            if let Some(model_reference) = model_type.as_model_object() {
231                let m = main_namespace.model_at_path(model_reference.string_path()).unwrap();
232                if let Some(shape) = m.cache().shape.get_declared(synthesized_shape_reference.string_path()) {
233                    json_to_teon_with_shape(json, path, shape, main_namespace)
234                } else {
235                    Err(Error::invalid_request_pathed(path.clone(), "unexpected type"))?
236                }
237            } else {
238                Err(Error::invalid_request_pathed(path.clone(), "unexpected type"))?
239            }
240        },
241        _ => Err(Error::invalid_request_pathed(path.clone(), "unexpected type"))?,
242    }
243}
244
245fn json_to_teon_with_synthesized_enum(json: &serde_json::Value, path: &KeyPath, synthesized_enum: &SynthesizedEnum) -> teo_result::Result<Value> {
246    if json.is_string() {
247        let name = json.as_str().unwrap();
248        if synthesized_enum.keys.contains_str(name) {
249            return Ok(Value::String(name.to_owned()));
250        }
251    }
252    Err(Error::invalid_request_pathed(path.clone(), "expect string enum variant"))
253}
254
255pub fn json_to_teon_with_shape(json: &serde_json::Value, path: &KeyPath, shape: &SynthesizedShape, main_namespace: &Namespace) -> teo_result::Result<Value> {
256    if let Some(object) = json.as_object() {
257        let required_keys: BTreeSet<&str> = shape.iter().filter_map(|(k, v)| if !v.is_optional() {
258            Some(k.as_str())
259        } else {
260            None
261        }).collect();
262        let all_keys: BTreeSet<&str> = shape.keys().map(AsRef::as_ref).collect();
263        let passed_in_keys: BTreeSet<&str> = object.keys().map(AsRef::as_ref).collect();
264        let unallowed_keys: Vec<&str> = passed_in_keys.difference(&all_keys).map(|s| *s).collect();
265        if let Some(unallowed) = unallowed_keys.first() {
266            return Err(Error::invalid_request_pathed(path + *unallowed, "unexpected key"));
267        }
268        let not_provided_keys: Vec<&str> = required_keys.difference(&passed_in_keys).map(|s| *s).collect();
269        if let Some(not_provided) = not_provided_keys.first() {
270            return Err(Error::invalid_request_pathed(path + *not_provided, "expect value"));
271        }
272        let map: IndexMap<String, Value> = object.iter().map(|(k, v)| Ok((k.to_owned(), json_to_teon(v, &(path + k), shape.get(k).unwrap(), main_namespace)?))).collect::<teo_result::Result<IndexMap<String, Value>>>()?;
273        Ok(Value::Dictionary(map))
274    } else {
275        Err(Error::invalid_request_pathed(path.clone(), "unexpected value"))
276    }
277
278}
279
280pub fn json_to_teon(json: &serde_json::Value, path: &KeyPath, input: &Type, main_namespace: &Namespace) -> teo_result::Result<Value> {
281    json_to_teon_with_type(json, path, input, main_namespace)
282}
283
284fn collect_interface_shape(interface: &Interface, gens: &Vec<Type>) -> SynthesizedShape {
285    interface.shape_from_generics(gens)
286}