trans_gen/gens/go/
mod.rs

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