trans_gen/gens/javascript/
mod.rs

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