trans_gen_cpp/
lib.rs

1use std::collections::HashMap;
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 type_name(schema: &Schema) -> String {
18    match schema {
19        Schema::Bool => "bool".to_owned(),
20        Schema::Int32 => "int".to_owned(),
21        Schema::Int64 => "long long".to_owned(),
22        Schema::Float32 => "float".to_owned(),
23        Schema::Float64 => "double".to_owned(),
24        Schema::String => "std::string".to_owned(),
25        Schema::OneOf {
26            base_name: name, ..
27        } => format!("std::shared_ptr<{}>", name.camel_case(conv)),
28        Schema::Struct(Struct { name, .. })
29        | Schema::Enum {
30            base_name: name, ..
31        } => format!("{}", name.camel_case(conv)),
32        Schema::Option(inner) => format!("std::shared_ptr<{}>", type_name(inner)),
33        Schema::Vec(inner) => format!("std::vector<{}>", type_name(inner)),
34        Schema::Map(key, value) => format!(
35            "std::unordered_map<{}, {}>",
36            type_name(key),
37            type_name(value)
38        ),
39    }
40}
41
42fn index_var_name(index_var: &mut usize) -> String {
43    let result = "ijk".chars().nth(*index_var).unwrap();
44    *index_var += 1;
45    result.to_string()
46}
47
48fn var_name(name: &str) -> &str {
49    match name.rfind('.') {
50        Some(index) => &name[(index + 1)..],
51        None => name,
52    }
53}
54
55fn write_includes(writer: &mut Writer, schema: &Schema, current: bool) -> std::fmt::Result {
56    let mut includes = vec!["<string>".to_string(), "\"../Stream.hpp\"".to_string()];
57    collect_includes(&mut includes, schema, current);
58    includes.sort();
59    includes.dedup();
60    for include in includes {
61        writeln!(writer, "#include {}", include)?;
62    }
63    Ok(())
64}
65
66fn collect_includes(result: &mut Vec<String>, schema: &Schema, current: bool) {
67    if current {
68        match schema {
69            Schema::Bool
70            | Schema::Int32
71            | Schema::Int64
72            | Schema::Float32
73            | Schema::Float64
74            | Schema::String => {}
75            Schema::Option(_) => {
76                result.push("<memory>".to_string());
77            }
78            Schema::Map(_, _) => {
79                result.push("<unordered_map>".to_string());
80            }
81            Schema::Vec(_) => {
82                result.push("<vector>".to_string());
83            }
84            Schema::Struct(Struct { name, .. })
85            | Schema::OneOf {
86                base_name: name, ..
87            }
88            | Schema::Enum {
89                base_name: name, ..
90            } => {
91                result.push("<stdexcept>".to_string());
92                result.push(format!("\"{}.hpp\"", name.camel_case(conv)));
93            }
94        }
95    }
96    match schema {
97        Schema::Bool
98        | Schema::Int32
99        | Schema::Int64
100        | Schema::Float32
101        | Schema::Float64
102        | Schema::String
103        | Schema::Enum { .. } => {}
104        Schema::Option(inner) => {
105            collect_includes(result, inner, true);
106        }
107        Schema::Map(key_type, value_type) => {
108            collect_includes(result, key_type, true);
109            collect_includes(result, value_type, true);
110        }
111        Schema::Vec(inner) => {
112            collect_includes(result, inner, true);
113        }
114        Schema::Struct(Struct { fields, .. }) => {
115            for field in fields {
116                collect_includes(result, &field.schema, true);
117            }
118        }
119        Schema::OneOf { variants, .. } => {
120            for variant in variants {
121                for field in &variant.fields {
122                    collect_includes(result, &field.schema, true);
123                }
124            }
125        }
126    }
127}
128
129fn write_struct_def(
130    writer: &mut Writer,
131    schema: &Schema,
132    struc: &Struct,
133    base: Option<(&Name, usize)>,
134) -> std::fmt::Result {
135    let full_name = if let Some((base_name, _)) = base {
136        format!(
137            "{}::{}",
138            base_name.camel_case(conv),
139            struc.name.camel_case(conv)
140        )
141    } else {
142        struc.name.camel_case(conv)
143    };
144
145    // Class
146    if let Some((base_name, _)) = base {
147        writeln!(
148            writer,
149            "class {}::{} : public {} {{",
150            base_name.camel_case(conv),
151            struc.name.camel_case(conv),
152            base_name.camel_case(conv),
153        )?;
154    } else {
155        writeln!(writer, "class {} {{", struc.name.camel_case(conv))?;
156    }
157    writer.inc_ident();
158    if let Some((_, discriminant)) = base {
159        writer.dec_ident();
160        writeln!(writer, "public:")?;
161        writer.inc_ident();
162        writeln!(writer, "static const int TAG = {};", discriminant)?;
163    }
164
165    // Fields
166    writer.dec_ident();
167    writeln!(writer, "public:")?;
168    writer.inc_ident();
169    for field in &struc.fields {
170        writeln!(
171            writer,
172            "{} {};",
173            type_name(&field.schema),
174            field.name.mixed_case(conv)
175        )?;
176    }
177
178    // Constructor
179    writeln!(writer, "{}();", struc.name.camel_case(conv))?;
180    if !struc.fields.is_empty() {
181        write!(writer, "{}(", struc.name.camel_case(conv))?;
182        for (index, field) in struc.fields.iter().enumerate() {
183            if index > 0 {
184                write!(writer, ", ")?;
185            }
186            write!(
187                writer,
188                "{} {}",
189                type_name(&field.schema),
190                field.name.mixed_case(conv)
191            )?;
192        }
193        writeln!(writer, ");")?;
194    }
195
196    // Read/write
197    writeln!(
198        writer,
199        "static {} readFrom(InputStream& stream);",
200        struc.name.camel_case(conv)
201    )?;
202    writeln!(writer, "void writeTo(OutputStream& stream) const;")?;
203
204    // Eq
205    if schema.hashable() {
206        writeln!(
207            writer,
208            "bool operator ==(const {}& other) const;",
209            struc.name.camel_case(conv)
210        )?;
211    }
212
213    // ToString
214    writeln!(
215        writer,
216        "std::string toString() const{};",
217        if base.is_some() { " override" } else { "" }
218    )?;
219
220    writer.dec_ident();
221    writeln!(writer, "}};").unwrap();
222
223    // Hash
224    if schema.hashable() {
225        writeln!(writer, "namespace std {{")?;
226        writer.inc_ident();
227        writeln!(writer, "template<>")?;
228        writeln!(writer, "struct hash<{}> {{", full_name)?;
229        writeln!(
230            writer,
231            "    size_t operator ()(const {}& value) const;",
232            full_name
233        )?;
234        writeln!(writer, "}};")?;
235        writer.dec_ident();
236        writeln!(writer, "}}")?;
237    }
238
239    Ok(())
240}
241
242fn write_struct_impl(
243    writer: &mut Writer,
244    schema: &Schema,
245    struc: &Struct,
246    base: Option<(&Name, usize)>,
247) -> std::fmt::Result {
248    let full_name = if let Some((base_name, _)) = base {
249        format!(
250            "{}::{}",
251            base_name.camel_case(conv),
252            struc.name.camel_case(conv)
253        )
254    } else {
255        struc.name.camel_case(conv)
256    };
257
258    // Constructor
259    writeln!(
260        writer,
261        "{}::{}() {{ }}",
262        full_name,
263        struc.name.camel_case(conv)
264    )?;
265    if !struc.fields.is_empty() {
266        write!(writer, "{}::{}(", full_name, struc.name.camel_case(conv))?;
267        for (index, field) in struc.fields.iter().enumerate() {
268            if index > 0 {
269                write!(writer, ", ")?;
270            }
271            write!(
272                writer,
273                "{} {}",
274                type_name(&field.schema),
275                field.name.mixed_case(conv),
276            )?;
277        }
278        write!(writer, ") : ")?;
279        for (index, field) in struc.fields.iter().enumerate() {
280            write!(
281                writer,
282                "{}({})",
283                field.name.mixed_case(conv),
284                field.name.mixed_case(conv),
285            )?;
286            if index + 1 < struc.fields.len() {
287                write!(writer, ", ")?;
288            } else {
289                writeln!(writer, " {{ }}")?;
290            }
291        }
292    }
293
294    // Read
295    writeln!(
296        writer,
297        "{} {}::readFrom(InputStream& stream) {{",
298        full_name, full_name,
299    )?;
300    writer.inc_ident();
301    writeln!(writer, "{} result;", full_name)?;
302    for field in &struc.fields {
303        fn assign(
304            writer: &mut Writer,
305            to: &str,
306            schema: &Schema,
307            index_var: &mut usize,
308        ) -> std::fmt::Result {
309            match schema {
310                Schema::Bool => {
311                    writeln!(writer, "{} = stream.readBool();", to)?;
312                }
313                Schema::Int32 => {
314                    writeln!(writer, "{} = stream.readInt();", to)?;
315                }
316                Schema::Int64 => {
317                    writeln!(writer, "{} = stream.readLongLong();", to)?;
318                }
319                Schema::Float32 => {
320                    writeln!(writer, "{} = stream.readFloat();", to)?;
321                }
322                Schema::Float64 => {
323                    writeln!(writer, "{} = stream.readDouble();", to)?;
324                }
325                Schema::String => {
326                    writeln!(writer, "{} = stream.readString();", to)?;
327                }
328                Schema::Struct(Struct { name, .. })
329                | Schema::OneOf {
330                    base_name: name, ..
331                } => {
332                    writeln!(
333                        writer,
334                        "{} = {}::readFrom(stream);",
335                        to,
336                        name.camel_case(conv)
337                    )?;
338                }
339                Schema::Option(inner) => {
340                    writeln!(writer, "if (stream.readBool()) {{")?;
341                    writer.inc_ident();
342                    writeln!(
343                        writer,
344                        "{} = std::shared_ptr<{}>(new {}());",
345                        to,
346                        type_name(inner),
347                        type_name(inner)
348                    )?;
349                    assign(writer, &format!("*{}", to), inner, index_var)?;
350                    writer.dec_ident();
351                    writeln!(writer, "}} else {{")?;
352                    writeln!(
353                        writer,
354                        "    {} = std::shared_ptr<{}>();",
355                        to,
356                        type_name(inner)
357                    )?;
358                    writeln!(writer, "}}")?;
359                }
360                Schema::Vec(inner) => {
361                    writeln!(
362                        writer,
363                        "{} = std::vector<{}>(stream.readInt());",
364                        to,
365                        type_name(inner),
366                    )?;
367                    let index_var_name = index_var_name(index_var);
368                    writeln!(
369                        writer,
370                        "for (size_t {} = 0; {} < {}.size(); {}++) {{",
371                        index_var_name, index_var_name, to, index_var_name
372                    )?;
373                    writer.inc_ident();
374                    assign(
375                        writer,
376                        &format!("{}[{}]", to, index_var_name),
377                        inner,
378                        index_var,
379                    )?;
380                    writer.dec_ident();
381                    writeln!(writer, "}}")?;
382                }
383                Schema::Map(key_type, value_type) => {
384                    let to_size = format!("{}Size", var_name(to));
385                    writeln!(writer, "size_t {} = stream.readInt();", to_size)?;
386                    writeln!(
387                        writer,
388                        "{} = std::unordered_map<{}, {}>();",
389                        to,
390                        type_name(key_type),
391                        type_name(value_type)
392                    )?;
393                    writeln!(writer, "{}.reserve({});", to, to_size)?;
394                    let index_var_name = index_var_name(index_var);
395                    writeln!(
396                        writer,
397                        "for (size_t {} = 0; {} < {}; {}++) {{",
398                        index_var_name, index_var_name, to_size, index_var_name
399                    )?;
400                    writer.inc_ident();
401                    writeln!(writer, "{} {}Key;", type_name(key_type), var_name(to))?;
402                    assign(writer, &format!("{}Key", var_name(to)), key_type, index_var)?;
403                    writeln!(writer, "{} {}Value;", type_name(value_type), var_name(to))?;
404                    assign(
405                        writer,
406                        &format!("{}Value", var_name(to)),
407                        value_type,
408                        index_var,
409                    )?;
410                    writeln!(
411                        writer,
412                        "{}.emplace(std::make_pair({}Key, {}Value));",
413                        to,
414                        var_name(to),
415                        var_name(to)
416                    )?;
417                    writer.dec_ident();
418                    writeln!(writer, "}}")?;
419                }
420                Schema::Enum {
421                    base_name,
422                    variants,
423                } => {
424                    writeln!(writer, "switch (stream.readInt()) {{")?;
425                    for (discriminant, variant) in variants.iter().enumerate() {
426                        writeln!(writer, "case {}:", discriminant)?;
427                        writeln!(
428                            writer,
429                            "    {} = {}::{};",
430                            to,
431                            base_name.camel_case(conv),
432                            variant.shouty_snake_case(conv)
433                        )?;
434                        writeln!(writer, "    break;")?;
435                    }
436                    writeln!(writer, "default:")?;
437                    writeln!(
438                        writer,
439                        "    throw std::runtime_error(\"Unexpected discriminant value\");"
440                    )?;
441                    writeln!(writer, "}}")?;
442                }
443            }
444            Ok(())
445        }
446        assign(
447            writer,
448            &format!("result.{}", field.name.mixed_case(conv)),
449            &field.schema,
450            &mut 0,
451        )?;
452    }
453    writeln!(writer, "return result;")?;
454    writer.dec_ident();
455    writeln!(writer, "}}")?;
456
457    // Writing
458    writeln!(
459        writer,
460        "void {}::writeTo(OutputStream& stream) const {{",
461        full_name,
462    )?;
463    writer.inc_ident();
464    if base.is_some() {
465        writeln!(writer, "stream.write(TAG);")?;
466    }
467    if let Some(magic) = struc.magic {
468        writeln!(writer, "stream.write({});", magic)?;
469    }
470    for field in &struc.fields {
471        fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
472            match schema {
473                Schema::Bool => {
474                    writeln!(writer, "stream.write({});", value)?;
475                }
476                Schema::Int32 => {
477                    writeln!(writer, "stream.write({});", value)?;
478                }
479                Schema::Int64 => {
480                    writeln!(writer, "stream.write({});", value)?;
481                }
482                Schema::Float32 => {
483                    writeln!(writer, "stream.write({});", value)?;
484                }
485                Schema::Float64 => {
486                    writeln!(writer, "stream.write({});", value)?;
487                }
488                Schema::String => {
489                    writeln!(writer, "stream.write({});", value)?;
490                }
491                Schema::Struct(_) => {
492                    writeln!(writer, "{}.writeTo(stream);", value)?;
493                }
494                Schema::OneOf { .. } => {
495                    writeln!(writer, "{}->writeTo(stream);", value)?;
496                }
497                Schema::Option(inner) => {
498                    writeln!(writer, "if ({}) {{", value)?;
499                    writer.inc_ident();
500                    writeln!(writer, "stream.write(true);")?;
501                    write(writer, &format!("(*{})", value), inner)?;
502                    writer.dec_ident();
503                    writeln!(writer, "}} else {{")?;
504                    writeln!(writer, "    stream.write(false);")?;
505                    writeln!(writer, "}}")?;
506                }
507                Schema::Vec(inner) => {
508                    writeln!(writer, "stream.write((int)({}.size()));", value)?;
509                    writeln!(
510                        writer,
511                        "for (const {}& {}Element : {}) {{",
512                        type_name(inner),
513                        var_name(value),
514                        value
515                    )?;
516                    writer.inc_ident();
517                    write(writer, &format!("{}Element", var_name(value)), inner)?;
518                    writer.dec_ident();
519                    writeln!(writer, "}}")?;
520                }
521                Schema::Map(key_type, value_type) => {
522                    writeln!(writer, "stream.write((int)({}.size()));", value)?;
523                    writeln!(
524                        writer,
525                        "for (const auto& {}Entry : {}) {{",
526                        var_name(value),
527                        value
528                    )?;
529                    writer.inc_ident();
530                    write(writer, &format!("{}Entry.first", var_name(value)), key_type)?;
531                    write(
532                        writer,
533                        &format!("{}Entry.second", var_name(value)),
534                        value_type,
535                    )?;
536                    writer.dec_ident();
537                    writeln!(writer, "}}")?;
538                }
539                Schema::Enum { .. } => {
540                    writeln!(writer, "stream.write((int)({}));", value)?;
541                }
542            }
543            Ok(())
544        }
545        write(writer, &field.name.mixed_case(conv), &field.schema)?;
546    }
547    writer.dec_ident();
548    writeln!(writer, "}}")?;
549
550    // Eq
551    if schema.hashable() {
552        writeln!(
553            writer,
554            "bool {}::operator ==(const {}& other) const {{",
555            full_name, full_name,
556        )?;
557        write!(writer, "    return ")?;
558        for (index, field) in struc.fields.iter().enumerate() {
559            if index > 0 {
560                write!(writer, " && ")?;
561            }
562            write!(
563                writer,
564                "{} == other.{}",
565                field.name.mixed_case(conv),
566                field.name.mixed_case(conv),
567            )?;
568        }
569        writeln!(writer, ";")?;
570        writeln!(writer, "}}")?;
571    }
572
573    // Hash
574    if schema.hashable() {
575        writeln!(
576            writer,
577            "size_t std::hash<{}>::operator ()(const {}& value) const {{",
578            full_name, full_name,
579        )?;
580        writer.inc_ident();
581        writeln!(writer, "size_t result = 0;")?;
582        for field in &struc.fields {
583            writeln!(
584                writer,
585                "result ^= std::hash<{}>{{}}(value.{}) + 0x9e3779b9 + (result<<6) + (result>>2);",
586                type_name(&field.schema),
587                field.name.mixed_case(conv),
588            )?;
589        }
590        writeln!(writer, "return result;")?;
591        writer.dec_ident();
592        writeln!(writer, "}}")?;
593    }
594
595    // ToString
596    writeln!(writer, "std::string {}::toString() const {{", full_name)?;
597    writer.inc_ident();
598    writeln!(writer, "return std::string({:?}) + \"(\" +", full_name)?;
599    writer.inc_ident();
600    for field in &struc.fields {
601        match *field.schema {
602            Schema::Struct(_) => {
603                writeln!(writer, "{}.toString() +", field.name.mixed_case(conv))?;
604            }
605            Schema::OneOf { .. } => {
606                writeln!(writer, "{}->toString() +", field.name.mixed_case(conv))?;
607            }
608            Schema::Bool => {
609                writeln!(
610                    writer,
611                    "({} ? \"true\" : \"false\") + ",
612                    field.name.mixed_case(conv)
613                )?;
614            }
615            Schema::String => {
616                writeln!(writer, "{} + ", field.name.mixed_case(conv))?;
617            }
618            Schema::Int32 | Schema::Int64 | Schema::Float32 | Schema::Float64 => {
619                writeln!(writer, "std::to_string({}) +", field.name.mixed_case(conv))?;
620            }
621            _ => {
622                writeln!(writer, "\"TODO\" + ")?;
623            }
624        }
625    }
626    writeln!(writer, "\")\";")?;
627    writer.dec_ident();
628    writer.dec_ident();
629    writeln!(writer, "}}")?;
630
631    Ok(())
632}
633
634impl trans_gen_core::Generator for Generator {
635    fn new(name: &str, version: &str) -> Self {
636        let mut files = HashMap::new();
637        files.insert(
638            "Stream.hpp".to_owned(),
639            include_str!("../template/Stream.hpp").to_owned(),
640        );
641        files.insert(
642            "Stream.cpp".to_owned(),
643            include_str!("../template/Stream.cpp").to_owned(),
644        );
645        Self { files }
646    }
647    fn result(self) -> HashMap<String, String> {
648        self.files
649    }
650    fn add_only(&mut self, schema: &Schema) {
651        match schema {
652            Schema::Enum {
653                base_name,
654                variants,
655            } => {
656                let file_name = format!("model/{}.hpp", base_name.camel_case(conv));
657                let mut writer = Writer::new();
658                writeln!(
659                    writer,
660                    "#ifndef _MODEL_{}_HPP_",
661                    base_name.shouty_snake_case(conv)
662                )
663                .unwrap();
664                writeln!(
665                    writer,
666                    "#define _MODEL_{}_HPP_",
667                    base_name.shouty_snake_case(conv)
668                )
669                .unwrap();
670                writeln!(writer).unwrap();
671                writeln!(writer, "#include \"../Stream.hpp\"").unwrap();
672                writeln!(writer).unwrap();
673                writeln!(writer, "enum {} {{", base_name.camel_case(conv)).unwrap();
674                writer.inc_ident();
675                for (index, variant) in variants.iter().enumerate() {
676                    writeln!(
677                        writer,
678                        "{} = {}{}",
679                        variant.shouty_snake_case(conv),
680                        index,
681                        if index + 1 < variants.len() { "," } else { "" }
682                    )
683                    .unwrap();
684                }
685                writer.dec_ident();
686                writeln!(writer, "}};").unwrap();
687                writeln!(writer).unwrap();
688                writeln!(writer, "#endif").unwrap();
689                self.files.insert(file_name, writer.get());
690            }
691            Schema::Struct(struc) => {
692                let file_name = format!("model/{}.hpp", struc.name.camel_case(conv));
693                let mut writer = Writer::new();
694                writeln!(
695                    writer,
696                    "#ifndef _MODEL_{}_HPP_",
697                    struc.name.shouty_snake_case(conv)
698                )
699                .unwrap();
700                writeln!(
701                    writer,
702                    "#define _MODEL_{}_HPP_",
703                    struc.name.shouty_snake_case(conv)
704                )
705                .unwrap();
706                writeln!(writer).unwrap();
707                write_includes(&mut writer, schema, false).unwrap();
708                writeln!(writer).unwrap();
709                write_struct_def(&mut writer, schema, struc, None).unwrap();
710                writeln!(writer).unwrap();
711                writeln!(writer, "#endif").unwrap();
712                self.files.insert(file_name, writer.get());
713
714                let file_name = format!("model/{}.cpp", struc.name.camel_case(conv));
715                let mut writer = Writer::new();
716                writeln!(writer, "#include \"{}.hpp\"", struc.name.camel_case(conv)).unwrap();
717                writeln!(writer).unwrap();
718                write_struct_impl(&mut writer, schema, struc, None).unwrap();
719                self.files.insert(file_name, writer.get());
720            }
721            Schema::OneOf {
722                base_name,
723                variants,
724            } => {
725                let file_name = format!("model/{}.hpp", base_name.camel_case(conv));
726                let mut writer = Writer::new();
727                writeln!(
728                    writer,
729                    "#ifndef _MODEL_{}_HPP_",
730                    base_name.shouty_snake_case(conv)
731                )
732                .unwrap();
733                writeln!(
734                    writer,
735                    "#define _MODEL_{}_HPP_",
736                    base_name.shouty_snake_case(conv)
737                )
738                .unwrap();
739                writeln!(writer).unwrap();
740                writeln!(writer, "#include <memory>").unwrap();
741                write_includes(&mut writer, schema, false).unwrap();
742                writeln!(writer).unwrap();
743                writeln!(writer, "class {} {{", base_name.camel_case(conv)).unwrap();
744                writeln!(writer, "public:").unwrap();
745                writer.inc_ident();
746                for variant in variants {
747                    writeln!(writer, "class {};", variant.name.camel_case(conv)).unwrap();
748                }
749                writeln!(writer).unwrap();
750                writeln!(
751                    writer,
752                    "static std::shared_ptr<{}> readFrom(InputStream& stream);",
753                    base_name.camel_case(conv)
754                )
755                .unwrap();
756                writeln!(
757                    writer,
758                    "virtual void writeTo(OutputStream& stream) const = 0;",
759                )
760                .unwrap();
761                writeln!(writer, "virtual std::string toString() const = 0;").unwrap();
762                writer.dec_ident();
763                writeln!(writer, "}};").unwrap();
764                for (discriminant, variant) in variants.iter().enumerate() {
765                    writeln!(writer).unwrap();
766                    write_struct_def(
767                        &mut writer,
768                        schema,
769                        variant,
770                        Some((base_name, discriminant)),
771                    )
772                    .unwrap();
773                }
774                writeln!(writer).unwrap();
775                writeln!(writer, "#endif").unwrap();
776                self.files.insert(file_name, writer.get());
777
778                let file_name = format!("model/{}.cpp", base_name.camel_case(conv));
779                let mut writer = Writer::new();
780                writeln!(writer, "#include \"{}.hpp\"", base_name.camel_case(conv)).unwrap();
781                writeln!(writer, "#include <stdexcept>").unwrap();
782                for (discriminant, variant) in variants.iter().enumerate() {
783                    writeln!(writer).unwrap();
784                    write_struct_impl(
785                        &mut writer,
786                        schema,
787                        variant,
788                        Some((base_name, discriminant)),
789                    )
790                    .unwrap();
791                }
792
793                // Reading
794                writeln!(
795                    writer,
796                    "std::shared_ptr<{}> {}::readFrom(InputStream& stream) {{",
797                    base_name.camel_case(conv),
798                    base_name.camel_case(conv),
799                )
800                .unwrap();
801                writer.inc_ident();
802                writeln!(writer, "switch (stream.readInt()) {{").unwrap();
803                for (discriminant, variant) in variants.iter().enumerate() {
804                    writeln!(writer, "case {}:", discriminant).unwrap();
805                    let variant_name = format!(
806                        "{}::{}",
807                        base_name.camel_case(conv),
808                        variant.name.camel_case(conv)
809                    );
810                    writeln!(
811                        writer,
812                        "    return std::shared_ptr<{}>(new {}({}::readFrom(stream)));",
813                        variant_name, variant_name, variant_name,
814                    )
815                    .unwrap();
816                }
817                writeln!(writer, "default:").unwrap();
818                writeln!(
819                    writer,
820                    "    throw std::runtime_error(\"Unexpected discriminant value\");"
821                )
822                .unwrap();
823                writeln!(writer, "}}").unwrap();
824                writer.dec_ident();
825                writeln!(writer, "}};").unwrap();
826
827                self.files.insert(file_name, writer.get());
828            }
829            Schema::Bool
830            | Schema::Int32
831            | Schema::Int64
832            | Schema::Float32
833            | Schema::Float64
834            | Schema::String
835            | Schema::Option(_)
836            | Schema::Vec(_)
837            | Schema::Map(_, _) => {}
838        }
839    }
840}