trans_gen/gens/csharp/
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        .replace("Params", "Parameters")
9}
10
11pub struct Generator {
12    main_namespace: String,
13    files: HashMap<String, String>,
14}
15
16fn is_null(value: &str, schema: &Schema) -> String {
17    if nullable(schema) {
18        format!("{} == null", value)
19    } else {
20        format!("!{}.HasValue", value)
21    }
22}
23
24fn option_unwrap(name: &str, schema: &Schema) -> String {
25    if nullable(schema) {
26        name.to_owned()
27    } else {
28        format!("{}.Value", name)
29    }
30}
31
32fn nullable(schema: &Schema) -> bool {
33    match schema {
34        Schema::Bool
35        | Schema::Int32
36        | Schema::Int64
37        | Schema::Float32
38        | Schema::Float64
39        | Schema::Enum { .. }
40        | Schema::Struct(_) => false,
41        Schema::String
42        | Schema::Option(_)
43        | Schema::Vec(_)
44        | Schema::Map(_, _)
45        | Schema::OneOf { .. } => true,
46    }
47}
48
49fn type_name(schema: &Schema) -> String {
50    format!("{}{}", type_name_prearray(schema), type_post_array(schema))
51}
52
53fn type_name_prearray(schema: &Schema) -> String {
54    match schema {
55        Schema::Bool => "bool".to_owned(),
56        Schema::Int32 => "int".to_owned(),
57        Schema::Int64 => "long".to_owned(),
58        Schema::Float32 => "float".to_owned(),
59        Schema::Float64 => "double".to_owned(),
60        Schema::String => "string".to_owned(),
61        Schema::Struct(Struct { name, .. })
62        | Schema::OneOf {
63            base_name: name, ..
64        }
65        | Schema::Enum {
66            base_name: name, ..
67        } => format!("Model.{}", name.camel_case(conv)),
68        Schema::Option(inner) => {
69            if nullable(inner) {
70                type_name(inner)
71            } else {
72                format!("{}?", type_name(inner))
73            }
74        }
75        Schema::Vec(inner) => type_name_prearray(inner),
76        Schema::Map(key, value) => format!(
77            "System.Collections.Generic.IDictionary<{}, {}>",
78            type_name(key),
79            type_name(value)
80        ),
81    }
82}
83
84fn type_post_array(schema: &Schema) -> String {
85    match schema {
86        Schema::Vec(inner) => format!("[]{}", type_post_array(inner)),
87        _ => String::new(),
88    }
89}
90
91fn index_var_name(index_var: &mut usize) -> String {
92    let result = "ijk".chars().nth(*index_var).unwrap();
93    *index_var += 1;
94    result.to_string()
95}
96
97fn var_name(name: &str) -> &str {
98    match name.rfind('.') {
99        Some(index) => &name[(index + 1)..],
100        None => name,
101    }
102}
103
104fn write_struct(
105    writer: &mut Writer,
106    struc: &Struct,
107    base: Option<(&Name, usize)>,
108) -> std::fmt::Result {
109    // Class
110    if let Some((base_name, _)) = base {
111        writeln!(
112            writer,
113            "public class {} : {}",
114            struc.name.camel_case(conv),
115            base_name.camel_case(conv)
116        )?;
117    } else {
118        writeln!(writer, "public struct {}", struc.name.camel_case(conv))?;
119    }
120    writeln!(writer, "{{")?;
121    writer.inc_ident();
122    if let Some((_, tag)) = base {
123        writeln!(writer, "public const int TAG = {};", tag)?;
124    }
125
126    // Fields
127    for field in &struc.fields {
128        writeln!(
129            writer,
130            "public {} {} {{ get; set; }}",
131            type_name(&field.schema),
132            field.name.camel_case(conv)
133        )?;
134    }
135
136    // Constructor
137    if base.is_some() {
138        writeln!(writer, "public {}() {{}}", struc.name.camel_case(conv))?;
139    }
140    if !struc.fields.is_empty() {
141        write!(writer, "public {}(", struc.name.camel_case(conv))?;
142        for (index, field) in struc.fields.iter().enumerate() {
143            if index > 0 {
144                write!(writer, ", ")?;
145            }
146            write!(
147                writer,
148                "{} {}",
149                type_name(&field.schema),
150                field.name.mixed_case(conv)
151            )?;
152        }
153        writeln!(writer, ")")?;
154        writeln!(writer, "{{")?;
155        for field in &struc.fields {
156            writeln!(
157                writer,
158                "    this.{} = {};",
159                field.name.camel_case(conv),
160                field.name.mixed_case(conv)
161            )?;
162        }
163        writeln!(writer, "}}")?;
164    }
165
166    // Reading
167    writeln!(
168        writer,
169        "public static {}{} ReadFrom(System.IO.BinaryReader reader)",
170        if base.is_some() { "new " } else { "" },
171        struc.name.camel_case(conv)
172    )?;
173    writeln!(writer, "{{")?;
174    writer.inc_ident();
175    writeln!(
176        writer,
177        "var result = new {}();",
178        struc.name.camel_case(conv)
179    )?;
180    for field in &struc.fields {
181        fn assign(
182            writer: &mut Writer,
183            to: &str,
184            schema: &Schema,
185            index_var: &mut usize,
186        ) -> std::fmt::Result {
187            match schema {
188                Schema::Bool => {
189                    writeln!(writer, "{} = reader.ReadBoolean();", to)?;
190                }
191                Schema::Int32 => {
192                    writeln!(writer, "{} = reader.ReadInt32();", to)?;
193                }
194                Schema::Int64 => {
195                    writeln!(writer, "{} = reader.ReadInt64();", to)?;
196                }
197                Schema::Float32 => {
198                    writeln!(writer, "{} = reader.ReadSingle();", to)?;
199                }
200                Schema::Float64 => {
201                    writeln!(writer, "{} = reader.ReadDouble();", to)?;
202                }
203                Schema::String => {
204                    writeln!(writer, "{} = System.Text.Encoding.UTF8.GetString(reader.ReadBytes(reader.ReadInt32()));", to)?;
205                }
206                Schema::Struct(Struct { name, .. })
207                | Schema::OneOf {
208                    base_name: name, ..
209                } => {
210                    writeln!(
211                        writer,
212                        "{} = Model.{}.ReadFrom(reader);",
213                        to,
214                        name.camel_case(conv)
215                    )?;
216                }
217                Schema::Option(inner) => {
218                    writeln!(writer, "if (reader.ReadBoolean())")?;
219                    writeln!(writer, "{{")?;
220                    writer.inc_ident();
221                    assign(writer, to, inner, index_var)?;
222                    writer.dec_ident();
223                    writeln!(writer, "}} else")?;
224                    writeln!(writer, "{{")?;
225                    writeln!(writer, "    {} = null;", to)?;
226                    writeln!(writer, "}}")?;
227                }
228                Schema::Vec(inner) => {
229                    writeln!(
230                        writer,
231                        "{} = new {}[reader.ReadInt32()]{};",
232                        to,
233                        type_name_prearray(inner),
234                        type_post_array(inner)
235                    )?;
236                    let index_var_name = index_var_name(index_var);
237                    writeln!(
238                        writer,
239                        "for (int {} = 0; {} < {}.Length; {}++)",
240                        index_var_name, index_var_name, to, index_var_name
241                    )?;
242                    writeln!(writer, "{{")?;
243                    writer.inc_ident();
244                    assign(
245                        writer,
246                        &format!("{}[{}]", to, index_var_name),
247                        inner,
248                        index_var,
249                    )?;
250                    writer.dec_ident();
251                    writeln!(writer, "}}")?;
252                }
253                Schema::Map(key_type, value_type) => {
254                    let to_size = format!("{}Size", var_name(to));
255                    writeln!(writer, "int {} = reader.ReadInt32();", to_size)?;
256                    writeln!(
257                        writer,
258                        "{} = new System.Collections.Generic.Dictionary<{}, {}>({});",
259                        to,
260                        type_name(key_type),
261                        type_name(value_type),
262                        to_size
263                    )?;
264                    let index_var_name = index_var_name(index_var);
265                    writeln!(
266                        writer,
267                        "for (int {} = 0; {} < {}; {}++)",
268                        index_var_name, index_var_name, to_size, index_var_name
269                    )?;
270                    writeln!(writer, "{{")?;
271                    writer.inc_ident();
272                    writeln!(writer, "{} {}Key;", type_name(key_type), var_name(to))?;
273                    assign(writer, &format!("{}Key", var_name(to)), key_type, index_var)?;
274                    writeln!(writer, "{} {}Value;", type_name(value_type), var_name(to))?;
275                    assign(
276                        writer,
277                        &format!("{}Value", var_name(to)),
278                        value_type,
279                        index_var,
280                    )?;
281                    writeln!(
282                        writer,
283                        "{}.Add({}Key, {}Value);",
284                        to,
285                        var_name(to),
286                        var_name(to)
287                    )?;
288                    writer.dec_ident();
289                    writeln!(writer, "}}")?;
290                }
291                Schema::Enum {
292                    documentation: _,
293                    base_name,
294                    variants,
295                } => {
296                    writeln!(writer, "switch (reader.ReadInt32())")?;
297                    writeln!(writer, "{{")?;
298                    for (tag, variant) in variants.iter().enumerate() {
299                        writeln!(writer, "case {}:", tag)?;
300                        writeln!(
301                            writer,
302                            "    {} = Model.{}.{};",
303                            to,
304                            base_name.camel_case(conv),
305                            variant.name.camel_case(conv)
306                        )?;
307                        writeln!(writer, "    break;")?;
308                    }
309                    writeln!(writer, "default:")?;
310                    writeln!(
311                        writer,
312                        "    throw new System.Exception(\"Unexpected tag value\");"
313                    )?;
314                    writeln!(writer, "}}")?;
315                }
316            }
317            Ok(())
318        }
319        assign(
320            writer,
321            &format!("result.{}", field.name.camel_case(conv)),
322            &field.schema,
323            &mut 0,
324        )?;
325    }
326    writeln!(writer, "return result;")?;
327    writer.dec_ident();
328    writeln!(writer, "}}")?;
329
330    // Writing
331    writeln!(
332        writer,
333        "public {}void WriteTo(System.IO.BinaryWriter writer)",
334        if base.is_some() { "override " } else { "" }
335    )?;
336    writeln!(writer, "{{")?;
337    writer.inc_ident();
338    if base.is_some() {
339        writeln!(writer, "writer.Write(TAG);")?;
340    }
341    if let Some(magic) = struc.magic {
342        writeln!(writer, "writer.Write({});", magic)?;
343    }
344    for field in &struc.fields {
345        fn write(writer: &mut Writer, value: &str, schema: &Schema) -> std::fmt::Result {
346            match schema {
347                Schema::Bool => {
348                    writeln!(writer, "writer.Write({});", value)?;
349                }
350                Schema::Int32 => {
351                    writeln!(writer, "writer.Write({});", value)?;
352                }
353                Schema::Int64 => {
354                    writeln!(writer, "writer.Write({});", value)?;
355                }
356                Schema::Float32 => {
357                    writeln!(writer, "writer.Write({});", value)?;
358                }
359                Schema::Float64 => {
360                    writeln!(writer, "writer.Write({});", value)?;
361                }
362                Schema::String => {
363                    let data_var = format!("{}Data", var_name(value));
364                    writeln!(
365                        writer,
366                        "var {} = System.Text.Encoding.UTF8.GetBytes({});",
367                        data_var, value
368                    )?;
369                    writeln!(writer, "writer.Write({}.Length);", data_var)?;
370                    writeln!(writer, "writer.Write({});", data_var)?;
371                }
372                Schema::Struct(_) | Schema::OneOf { .. } => {
373                    writeln!(writer, "{}.WriteTo(writer);", value)?;
374                }
375                Schema::Option(inner) => {
376                    writeln!(writer, "if ({})", is_null(value, inner))?;
377                    writeln!(writer, "{{")?;
378                    writeln!(writer, "    writer.Write(false);")?;
379                    writeln!(writer, "}} else")?;
380                    writeln!(writer, "{{")?;
381                    writer.inc_ident();
382                    writeln!(writer, "writer.Write(true);")?;
383                    write(writer, &option_unwrap(value, inner), inner)?;
384                    writer.dec_ident();
385                    writeln!(writer, "}}")?;
386                }
387                Schema::Vec(inner) => {
388                    writeln!(writer, "writer.Write({}.Length);", value)?;
389                    writeln!(
390                        writer,
391                        "foreach (var {}Element in {})",
392                        var_name(value),
393                        value
394                    )?;
395                    writeln!(writer, "{{")?;
396                    writer.inc_ident();
397                    write(writer, &format!("{}Element", var_name(value)), inner)?;
398                    writer.dec_ident();
399                    writeln!(writer, "}}")?;
400                }
401                Schema::Map(key_type, value_type) => {
402                    writeln!(writer, "writer.Write({}.Count);", value)?;
403                    writeln!(
404                        writer,
405                        "foreach (var {}Entry in {})",
406                        var_name(value),
407                        value
408                    )?;
409                    writeln!(writer, "{{")?;
410                    writer.inc_ident();
411                    writeln!(
412                        writer,
413                        "var {}Key = {}Entry.Key;",
414                        var_name(value),
415                        var_name(value)
416                    )?;
417                    writeln!(
418                        writer,
419                        "var {}Value = {}Entry.Value;",
420                        var_name(value),
421                        var_name(value)
422                    )?;
423                    write(writer, &format!("{}Key", var_name(value)), key_type)?;
424                    write(writer, &format!("{}Value", var_name(value)), value_type)?;
425                    writer.dec_ident();
426                    writeln!(writer, "}}")?;
427                }
428                Schema::Enum { .. } => {
429                    writeln!(writer, "writer.Write((int) ({}));", value)?;
430                }
431            }
432            Ok(())
433        }
434        write(writer, &field.name.camel_case(conv), &field.schema)?;
435    }
436    writer.dec_ident();
437    writeln!(writer, "}}")?;
438    writer.dec_ident();
439    writeln!(writer, "}}")?;
440    Ok(())
441}
442
443impl crate::Generator for Generator {
444    type Options = ();
445    fn new(name: &str, _version: &str, _: ()) -> Self {
446        Self {
447            main_namespace: Name::new(name.to_owned()).camel_case(conv),
448            files: HashMap::new(),
449        }
450    }
451    fn result(self) -> GenResult {
452        self.files.into()
453    }
454    fn add_only(&mut self, schema: &Schema) {
455        match schema {
456            Schema::Enum {
457                documentation: _,
458                base_name,
459                variants,
460            } => {
461                let file_name = format!("Model/{}.cs", base_name.camel_case(conv));
462                let mut writer = Writer::new();
463                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
464                writeln!(writer, "{{").unwrap();
465                writer.inc_ident();
466                writeln!(writer, "public enum {}", base_name.camel_case(conv)).unwrap();
467                writeln!(writer, "{{").unwrap();
468                writer.inc_ident();
469                for (tag, variant) in variants.iter().enumerate() {
470                    writeln!(writer, "{} = {},", variant.name.camel_case(conv), tag,).unwrap();
471                }
472                writer.dec_ident();
473                writeln!(writer, "}}").unwrap();
474                writer.dec_ident();
475                writeln!(writer, "}}").unwrap();
476                self.files.insert(file_name, writer.get());
477            }
478            Schema::Struct(struc) => {
479                let file_name = format!("Model/{}.cs", struc.name.camel_case(conv));
480                let mut writer = Writer::new();
481                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
482                writeln!(writer, "{{").unwrap();
483                writer.inc_ident();
484                write_struct(&mut writer, struc, None).unwrap();
485                writer.dec_ident();
486                writeln!(writer, "}}").unwrap();
487                self.files.insert(file_name, writer.get());
488            }
489            Schema::OneOf {
490                documentation: _,
491                base_name,
492                variants,
493            } => {
494                let file_name = format!("Model/{}.cs", base_name.camel_case(conv));
495                let mut writer = Writer::new();
496                writeln!(writer, "namespace {}.Model", self.main_namespace).unwrap();
497                writeln!(writer, "{{").unwrap();
498                writer.inc_ident();
499                writeln!(
500                    &mut writer,
501                    "public abstract class {}",
502                    base_name.camel_case(conv)
503                )
504                .unwrap();
505                writeln!(writer, "{{").unwrap();
506                {
507                    writer.inc_ident();
508                    writeln!(
509                        &mut writer,
510                        "public abstract void WriteTo(System.IO.BinaryWriter writer);"
511                    )
512                    .unwrap();
513                    writeln!(
514                        &mut writer,
515                        "public static {} ReadFrom(System.IO.BinaryReader reader)",
516                        base_name.camel_case(conv)
517                    )
518                    .unwrap();
519                    writeln!(writer, "{{").unwrap();
520                    {
521                        writer.inc_ident();
522                        writeln!(&mut writer, "switch (reader.ReadInt32())").unwrap();
523                        writeln!(writer, "{{").unwrap();
524                        writer.inc_ident();
525                        for variant in variants {
526                            writeln!(&mut writer, "case {}.TAG:", variant.name.camel_case(conv))
527                                .unwrap();
528                            writeln!(
529                                &mut writer,
530                                "    return {}.ReadFrom(reader);",
531                                variant.name.camel_case(conv)
532                            )
533                            .unwrap();
534                        }
535                        writeln!(&mut writer, "default:").unwrap();
536                        writeln!(
537                            &mut writer,
538                            "    throw new System.Exception(\"Unexpected tag value\");"
539                        )
540                        .unwrap();
541                        writer.dec_ident();
542                        writeln!(&mut writer, "}}").unwrap();
543                        writer.dec_ident();
544                    }
545                    writeln!(&mut writer, "}}").unwrap();
546                    for (tag, variant) in variants.iter().enumerate() {
547                        writeln!(&mut writer).unwrap();
548                        write_struct(&mut writer, variant, Some((base_name, tag))).unwrap();
549                    }
550                    writer.dec_ident();
551                }
552                writeln!(&mut writer, "}}").unwrap();
553                writer.dec_ident();
554                writeln!(writer, "}}").unwrap();
555                self.files.insert(file_name, writer.get());
556            }
557            Schema::Bool
558            | Schema::Int32
559            | Schema::Int64
560            | Schema::Float32
561            | Schema::Float64
562            | Schema::String
563            | Schema::Option(_)
564            | Schema::Vec(_)
565            | Schema::Map(_, _) => {}
566        }
567    }
568}