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