trans_gen_javascript/
lib.rs

1use std::collections::{HashMap, HashSet};
2use std::fmt::Write;
3use trans_gen_core::trans_schema::*;
4use trans_gen_core::Writer;
5
6fn conv(name: &str) -> String {
7    name.replace("Int32", "Int")
8        .replace("Int64", "Long")
9        .replace("Float32", "Float")
10        .replace("Float64", "Double")
11}
12
13pub struct Generator {
14    files: HashMap<String, String>,
15}
16
17fn index_var_name(index_var: &mut usize) -> String {
18    let result = "ijk".chars().nth(*index_var).unwrap();
19    *index_var += 1;
20    result.to_string()
21}
22
23fn write_imports(writer: &mut Writer, schema: &Schema) -> std::fmt::Result {
24    fn write_imports_struct(
25        writer: &mut Writer,
26        struc: &Struct,
27        added: &mut HashSet<String>,
28    ) -> std::fmt::Result {
29        for field in &struc.fields {
30            fn add_imports(
31                writer: &mut Writer,
32                schema: &Schema,
33                added: &mut HashSet<String>,
34            ) -> std::fmt::Result {
35                match schema {
36                    Schema::Struct(Struct { name, .. })
37                    | Schema::OneOf {
38                        base_name: name, ..
39                    } => {
40                        if added.insert(file_name(name)) {
41                            writeln!(
42                                writer,
43                                "const {} = require('./{}').{};",
44                                name.camel_case(conv),
45                                file_name(name),
46                                name.camel_case(conv),
47                            )?;
48                        }
49                    }
50                    Schema::Option(inner) => {
51                        add_imports(writer, inner, added)?;
52                    }
53                    Schema::Vec(inner) => {
54                        add_imports(writer, inner, added)?;
55                    }
56                    Schema::Map(key_type, value_type) => {
57                        add_imports(writer, key_type, added)?;
58                        add_imports(writer, value_type, added)?;
59                    }
60                    Schema::Enum { .. } => {}
61                    Schema::Bool
62                    | Schema::Int32
63                    | Schema::Int64
64                    | Schema::Float32
65                    | Schema::Float64
66                    | Schema::String => {}
67                }
68                Ok(())
69            }
70            add_imports(writer, &field.schema, added)?;
71        }
72        Ok(())
73    }
74    let mut added = HashSet::new();
75    match schema {
76        Schema::Struct(struc) => {
77            write_imports_struct(writer, struc, &mut added)?;
78        }
79        Schema::OneOf { variants, .. } => {
80            for variant in variants {
81                write_imports_struct(writer, variant, &mut added)?;
82            }
83        }
84        _ => {}
85    }
86    Ok(())
87}
88
89fn write_struct(
90    writer: &mut Writer,
91    struc: &Struct,
92    base: Option<(&Name, usize)>,
93) -> std::fmt::Result {
94    // Class
95    write!(writer, "class {}", struc.name.camel_case(conv))?;
96    if let Some((base_name, _)) = base {
97        write!(writer, " extends {}", base_name.camel_case(conv))?;
98    }
99    writeln!(writer, " {{")?;
100    writer.inc_ident();
101
102    // Constructor
103    write!(writer, "constructor(")?;
104    for (index, field) in struc.fields.iter().enumerate() {
105        write!(writer, "{}", field.name.mixed_case(conv))?;
106        if index + 1 < struc.fields.len() {
107            write!(writer, ", ")?;
108        }
109    }
110    writeln!(writer, ") {{")?;
111    writer.inc_ident();
112    if base.is_some() {
113        writeln!(writer, "super();")?;
114    }
115    for field in &struc.fields {
116        writeln!(
117            writer,
118            "this.{} = {};",
119            field.name.mixed_case(conv),
120            field.name.mixed_case(conv)
121        )?;
122    }
123    writer.dec_ident();
124    writeln!(writer, "}}")?;
125
126    // Reading
127    writeln!(writer, "static async readFrom(stream) {{")?;
128    writer.inc_ident();
129    for field in &struc.fields {
130        fn assign(
131            writer: &mut Writer,
132            to: &str,
133            schema: &Schema,
134            index_var: &mut usize,
135        ) -> std::fmt::Result {
136            match schema {
137                Schema::Bool => {
138                    writeln!(writer, "{} = await stream.readBool();", to)?;
139                }
140                Schema::Int32 => {
141                    writeln!(writer, "{} = await stream.readInt();", to)?;
142                }
143                Schema::Int64 => {
144                    writeln!(writer, "{} = await stream.readLong();", to)?;
145                }
146                Schema::Float32 => {
147                    writeln!(writer, "{} = await stream.readFloat();", to)?;
148                }
149                Schema::Float64 => {
150                    writeln!(writer, "{} = await stream.readDouble();", to)?;
151                }
152                Schema::String => {
153                    writeln!(writer, "{} = await stream.readString();", to)?;
154                }
155                Schema::Struct(Struct { name, .. })
156                | Schema::OneOf {
157                    base_name: name, ..
158                } => {
159                    writeln!(
160                        writer,
161                        "{} = await {}.readFrom(stream);",
162                        to,
163                        name.camel_case(conv)
164                    )?;
165                }
166                Schema::Option(inner) => {
167                    writeln!(writer, "if (await stream.readBool()) {{")?;
168                    writer.inc_ident();
169                    assign(writer, to, inner, index_var)?;
170                    writer.dec_ident();
171                    writeln!(writer, "}} else {{")?;
172                    writeln!(writer, "    {} = null;", to)?;
173                    writeln!(writer, "}}")?;
174                }
175                Schema::Vec(inner) => {
176                    writeln!(writer, "{} = [];", to)?;
177                    let index_var_name = index_var_name(index_var);
178                    writeln!(
179                        writer,
180                        "for (let {} = await stream.readInt(); {} > 0; {}--) {{",
181                        index_var_name, index_var_name, index_var_name,
182                    )?;
183                    writer.inc_ident();
184                    writeln!(writer, "let {}Element;", to)?;
185                    assign(writer, &format!("{}Element", to), inner, index_var)?;
186                    writeln!(writer, "{}.push({}Element);", to, to)?;
187                    writer.dec_ident();
188                    writeln!(writer, "}}")?;
189                }
190                Schema::Map(key_type, value_type) => {
191                    writeln!(writer, "{} = new Map();", to)?;
192                    let index_var_name = index_var_name(index_var);
193                    writeln!(
194                        writer,
195                        "for (let {} = await stream.readInt(); {} > 0; {}--) {{",
196                        index_var_name, index_var_name, index_var_name,
197                    )?;
198                    writer.inc_ident();
199                    writeln!(writer, "let {}Key;", to)?;
200                    writeln!(writer, "let {}Value;", to)?;
201                    assign(writer, &format!("{}Key", to), key_type, index_var)?;
202                    assign(writer, &format!("{}Value", to), value_type, index_var)?;
203                    writeln!(writer, "{}.set({}Key, {}Value);", to, to, to)?;
204                    writer.dec_ident();
205                    writeln!(writer, "}}")?;
206                }
207                Schema::Enum { base_name, .. } => {
208                    writeln!(writer, "{} = await stream.readInt();", to)?;
209                }
210            }
211            Ok(())
212        }
213        writeln!(writer, "let {};", field.name.mixed_case(conv))?;
214        assign(writer, &field.name.mixed_case(conv), &field.schema, &mut 0)?;
215    }
216    write!(writer, "return new {}(", struc.name.camel_case(conv))?;
217    let mut first = true;
218    for field in &struc.fields {
219        if first {
220            first = false;
221        } else {
222            write!(writer, ", ")?;
223        }
224        write!(writer, "{}", field.name.mixed_case(conv))?;
225    }
226    writeln!(writer, ");")?;
227    writer.dec_ident();
228    writeln!(writer, "}}")?;
229
230    // Writing
231    writeln!(writer, "async writeTo(stream) {{")?;
232    writer.inc_ident();
233    if base.is_some() {
234        writeln!(
235            writer,
236            "await stream.writeInt({}.TAG);",
237            struc.name.camel_case(conv),
238        )?;
239    }
240    if let Some(magic) = struc.magic {
241        writeln!(writer, "await stream.writeInt({});", magic)?;
242    }
243    for field in &struc.fields {
244        fn write(
245            writer: &mut Writer,
246            value: &str,
247            schema: &Schema,
248            index_var: &mut usize,
249        ) -> std::fmt::Result {
250            match schema {
251                Schema::Bool => {
252                    writeln!(writer, "await stream.writeBool({});", value)?;
253                }
254                Schema::Int32 => {
255                    writeln!(writer, "await stream.writeInt({});", value)?;
256                }
257                Schema::Int64 => {
258                    writeln!(writer, "await stream.writeLong({});", value)?;
259                }
260                Schema::Float32 => {
261                    writeln!(writer, "await stream.writeFloat({});", value)?;
262                }
263                Schema::Float64 => {
264                    writeln!(writer, "await stream.writeDouble({});", value)?;
265                }
266                Schema::String => {
267                    writeln!(writer, "await stream.writeString({});", value)?;
268                }
269                Schema::Struct(_) | Schema::OneOf { .. } => {
270                    writeln!(writer, "await {}.writeTo(stream);", value)?;
271                }
272                Schema::Option(inner) => {
273                    writeln!(writer, "if ({} === null) {{", value)?;
274                    writeln!(writer, "    await stream.writeBool(false);")?;
275                    writeln!(writer, "}} else {{")?;
276                    writer.inc_ident();
277                    writeln!(writer, "await stream.writeBool(true);")?;
278                    write(writer, value, inner, index_var)?;
279                    writer.dec_ident();
280                    writeln!(writer, "}}")?;
281                }
282                Schema::Vec(inner) => {
283                    writeln!(writer, "await stream.writeInt({}.length);", value)?;
284                    writeln!(writer, "for (let {}Element of {}) {{", value, value,)?;
285                    writer.inc_ident();
286                    write(writer, &format!("{}Element", value), inner, index_var)?;
287                    writer.dec_ident();
288                    writeln!(writer, "}}")?;
289                }
290                Schema::Map(key_type, value_type) => {
291                    writeln!(writer, "await stream.writeInt({}.size);", value)?;
292                    let index_var_name = index_var_name(index_var);
293                    writeln!(writer, "for (let {}Entry of {}) {{", value, value)?;
294                    writer.inc_ident();
295                    writeln!(writer, "let {}Key = {}Entry[0];", value, value)?;
296                    writeln!(writer, "let {}Value = {}Entry[1];", value, value)?;
297                    write(writer, &format!("{}Key", value), key_type, index_var)?;
298                    write(writer, &format!("{}Value", value), value_type, index_var)?;
299                    writer.dec_ident();
300                    writeln!(writer, "}}")?;
301                }
302                Schema::Enum { .. } => {
303                    writeln!(writer, "await stream.writeInt({});", value)?;
304                }
305            }
306            Ok(())
307        }
308        writeln!(
309            writer,
310            "let {} = this.{};",
311            field.name.mixed_case(conv),
312            field.name.mixed_case(conv),
313        )?;
314        write(writer, &field.name.mixed_case(conv), &field.schema, &mut 0)?;
315    }
316    writer.dec_ident();
317    writeln!(writer, "}}")?;
318
319    writer.dec_ident();
320    writeln!(writer, "}}")?;
321
322    if let Some((base_name, _)) = base {
323        writeln!(
324            writer,
325            "{}.{} = {};",
326            base_name.camel_case(conv),
327            struc.name.camel_case(conv),
328            struc.name.camel_case(conv)
329        )?;
330    }
331
332    if let Some((_, discriminant)) = base {
333        writeln!(
334            writer,
335            "{}.TAG = {};",
336            struc.name.camel_case(conv),
337            discriminant,
338        )?;
339    }
340
341    Ok(())
342}
343
344fn file_name(name: &Name) -> String {
345    name.snake_case(conv).replace('_', "-")
346}
347
348impl trans_gen_core::Generator for Generator {
349    fn new(name: &str, version: &str) -> Self {
350        let mut files = HashMap::new();
351        files.insert(
352            "stream-wrapper.js".to_owned(),
353            include_str!("../template/stream-wrapper.js").to_owned(),
354        );
355        Self { files }
356    }
357    fn result(mut self) -> HashMap<String, String> {
358        self.files
359    }
360    fn add_only(&mut self, schema: &Schema) {
361        match schema {
362            Schema::Enum {
363                base_name,
364                variants,
365            } => {
366                let file_name = format!("model/{}.js", file_name(base_name));
367                let mut writer = Writer::new();
368                writeln!(writer, "module.exports = {{").unwrap();
369                writer.inc_ident();
370                for (index, variant) in variants.iter().enumerate() {
371                    writeln!(
372                        writer,
373                        "{} : {}{}",
374                        variant.camel_case(conv),
375                        index,
376                        if index + 1 < variants.len() { "," } else { "" }
377                    )
378                    .unwrap();
379                }
380                writer.dec_ident();
381                writeln!(writer, "}}").unwrap();
382                self.files.insert(file_name, writer.get());
383            }
384            Schema::Struct(struc) => {
385                let file_name = format!("model/{}.js", file_name(&struc.name));
386                let mut writer = Writer::new();
387                write_imports(&mut writer, schema).unwrap();
388                write_struct(&mut writer, struc, None).unwrap();
389                writeln!(
390                    writer,
391                    "module.exports = {{ {}: {} }}",
392                    struc.name.camel_case(conv),
393                    struc.name.camel_case(conv),
394                )
395                .unwrap();
396                self.files.insert(file_name, writer.get());
397            }
398            Schema::OneOf {
399                base_name,
400                variants,
401            } => {
402                let file_name = format!("model/{}.js", file_name(base_name));
403                let mut writer = Writer::new();
404                write_imports(&mut writer, schema).unwrap();
405                writeln!(&mut writer, "class {} {{", base_name.camel_case(conv)).unwrap();
406                {
407                    writer.inc_ident();
408                    writeln!(&mut writer, "static async readFrom(stream) {{").unwrap();
409                    writer.inc_ident();
410                    writeln!(&mut writer, "let discriminant = await stream.readInt();").unwrap();
411                    for variant in variants {
412                        writeln!(
413                            &mut writer,
414                            "if (discriminant == {}.TAG) {{",
415                            variant.name.camel_case(conv)
416                        )
417                        .unwrap();
418                        writeln!(
419                            &mut writer,
420                            "    return await {}.readFrom(stream);",
421                            variant.name.camel_case(conv),
422                        )
423                        .unwrap();
424                        writeln!(&mut writer, "}}").unwrap();
425                    }
426                    writeln!(
427                        &mut writer,
428                        "throw new Error(\"Unexpected discriminant value\");"
429                    )
430                    .unwrap();
431                    writer.dec_ident();
432                    writeln!(writer, "}}").unwrap();
433                }
434                writer.dec_ident();
435                writeln!(writer, "}}").unwrap();
436                writeln!(&mut writer).unwrap();
437                for (discriminant, variant) in variants.iter().enumerate() {
438                    write_struct(&mut writer, variant, Some((base_name, discriminant))).unwrap();
439                }
440                writeln!(
441                    writer,
442                    "module.exports = {{ {}: {} }}",
443                    base_name.camel_case(conv),
444                    base_name.camel_case(conv),
445                )
446                .unwrap();
447                self.files.insert(file_name, writer.get());
448            }
449            Schema::Bool
450            | Schema::Int32
451            | Schema::Int64
452            | Schema::Float32
453            | Schema::Float64
454            | Schema::String
455            | Schema::Option(_)
456            | Schema::Vec(_)
457            | Schema::Map(_, _) => {}
458        }
459    }
460}