schemajen/codegen/
dart.rs

1use super::*;
2
3pub struct ArrayField {
4    very_inner: String,
5    depth: usize,
6}
7
8impl ArrayField {
9    pub fn depth_from_ty(ty: &JsonType, begin: usize) -> usize {
10        let depth = begin;
11        match ty {
12            JsonType::Array(a) => Self::depth_from_ty(a, depth + 1),
13            _ => depth,
14        }
15    }
16}
17
18enum FieldType {
19    Primitive,
20    Struct,
21    Array(ArrayField),
22}
23
24struct StructItem {
25    acc: String,
26    name: String,
27    fields: Vec<(String, FieldType)>,
28}
29
30#[derive(Default)]
31pub struct DartAccumulator {
32    struct_stack: Vec<StructItem>,
33    done_list: Vec<String>,
34}
35
36impl DartAccumulator {
37    pub fn begin() -> Self {
38        DartAccumulator::default()
39    }
40
41    fn get_current(&mut self) -> &mut StructItem {
42        self.struct_stack.last_mut().unwrap()
43    }
44
45    fn is_builtin_type(s: &str) -> bool {
46        matches!(s, "dynamic" | "bool" | "String" | "double" | "int")
47    }
48
49    fn get_type(&mut self, ty: JsonType) -> String {
50        match ty {
51            JsonType::Null => String::from("dynamic"),
52            JsonType::Number(n) => String::from(self.get_number(n)),
53            JsonType::Boolean => String::from("bool"),
54            JsonType::String => String::from("String"),
55            JsonType::Object(ty) => ty,
56            JsonType::Array(ty) => format!("List<{}>", self.get_type(*ty)),
57        }
58    }
59
60    fn get_without_outer_list(&mut self, ty: JsonType) -> String {
61        match ty {
62            JsonType::Null => String::from("dynamic"),
63            JsonType::Number(n) => String::from(self.get_number(n)),
64            JsonType::Boolean => String::from("bool"),
65            JsonType::String => String::from("String"),
66            JsonType::Object(ty) => ty,
67            JsonType::Array(ty) => self.get_without_outer_list(*ty),
68        }
69    }
70
71    fn get_number(&self, num: Number) -> &'static str {
72        match num {
73            Number::Int => "int",
74            Number::Float => "double",
75        }
76    }
77}
78
79impl TypeAccumulator for DartAccumulator {
80    fn end(&mut self) -> String {
81        let mut end_str = String::new();
82
83        self.done_list.iter().for_each(|done| end_str += done);
84
85        end_str += r#"//  import 'dart:convert';
86//  var data = jsonDecode("{ ... }");
87//  var ty = T.fromJson(data);
88//  jsonEncode(ty.toJson());"#;
89
90        end_str
91    }
92
93    fn number(&mut self, key: &str, number: Number) -> Result<(), Error> {
94        let num_ty = self.get_number(number);
95        let acc = self.get_current();
96        acc.acc += &format!("\tfinal {} {};\n", num_ty, key);
97        acc.fields.push((String::from(key), FieldType::Primitive));
98        Ok(())
99    }
100
101    fn boolean(&mut self, key: &str) -> Result<(), Error> {
102        let acc = self.get_current();
103        acc.acc += &format!("\tfinal bool {};\n", key);
104        acc.fields.push((String::from(key), FieldType::Primitive));
105        Ok(())
106    }
107
108    fn string(&mut self, key: &str) -> Result<(), Error> {
109        let acc = self.get_current();
110        acc.acc += &format!("\tfinal String {};\n", key);
111        acc.fields.push((String::from(key), FieldType::Primitive));
112        Ok(())
113    }
114
115    fn unknown(&mut self, key: &str) -> Result<(), Error> {
116        let acc = self.get_current();
117        acc.acc += &format!("\tfinal dynamic {};\n", key);
118        acc.fields.push((String::from(key), FieldType::Primitive));
119        Ok(())
120    }
121
122    fn array(&mut self, key: &str, ty: JsonType) -> Result<(), Error> {
123        let ty_str = self.get_type(ty.clone());
124        let ty_without_outer = self.get_without_outer_list(ty.clone());
125        let acc = self.get_current();
126        let ty_name = format!("List<{}>", ty_str);
127        acc.acc += &format!("\tfinal {} {};\n", ty_name, key);
128        acc.fields.push((
129            String::from(key),
130            FieldType::Array(ArrayField {
131                very_inner: ty_without_outer,
132                depth: ArrayField::depth_from_ty(&ty, 1),
133            }),
134        ));
135        Ok(())
136    }
137
138    fn object(&mut self, key: &str, object_name: &str) -> Result<(), Error> {
139        let acc = self.get_current();
140        acc.acc += &format!("\tfinal {} {};\n", object_name, key);
141        acc.fields.push((String::from(key), FieldType::Struct));
142        Ok(())
143    }
144
145    fn push_object_type(&mut self, object_name: &str) -> Result<(), Error> {
146        self.struct_stack.push(StructItem {
147            acc: String::new(),
148            name: String::from(object_name),
149            fields: vec![],
150        });
151        let acc = self.get_current();
152        acc.acc += &format!("class {} {{\n", object_name);
153        Ok(())
154    }
155
156    fn pop_object_type(&mut self) -> Result<(), Error> {
157        let acc = self.get_current();
158        acc.acc += &format!("\t{}.fromJson(Map<String, dynamic> json):\n", acc.name);
159        acc.fields.iter().for_each(|(field, field_ty)| {
160            acc.acc += &format!(
161                "\t\t{} = {},\n",
162                field,
163                match field_ty {
164                    FieldType::Struct | FieldType::Primitive => format!("json['{}']", field),
165                    FieldType::Array(s) => {
166                        fn list_from_depth(name: &str, depth: usize) -> String {
167                            let mut acc = String::from(name);
168                            (0..depth + 1).for_each(|_| {
169                                acc = format!("List<{}>", acc);
170                            });
171                            acc
172                        }
173                        let mut iname = String::from("i");
174                        let mut acc = if Self::is_builtin_type(&s.very_inner) {
175                            iname.clone()
176                        } else {
177                            format!("{}.fromJson({})", s.very_inner, iname)
178                        };
179                        for depth in 0..s.depth {
180                            let next_iname = if depth == s.depth - 1 {
181                                format!("json['{}']", field)
182                            } else {
183                                format!("i{}", depth)
184                            };
185
186                            acc = format!(
187                                "{}.from({}.map(({}) => {}))",
188                                list_from_depth(&s.very_inner, depth),
189                                next_iname,
190                                iname,
191                                acc
192                            );
193                            iname = next_iname;
194                        }
195                        acc
196                    }
197                }
198            );
199        });
200        acc.acc.pop();
201        acc.acc.pop();
202        acc.acc += ";\n\n";
203        acc.acc += "\tMap<String, dynamic> toJson() => {";
204        acc.fields.iter().for_each(|(field, field_ty)| {
205            acc.acc += &format!(
206                "\n\t\t'{}': {},",
207                field,
208                &match field_ty {
209                    FieldType::Struct => field.to_owned() + ".toJson()",
210                    FieldType::Primitive => field.to_owned(),
211                    FieldType::Array(s) => {
212                        let mut iname = String::from("i");
213                        let mut acc = if Self::is_builtin_type(&s.very_inner) {
214                            iname.clone()
215                        } else {
216                            iname.clone() + ".toJson()"
217                        };
218                        for depth in 0..s.depth {
219                            let next_iname = if depth == s.depth - 1 {
220                                field.to_owned()
221                            } else {
222                                format!("i{}", depth)
223                            };
224                            acc = format!("{}.map(({}) => {}).toList()", next_iname, iname, acc);
225                            iname = next_iname;
226                        }
227                        acc
228                    }
229                }
230            );
231        });
232        acc.acc += "\n\t};\n";
233        acc.acc += "}\n\n";
234        let s = self.struct_stack.pop().unwrap();
235        self.done_list.push(s.acc);
236        Ok(())
237    }
238
239    fn prefered_object_name(&self) -> String {
240        String::from("_Type")
241    }
242}