Skip to main content

serde_generate/
csharp.rs

1// Copyright (c) Facebook, Inc. and its affiliates
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4use crate::{
5    common,
6    indent::{IndentConfig, IndentedWriter},
7    CodeGeneratorConfig, Encoding,
8};
9use heck::CamelCase;
10use include_dir::include_dir as include_directory;
11use serde_reflection::{ContainerFormat, Format, FormatHolder, Named, Registry, VariantFormat};
12use std::{
13    collections::{BTreeMap, HashMap},
14    fmt::Write as _,
15    io::{Result, Write},
16    path::PathBuf,
17};
18
19/// Main configuration object for code-generation in C#.
20pub struct CodeGenerator<'a> {
21    /// Language-independent configuration.
22    config: &'a CodeGeneratorConfig,
23    /// Mapping from external type names to fully-qualified class names (e.g. "MyClass" -> "MyNamespace.MyClass").
24    /// Derived from `config.external_definitions`.
25    external_qualified_names: HashMap<String, String>,
26}
27
28/// Shared state for the code generation of a C# source file.
29struct CSharpEmitter<'a, T> {
30    /// Writer.
31    out: IndentedWriter<T>,
32    /// Generator.
33    generator: &'a CodeGenerator<'a>,
34    /// Current namespace (e.g. vec!["MyNamespace", "MyClass"])
35    current_namespace: Vec<String>,
36    /// Current (non-qualified) generated class names that could clash with names in the registry
37    /// (e.g. "Builder" or variant classes).
38    /// * We count multiplicities to allow inplace backtracking.
39    /// * Names in the registry (and a few base types such as "Decimal") are assumed to never clash.
40    current_reserved_names: HashMap<String, usize>,
41    /// When we find an enum with all Unit variants, we ser/de as a regular C# enum.
42    /// We keep track of this so we can use the enum's extension class for ser/de since enums can't have methods.
43    cstyle_enum_names: Vec<String>,
44}
45
46impl<'a> CodeGenerator<'a> {
47    /// Create a C# code generator for the given config.
48    pub fn new(config: &'a CodeGeneratorConfig) -> Self {
49        let mut external_qualified_names = HashMap::new();
50        for (namespace, names) in &config.external_definitions {
51            for name in names {
52                external_qualified_names.insert(name.to_string(), format!("{namespace}.{name}"));
53            }
54        }
55        Self {
56            config,
57            external_qualified_names,
58        }
59    }
60
61    /// Output class definitions for `registry` in separate source files.
62    /// Source files will be created in a subdirectory of `install_dir` corresponding to the given
63    /// package name (if any, otherwise `install_dir` itself).
64    pub fn write_source_files(
65        &self,
66        install_dir: std::path::PathBuf,
67        registry: &Registry,
68    ) -> Result<std::path::PathBuf> {
69        let current_namespace = self
70            .config
71            .module_name
72            .split('.')
73            .map(String::from)
74            .collect::<Vec<_>>();
75
76        let mut dir_path = install_dir;
77        for part in &current_namespace {
78            dir_path = dir_path.join(part);
79        }
80        std::fs::create_dir_all(&dir_path)?;
81
82        // When we find an enum with all Unit variants, we ser/de as a regular C# enum.
83        // We keep track of this so we can use the enum's extension class for ser/de since enums can't have methods.
84        let mut cstyle_enum_names = Vec::new();
85        if self.config.enums.c_style {
86            for (name, format) in registry {
87                if let ContainerFormat::Enum(variants) = format {
88                    if variants.values().all(|f| f.value == VariantFormat::Unit) {
89                        cstyle_enum_names.push(name.clone());
90                    }
91                }
92            }
93        }
94
95        for (name, format) in registry {
96            self.write_container_class(
97                &dir_path,
98                current_namespace.clone(),
99                cstyle_enum_names.clone(),
100                name,
101                format,
102            )?;
103        }
104        if self.config.serialization {
105            self.write_helper_class(&dir_path, current_namespace, cstyle_enum_names, registry)?;
106        }
107        Ok(dir_path)
108    }
109
110    fn write_container_class(
111        &self,
112        dir_path: &std::path::Path,
113        current_namespace: Vec<String>,
114        cstyle_enum_names: Vec<String>,
115        name: &str,
116        format: &ContainerFormat,
117    ) -> Result<()> {
118        let mut file = std::fs::File::create(dir_path.join(name.to_string() + ".cs"))?;
119        let mut emitter = CSharpEmitter {
120            out: IndentedWriter::new(&mut file, IndentConfig::Space(4)),
121            generator: self,
122            current_namespace,
123            current_reserved_names: HashMap::new(),
124            cstyle_enum_names,
125        };
126
127        emitter.output_preamble()?;
128        emitter.output_open_namespace()?;
129        emitter.output_container(name, format)?;
130        emitter.output_close_namespace()?;
131
132        Ok(())
133    }
134
135    fn write_helper_class(
136        &self,
137        dir_path: &std::path::Path,
138        current_namespace: Vec<String>,
139        cstyle_enum_names: Vec<String>,
140        registry: &Registry,
141    ) -> Result<()> {
142        let mut file = std::fs::File::create(dir_path.join("TraitHelpers.cs"))?;
143        let mut emitter = CSharpEmitter {
144            out: IndentedWriter::new(&mut file, IndentConfig::Space(4)),
145            generator: self,
146            current_namespace,
147            current_reserved_names: HashMap::new(),
148            cstyle_enum_names,
149        };
150
151        emitter.output_preamble()?;
152        emitter.output_open_namespace()?;
153        emitter.output_trait_helpers(registry)?;
154        emitter.output_close_namespace()?;
155
156        Ok(())
157    }
158}
159
160impl<'a, T> CSharpEmitter<'a, T>
161where
162    T: Write,
163{
164    fn output_preamble(&mut self) -> Result<()> {
165        writeln!(
166            self.out,
167            r"using System;
168using System.Collections.Generic;
169using System.IO;
170using System.Linq;
171using System.Text;
172using System.Numerics;"
173        )?;
174        Ok(())
175    }
176
177    fn output_open_namespace(&mut self) -> Result<()> {
178        writeln!(
179            self.out,
180            "\nnamespace {} {{",
181            self.generator.config.module_name
182        )?;
183        self.out.indent();
184        Ok(())
185    }
186
187    fn output_close_namespace(&mut self) -> Result<()> {
188        self.out.unindent();
189        writeln!(
190            self.out,
191            "\n}} // end of namespace {}",
192            self.generator.config.module_name
193        )?;
194        Ok(())
195    }
196
197    /// Compute a safe reference to the registry type `name` in the given context.
198    /// If `name` is not marked as "reserved" (e.g. "Builder"), we compare the global
199    /// name `self.external_qualified_names[name]` with the current namespace and try to use the
200    /// short string `name` if possible.
201    fn quote_qualified_name(&self, name: &str) -> String {
202        let qname = self
203            .generator
204            .external_qualified_names
205            .get(name)
206            .cloned()
207            .unwrap_or_else(|| format!("{}.{}", self.generator.config.module_name, name));
208        let mut path = qname.split('.').collect::<Vec<_>>();
209        if path.len() <= 1 {
210            return qname;
211        }
212        let name = path.pop().unwrap();
213        if self.current_reserved_names.contains_key(name) {
214            return qname;
215        }
216        for (index, element) in path.iter().enumerate() {
217            match self.current_namespace.get(index) {
218                Some(e) if e == element => (),
219                _ => {
220                    return qname;
221                }
222            }
223        }
224        name.to_string()
225    }
226
227    fn output_comment(&mut self, name: &str) -> std::io::Result<()> {
228        let mut path = self.current_namespace.clone();
229        path.push(name.to_string());
230        if let Some(doc) = self.generator.config.comments.get(&path) {
231            let text = textwrap::indent(doc, "/// ").replace("\n\n", "\n///\n");
232            write!(self.out, "{text}")?;
233        }
234        Ok(())
235    }
236
237    fn output_custom_code(&mut self) -> std::io::Result<()> {
238        if let Some(code) = self
239            .generator
240            .config
241            .custom_code
242            .get(&self.current_namespace)
243        {
244            writeln!(self.out, "\n{code}")?;
245        }
246        Ok(())
247    }
248
249    fn is_nullable(&self, format: &Format) -> bool {
250        use Format::*;
251        match format {
252            TypeName(name) => !self.cstyle_enum_names.contains(name),
253            Str | Seq(_) | Map { .. } | TupleArray { .. } => true,
254            Variable(_) => panic!("unexpected value"),
255            _ => false,
256        }
257    }
258
259    fn quote_type(&self, format: &Format) -> String {
260        use Format::*;
261        match format {
262            TypeName(x) => self.quote_qualified_name(x),
263            Unit => "Serde.Unit".into(),
264            Bool => "bool".into(),
265            I8 => "sbyte".into(),
266            I16 => "short".into(),
267            I32 => "int".into(),
268            I64 => "long".into(),
269            I128 => "BigInteger".into(),
270            U8 => "byte".into(),
271            U16 => "ushort".into(),
272            U32 => "uint".into(),
273            U64 => "ulong".into(),
274            U128 => "BigInteger".into(),
275            F32 => "float".into(),
276            F64 => "double".into(),
277            Char => "char".into(),
278            Str => "string".into(),
279            Bytes => "Serde.ValueArray<byte>".into(),
280
281            Option(format) => format!("Serde.Option<{}>", self.quote_type(format)),
282            Seq(format) => format!("Serde.ValueArray<{}>", self.quote_type(format)),
283            Map { key, value } => format!(
284                "Serde.ValueDictionary<{}, {}>",
285                self.quote_type(key),
286                self.quote_type(value)
287            ),
288            Tuple(formats) => format!("({})", self.quote_types(formats)),
289            TupleArray {
290                content,
291                size: _size,
292            } => format!("Serde.ValueArray<{}>", self.quote_type(content),),
293            Variable(_) => panic!("unexpected value"),
294        }
295    }
296
297    fn enter_class(&mut self, name: &str, reserved_subclass_names: &[&str]) {
298        self.out.indent();
299        self.current_namespace.push(name.to_string());
300        for name in reserved_subclass_names {
301            let entry = self
302                .current_reserved_names
303                .entry(name.to_string())
304                .or_insert(0);
305            *entry += 1;
306        }
307    }
308
309    fn leave_class(&mut self, reserved_subclass_names: &[&str]) {
310        self.out.unindent();
311        self.current_namespace.pop();
312        for name in reserved_subclass_names {
313            let entry = self.current_reserved_names.get_mut(*name).unwrap();
314            *entry -= 1;
315            if *entry == 0 {
316                self.current_reserved_names.remove(*name);
317            }
318        }
319    }
320
321    fn quote_types(&self, formats: &[Format]) -> String {
322        formats
323            .iter()
324            .map(|f| self.quote_type(f))
325            .collect::<Vec<_>>()
326            .join(", ")
327    }
328
329    fn output_trait_helpers(&mut self, registry: &Registry) -> Result<()> {
330        let mut subtypes = BTreeMap::new();
331        for format in registry.values() {
332            format
333                .visit(&mut |f| {
334                    if Self::needs_helper(f) {
335                        subtypes.insert(common::mangle_type(f), f.clone());
336                    }
337                    Ok(())
338                })
339                .unwrap();
340        }
341        writeln!(self.out, "static class TraitHelpers {{")?;
342        let reserved_names = &[];
343        self.enter_class("TraitHelpers", reserved_names);
344        for (mangled_name, subtype) in &subtypes {
345            self.output_serialization_helper(mangled_name, subtype)?;
346            self.output_deserialization_helper(mangled_name, subtype)?;
347        }
348        self.leave_class(reserved_names);
349        writeln!(self.out, "}}\n")
350    }
351
352    fn needs_helper(format: &Format) -> bool {
353        use Format::*;
354        matches!(
355            format,
356            Option(_) | Seq(_) | Map { .. } | Tuple(_) | TupleArray { .. }
357        )
358    }
359
360    fn quote_serialize_value(&self, value: &str, format: &Format) -> String {
361        use Format::*;
362        match format {
363            TypeName(_) => format!("{value}.Serialize(serializer);"),
364            Unit => format!("serializer.serialize_unit({value});"),
365            Bool => format!("serializer.serialize_bool({value});"),
366            I8 => format!("serializer.serialize_i8({value});"),
367            I16 => format!("serializer.serialize_i16({value});"),
368            I32 => format!("serializer.serialize_i32({value});"),
369            I64 => format!("serializer.serialize_i64({value});"),
370            I128 => format!("serializer.serialize_i128({value});"),
371            U8 => format!("serializer.serialize_u8({value});"),
372            U16 => format!("serializer.serialize_u16({value});"),
373            U32 => format!("serializer.serialize_u32({value});"),
374            U64 => format!("serializer.serialize_u64({value});"),
375            U128 => format!("serializer.serialize_u128({value});"),
376            F32 => format!("serializer.serialize_f32({value});"),
377            F64 => format!("serializer.serialize_f64({value});"),
378            Char => format!("serializer.serialize_char({value});"),
379            Str => format!("serializer.serialize_str({value});"),
380            Bytes => format!("serializer.serialize_bytes({value});"),
381            _ => format!(
382                "{}.serialize_{}({}, serializer);",
383                self.quote_qualified_name("TraitHelpers"),
384                common::mangle_type(format),
385                value
386            ),
387        }
388    }
389
390    fn quote_deserialize(&self, format: &Format) -> String {
391        use Format::*;
392        match format {
393            TypeName(name) => {
394                if self.cstyle_enum_names.contains(name) {
395                    let extensions_name = format!("{}Extensions", name.to_camel_case());
396                    format!(
397                        "{}.Deserialize(deserializer)",
398                        self.quote_qualified_name(&extensions_name)
399                    )
400                } else {
401                    format!(
402                        "{}.Deserialize(deserializer)",
403                        self.quote_qualified_name(name)
404                    )
405                }
406            }
407            Unit => "deserializer.deserialize_unit()".to_string(),
408            Bool => "deserializer.deserialize_bool()".to_string(),
409            I8 => "deserializer.deserialize_i8()".to_string(),
410            I16 => "deserializer.deserialize_i16()".to_string(),
411            I32 => "deserializer.deserialize_i32()".to_string(),
412            I64 => "deserializer.deserialize_i64()".to_string(),
413            I128 => "deserializer.deserialize_i128()".to_string(),
414            U8 => "deserializer.deserialize_u8()".to_string(),
415            U16 => "deserializer.deserialize_u16()".to_string(),
416            U32 => "deserializer.deserialize_u32()".to_string(),
417            U64 => "deserializer.deserialize_u64()".to_string(),
418            U128 => "deserializer.deserialize_u128()".to_string(),
419            F32 => "deserializer.deserialize_f32()".to_string(),
420            F64 => "deserializer.deserialize_f64()".to_string(),
421            Char => "deserializer.deserialize_char()".to_string(),
422            Str => "deserializer.deserialize_str()".to_string(),
423            Bytes => "deserializer.deserialize_bytes()".to_string(),
424            _ => format!(
425                "{}.deserialize_{}(deserializer)",
426                self.quote_qualified_name("TraitHelpers"),
427                common::mangle_type(format),
428            ),
429        }
430    }
431
432    fn output_serialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
433        use Format::*;
434
435        write!(
436            self.out,
437            "public static void serialize_{}({} value, Serde.ISerializer serializer) {{",
438            name,
439            self.quote_type(format0)
440        )?;
441        self.out.indent();
442        match format0 {
443            Option(format) => {
444                write!(
445                    self.out,
446                    r#"
447if (value.IsSome(out var val)) {{
448    serializer.serialize_option_tag(true);
449    {}
450}} else {{
451    serializer.serialize_option_tag(false);
452}}
453"#,
454                    self.quote_serialize_value("val", format)
455                )?;
456            }
457
458            Seq(format) => {
459                write!(
460                    self.out,
461                    r#"
462serializer.serialize_len(value.Count);
463foreach (var item in value) {{
464    {}
465}}
466"#,
467                    self.quote_serialize_value("item", format)
468                )?;
469            }
470
471            Map { key, value } => {
472                write!(
473                    self.out,
474                    r#"
475serializer.serialize_len(value.Count);
476int[] offsets = new int[value.Count];
477int count = 0;
478foreach (KeyValuePair<{}, {}> entry in value) {{
479    offsets[count++] = serializer.get_buffer_offset();
480    {}
481    {}
482}}
483serializer.sort_map_entries(offsets);
484"#,
485                    self.quote_type(key),
486                    self.quote_type(value),
487                    self.quote_serialize_value("entry.Key", key),
488                    self.quote_serialize_value("entry.Value", value)
489                )?;
490            }
491
492            Tuple(formats) => {
493                writeln!(self.out)?;
494                for (index, format) in formats.iter().enumerate() {
495                    let expr = format!("value.Item{}", index + 1);
496                    writeln!(self.out, "{}", self.quote_serialize_value(&expr, format))?;
497                }
498            }
499
500            TupleArray { content, size } => {
501                write!(
502                    self.out,
503                    r#"
504if (value.Count != {0}) {{
505    throw new Serde.SerializationException("Invalid length for fixed-size array: " + value.Count + " instead of " + {0});
506}}
507foreach (var item in value) {{
508    {1}
509}}
510"#,
511                    size,
512                    self.quote_serialize_value("item", content),
513                )?;
514            }
515
516            _ => panic!("unexpected case"),
517        }
518        self.out.unindent();
519        writeln!(self.out, "}}\n")
520    }
521
522    fn output_deserialization_helper(&mut self, name: &str, format0: &Format) -> Result<()> {
523        use Format::*;
524
525        write!(
526            self.out,
527            "public static {} deserialize_{}(Serde.IDeserializer deserializer) {{",
528            self.quote_type(format0),
529            name,
530        )?;
531        self.out.indent();
532        match format0 {
533            Option(format) => {
534                write!(
535                    self.out,
536                    r#"
537bool tag = deserializer.deserialize_option_tag();
538if (!tag) {{
539    return Serde.Option<{0}>.None;
540}} else {{
541    return Serde.Option<{0}>.Some({1});
542}}
543"#,
544                    self.quote_type(format),
545                    self.quote_deserialize(format),
546                )?;
547            }
548
549            Seq(format) => {
550                write!(
551                    self.out,
552                    r#"
553long length = deserializer.deserialize_len();
554{0}[] obj = new {0}[length];
555for (int i = 0; i < length; i++) {{
556    obj[i] = {1};
557}}
558return new Serde.ValueArray<{0}>(obj);
559"#,
560                    self.quote_type(format),
561                    self.quote_deserialize(format)
562                )?;
563            }
564
565            Map { key, value } => {
566                write!(
567                    self.out,
568                    r#"
569long length = deserializer.deserialize_len();
570var obj = new Dictionary<{0}, {1}>();
571int previous_key_start = 0;
572int previous_key_end = 0;
573for (long i = 0; i < length; i++) {{
574    int key_start = deserializer.get_buffer_offset();
575    var key = {2};
576    int key_end = deserializer.get_buffer_offset();
577    if (i > 0) {{
578        deserializer.check_that_key_slices_are_increasing(
579            new Serde.Range(previous_key_start, previous_key_end),
580            new Serde.Range(key_start, key_end));
581    }}
582    previous_key_start = key_start;
583    previous_key_end = key_end;
584    var value = {3};
585    obj[key] = value;
586}}
587return new Serde.ValueDictionary<{0}, {1}>(obj);
588"#,
589                    self.quote_type(key),
590                    self.quote_type(value),
591                    self.quote_deserialize(key),
592                    self.quote_deserialize(value),
593                )?;
594            }
595
596            Tuple(formats) => {
597                write!(
598                    self.out,
599                    r#"
600return ({}
601);
602"#,
603                    formats
604                        .iter()
605                        .map(|f| format!("\n    {}", self.quote_deserialize(f)))
606                        .collect::<Vec<_>>()
607                        .join(",")
608                )?;
609            }
610
611            TupleArray { content, size } => {
612                write!(
613                    self.out,
614                    r#"
615{0}[] obj = new {0}[{1}];
616for (int i = 0; i < {1}; i++) {{
617    obj[i] = {2};
618}}
619return new Serde.ValueArray<{0}>(obj);
620"#,
621                    self.quote_type(content),
622                    size,
623                    self.quote_deserialize(content)
624                )?;
625            }
626
627            _ => panic!("unexpected case"),
628        }
629        self.out.unindent();
630        writeln!(self.out, "}}\n")
631    }
632
633    fn output_variant(
634        &mut self,
635        base: &str,
636        index: u32,
637        name: &str,
638        variant: &VariantFormat,
639    ) -> Result<()> {
640        use VariantFormat::*;
641        let fields = match variant {
642            Unit => Vec::new(),
643            NewType(format) => vec![Named {
644                name: "value".to_string(),
645                value: format.as_ref().clone(),
646            }],
647            Tuple(formats) => formats
648                .iter()
649                .enumerate()
650                .map(|(i, f)| Named {
651                    name: format!("field{i}"),
652                    value: f.clone(),
653                })
654                .collect(),
655            Struct(fields) => fields.clone(),
656            Variable(_) => panic!("incorrect value"),
657        };
658        self.output_struct_or_variant_container(Some(base), Some(index), name, &fields)
659    }
660
661    fn output_variants(
662        &mut self,
663        base: &str,
664        variants: &BTreeMap<u32, Named<VariantFormat>>,
665    ) -> Result<()> {
666        for (index, variant) in variants {
667            self.output_variant(base, *index, &variant.name, &variant.value)?;
668        }
669        Ok(())
670    }
671
672    fn output_struct_or_variant_container(
673        &mut self,
674        variant_base: Option<&str>,
675        variant_index: Option<u32>,
676        name: &str,
677        fields: &[Named<Format>],
678    ) -> Result<()> {
679        // Beginning of class
680        writeln!(self.out)?;
681        let fn_mods = if let Some(base) = variant_base {
682            self.output_comment(name)?;
683            writeln!(
684                self.out,
685                "public sealed class {name}: {base}, IEquatable<{name}>, ICloneable {{"
686            )?;
687            "override "
688        } else {
689            self.output_comment(name)?;
690            writeln!(
691                self.out,
692                "public sealed class {name}: IEquatable<{name}>, ICloneable {{"
693            )?;
694            ""
695        };
696        let reserved_names = &[];
697        self.enter_class(name, reserved_names);
698
699        // Fields
700        for field in fields {
701            self.output_comment(&field.name)?;
702            writeln!(
703                self.out,
704                "public {} {};",
705                self.quote_type(&field.value),
706                field.name
707            )?;
708        }
709        if !fields.is_empty() {
710            writeln!(self.out)?;
711        }
712
713        // Constructor.
714        writeln!(
715            self.out,
716            "public {}({}) {{",
717            name,
718            fields
719                .iter()
720                .map(|f| format!("{} _{}", self.quote_type(&f.value), &f.name))
721                .collect::<Vec<_>>()
722                .join(", ")
723        )?;
724        self.out.indent();
725        for field in fields {
726            if self.is_nullable(&field.value) {
727                writeln!(
728                    self.out,
729                    "if (_{0} == null) throw new ArgumentNullException(nameof(_{0}));",
730                    &field.name
731                )?;
732            }
733            writeln!(self.out, "{0} = _{0};", &field.name)?;
734        }
735        self.out.unindent();
736        writeln!(self.out, "}}")?;
737
738        // Serialize
739        if self.generator.config.serialization {
740            writeln!(
741                self.out,
742                "\npublic {fn_mods}void Serialize(Serde.ISerializer serializer) {{"
743            )?;
744            self.out.indent();
745            writeln!(self.out, "serializer.increase_container_depth();")?;
746            if let Some(index) = variant_index {
747                writeln!(self.out, "serializer.serialize_variant_index({index});")?;
748            }
749            for field in fields {
750                writeln!(
751                    self.out,
752                    "{}",
753                    self.quote_serialize_value(&field.name, &field.value)
754                )?;
755            }
756            writeln!(self.out, "serializer.decrease_container_depth();")?;
757            self.out.unindent();
758            writeln!(self.out, "}}")?;
759
760            if variant_index.is_none() {
761                for encoding in &self.generator.config.encodings {
762                    self.output_class_serialize_for_encoding(*encoding)?;
763                }
764            }
765        }
766
767        // Deserialize (struct) or Load (variant)
768        if self.generator.config.serialization {
769            if variant_index.is_none() {
770                writeln!(
771                    self.out,
772                    "\npublic static {fn_mods}{name} Deserialize(Serde.IDeserializer deserializer) {{",
773                )?;
774            } else {
775                writeln!(
776                    self.out,
777                    "\ninternal static {name} Load(Serde.IDeserializer deserializer) {{",
778                )?;
779            }
780            self.out.indent();
781            writeln!(self.out, "deserializer.increase_container_depth();")?;
782            writeln!(
783                self.out,
784                "{0} obj = new {0}(\n\t{1});",
785                name,
786                fields
787                    .iter()
788                    .map(|f| self.quote_deserialize(&f.value))
789                    .collect::<Vec<_>>()
790                    .join(",\n\t")
791            )?;
792            writeln!(self.out, "deserializer.decrease_container_depth();")?;
793            writeln!(self.out, "return obj;")?;
794            self.out.unindent();
795            writeln!(self.out, "}}")?;
796
797            if variant_index.is_none() {
798                for encoding in &self.generator.config.encodings {
799                    self.output_class_deserialize_for_encoding(name, *encoding)?;
800                }
801            }
802        }
803        // Equality
804        writeln!(
805            self.out,
806            "public override bool Equals(object obj) => obj is {name} other && Equals(other);\n"
807        )?;
808        writeln!(
809            self.out,
810            "public static bool operator ==({name} left, {name} right) => Equals(left, right);\n"
811        )?;
812        writeln!(
813            self.out,
814            "public static bool operator !=({name} left, {name} right) => !Equals(left, right);\n"
815        )?;
816
817        writeln!(self.out, "public bool Equals({name} other) {{")?;
818        self.out.indent();
819        writeln!(self.out, "if (other == null) return false;")?;
820        writeln!(self.out, "if (ReferenceEquals(this, other)) return true;")?;
821        for field in fields {
822            writeln!(
823                self.out,
824                "if (!{0}.Equals(other.{0})) return false;",
825                &field.name,
826            )?;
827        }
828        writeln!(self.out, "return true;")?;
829        self.out.unindent();
830        writeln!(self.out, "}}")?;
831
832        // Hashing
833        writeln!(self.out, "\npublic override int GetHashCode() {{")?;
834        self.out.indent();
835        writeln!(self.out, "unchecked {{")?;
836        self.out.indent();
837        writeln!(self.out, "int value = 7;")?;
838        for field in fields {
839            writeln!(
840                self.out,
841                "value = 31 * value + {0}.GetHashCode();",
842                &field.name
843            )?;
844        }
845        writeln!(self.out, "return value;")?;
846        self.out.unindent();
847        writeln!(self.out, "}}")?;
848        self.out.unindent();
849        writeln!(self.out, "}}\n")?;
850
851        // Clone
852        if variant_base.is_none() {
853            // Derived classes can use the method inherited from the base class, it works with derived fields.
854            writeln!(
855                self.out,
856                "/// <summary>Creates a shallow clone of the object.</summary>"
857            )?;
858            writeln!(
859                self.out,
860                "public {name} Clone() => ({name})MemberwiseClone();\n"
861            )?;
862            writeln!(self.out, "object ICloneable.Clone() => Clone();\n")?;
863        }
864
865        // Custom code
866        self.output_custom_code()?;
867
868        // End of class
869        self.leave_class(reserved_names);
870        writeln!(self.out, "}}")
871    }
872
873    fn output_enum_container(
874        &mut self,
875        name: &str,
876        variants: &BTreeMap<u32, Named<VariantFormat>>,
877    ) -> Result<()> {
878        writeln!(self.out)?;
879        self.output_comment(name)?;
880        writeln!(
881            self.out,
882            "public abstract class {name}: IEquatable<{name}>, ICloneable {{"
883        )?;
884        let reserved_names = variants
885            .values()
886            .map(|v| v.name.as_str())
887            .collect::<Vec<_>>();
888        self.enter_class(name, &reserved_names);
889
890        // Serialize/Deserialize
891        if self.generator.config.serialization {
892            writeln!(
893                self.out,
894                "\npublic abstract void Serialize(Serde.ISerializer serializer);"
895            )?;
896            write!(
897                self.out,
898                "\npublic static {name} Deserialize(Serde.IDeserializer deserializer) {{"
899            )?;
900            self.out.indent();
901            writeln!(
902                self.out,
903                r#"
904int index = deserializer.deserialize_variant_index();
905switch (index) {{"#,
906            )?;
907            self.out.indent();
908            for (index, variant) in variants {
909                writeln!(
910                    self.out,
911                    "case {}: return {}.Load(deserializer);",
912                    index, variant.name,
913                )?;
914            }
915            writeln!(
916                self.out,
917                r#"default: throw new Serde.DeserializationException("Unknown variant index for {name}: " + index);"#,
918            )?;
919            self.out.unindent();
920            writeln!(self.out, "}}")?;
921            self.out.unindent();
922            writeln!(self.out, "}}")?;
923
924            for encoding in &self.generator.config.encodings {
925                self.output_class_serialize_for_encoding(*encoding)?;
926                self.output_class_deserialize_for_encoding(name, *encoding)?;
927            }
928        }
929
930        // HashCode
931        writeln!(self.out, "public override int GetHashCode() {{")?;
932        self.out.indent();
933        writeln!(self.out, "switch (this) {{")?;
934        for variant in variants.values() {
935            writeln!(self.out, "case {} x: return x.GetHashCode();", variant.name)?;
936        }
937        writeln!(
938            self.out,
939            r#"default: throw new InvalidOperationException("Unknown variant type");"#
940        )?;
941        writeln!(self.out, "}}")?;
942        self.out.unindent();
943        writeln!(self.out, "}}")?;
944
945        // Equals
946        writeln!(
947            self.out,
948            "public override bool Equals(object obj) => obj is {name} other && Equals(other);\n"
949        )?;
950
951        writeln!(self.out, "public bool Equals({name} other) {{")?;
952        self.out.indent();
953        writeln!(self.out, "if (other == null) return false;")?;
954        writeln!(self.out, "if (ReferenceEquals(this, other)) return true;")?;
955        writeln!(self.out, "if (GetType() != other.GetType()) return false;")?;
956        writeln!(self.out, "switch (this) {{")?;
957        for variant in variants.values() {
958            writeln!(
959                self.out,
960                "case {0} x: return x.Equals(({0})other);",
961                variant.name
962            )?;
963        }
964        writeln!(
965            self.out,
966            r#"default: throw new InvalidOperationException("Unknown variant type");"#
967        )?;
968        writeln!(self.out, "}}")?;
969        self.out.unindent();
970        writeln!(self.out, "}}\n")?;
971
972        // Clone
973        writeln!(
974            self.out,
975            "/// <summary>Creates a shallow clone of the object.</summary>"
976        )?;
977        writeln!(
978            self.out,
979            "public {name} Clone() => ({name})MemberwiseClone();\n"
980        )?;
981        writeln!(self.out, "object ICloneable.Clone() => Clone();\n")?;
982
983        self.output_variants(name, variants)?;
984        self.leave_class(&reserved_names);
985        writeln!(self.out, "}}\n")
986    }
987
988    fn output_cstyle_enum(
989        &mut self,
990        name: &str,
991        variants: &BTreeMap<u32, Named<VariantFormat>>,
992    ) -> Result<()> {
993        writeln!(self.out)?;
994        self.output_comment(name)?;
995        writeln!(self.out, "public enum {name} {{")?;
996        self.out.indent();
997        for (index, variant) in variants {
998            writeln!(self.out, "{} = {},", variant.name, index)?;
999        }
1000        self.out.unindent();
1001        writeln!(self.out, "}}")?;
1002
1003        if self.generator.config.serialization {
1004            let ext_name = format!("{}Extensions", name.to_camel_case());
1005            writeln!(self.out, "public static class {ext_name} {{")?;
1006            self.enter_class(&ext_name, &[]);
1007
1008            writeln!(
1009                self.out,
1010                r#"
1011public static void Serialize(this {name} value, Serde.ISerializer serializer) {{
1012    serializer.increase_container_depth();
1013    serializer.serialize_variant_index((int)value);
1014    serializer.decrease_container_depth();
1015}}
1016
1017public static {name} Deserialize(Serde.IDeserializer deserializer) {{
1018    deserializer.increase_container_depth();
1019    int index = deserializer.deserialize_variant_index();
1020    if (!Enum.IsDefined(typeof({name}), index))
1021        throw new Serde.DeserializationException("Unknown variant index for {name}: " + index);
1022    {name} value = ({name})index;
1023    deserializer.decrease_container_depth();
1024    return value;
1025}}"#
1026            )?;
1027
1028            for encoding in &self.generator.config.encodings {
1029                writeln!(
1030                    self.out,
1031                    r#"
1032public static byte[] {0}Serialize(this {1} value)  {{
1033    Serde.ISerializer serializer = new {0}.{0}Serializer();
1034    Serialize(value, serializer);
1035    return serializer.get_bytes();
1036}}"#,
1037                    encoding.name().to_camel_case(),
1038                    name
1039                )?;
1040                self.output_class_deserialize_for_encoding(name, *encoding)?;
1041            }
1042
1043            self.leave_class(&[]);
1044            writeln!(self.out, "}}")?;
1045        }
1046
1047        Ok(())
1048    }
1049
1050    fn output_class_serialize_for_encoding(&mut self, encoding: Encoding) -> Result<()> {
1051        writeln!(
1052            self.out,
1053            r#"
1054public int {0}Serialize(byte[] outputBuffer) => {0}Serialize(new ArraySegment<byte>(outputBuffer));
1055
1056public int {0}Serialize(ArraySegment<byte> outputBuffer) {{
1057    Serde.ISerializer serializer = new {0}.{0}Serializer(outputBuffer);
1058    Serialize(serializer);
1059    return serializer.get_buffer_offset();
1060}}
1061
1062public byte[] {0}Serialize()  {{
1063    Serde.ISerializer serializer = new {0}.{0}Serializer();
1064    Serialize(serializer);
1065    return serializer.get_bytes();
1066}}"#,
1067            encoding.name().to_camel_case()
1068        )
1069    }
1070
1071    fn output_class_deserialize_for_encoding(
1072        &mut self,
1073        name: &str,
1074        encoding: Encoding,
1075    ) -> Result<()> {
1076        writeln!(
1077            self.out,
1078            r#"
1079public static {0} {1}Deserialize(byte[] input) => {1}Deserialize(new ArraySegment<byte>(input));
1080
1081public static {0} {1}Deserialize(ArraySegment<byte> input) {{
1082    if (input == null) {{
1083         throw new Serde.DeserializationException("Cannot deserialize null array");
1084    }}
1085    Serde.IDeserializer deserializer = new {1}.{1}Deserializer(input);
1086    {0} value = Deserialize(deserializer);
1087    if (deserializer.get_buffer_offset() < input.Count) {{
1088         throw new Serde.DeserializationException("Some input bytes were not read");
1089    }}
1090    return value;
1091}}"#,
1092            name,
1093            encoding.name().to_camel_case()
1094        )
1095    }
1096
1097    fn output_container(&mut self, name: &str, format: &ContainerFormat) -> Result<()> {
1098        use ContainerFormat::*;
1099        let fields = match format {
1100            UnitStruct => Vec::new(),
1101            NewTypeStruct(format) => vec![Named {
1102                name: "value".to_string(),
1103                value: format.as_ref().clone(),
1104            }],
1105            TupleStruct(formats) => formats
1106                .iter()
1107                .enumerate()
1108                .map(|(i, f)| Named {
1109                    name: format!("field{i}"),
1110                    value: f.clone(),
1111                })
1112                .collect::<Vec<_>>(),
1113            Struct(fields) => fields.clone(),
1114            Enum(variants) => {
1115                if variants
1116                    .iter()
1117                    .all(|(_i, v)| v.value == VariantFormat::Unit)
1118                    && self.cstyle_enum_names.contains(&name.into())
1119                {
1120                    self.output_cstyle_enum(name, variants)?;
1121                } else {
1122                    self.output_enum_container(name, variants)?;
1123                }
1124                return Ok(());
1125            }
1126        };
1127        self.output_struct_or_variant_container(None, None, name, &fields)
1128    }
1129}
1130
1131/// Installer for generated source files in C#.
1132pub struct Installer {
1133    install_dir: PathBuf,
1134}
1135
1136impl Installer {
1137    pub fn new(install_dir: PathBuf) -> Self {
1138        Installer { install_dir }
1139    }
1140
1141    fn install_runtime(
1142        &self,
1143        source_dir: include_dir::Dir,
1144        path: &str,
1145    ) -> std::result::Result<(), Box<dyn std::error::Error>> {
1146        let dir_path = self.install_dir.join(path);
1147        std::fs::create_dir_all(&dir_path)?;
1148        for entry in source_dir.files() {
1149            let mut file = std::fs::File::create(dir_path.join(entry.path()))?;
1150            file.write_all(entry.contents())?;
1151        }
1152        Ok(())
1153    }
1154}
1155
1156impl crate::SourceInstaller for Installer {
1157    type Error = Box<dyn std::error::Error>;
1158
1159    fn install_module(
1160        &self,
1161        config: &CodeGeneratorConfig,
1162        registry: &Registry,
1163    ) -> std::result::Result<(), Self::Error> {
1164        let name = config.module_name.clone();
1165        let generator = CodeGenerator::new(config);
1166        let dir_path = generator.write_source_files(self.install_dir.clone(), registry)?;
1167
1168        if !config.package_manifest {
1169            return Ok(());
1170        }
1171
1172        let back_path: String = "..\\"
1173            .to_string()
1174            .repeat(dir_path.strip_prefix(&self.install_dir)?.iter().count());
1175        let mut deps = vec!["Serde".to_string()];
1176        for encoding in &config.encodings {
1177            deps.push(encoding.name().to_camel_case());
1178        }
1179        let mut dependencies = String::new();
1180        for dep in deps {
1181            writeln!(
1182                &mut dependencies,
1183                "        <ProjectReference Include=\"{back_path}{dep}\\{dep}.csproj\" />"
1184            )?;
1185        }
1186        let mut proj = std::fs::File::create(dir_path.join(name + ".csproj"))?;
1187        write!(
1188            proj,
1189            r#"
1190<Project Sdk="Microsoft.NET.Sdk">
1191    <PropertyGroup>
1192        <TargetFramework>netstandard2.0</TargetFramework>
1193        <LangVersion>7.2</LangVersion>
1194    </PropertyGroup>
1195    <ItemGroup>
1196        <PackageReference Include="System.Memory" Version="4.5.4" />
1197        <PackageReference Include="System.ValueTuple" Version="4.5.0" />
1198    </ItemGroup>
1199    <ItemGroup>
1200{dependencies}
1201    </ItemGroup>
1202</Project>
1203"#
1204        )?;
1205
1206        Ok(())
1207    }
1208
1209    fn install_serde_runtime(&self) -> std::result::Result<(), Self::Error> {
1210        self.install_runtime(include_directory!("runtime/csharp/Serde"), "Serde")
1211    }
1212
1213    fn install_bincode_runtime(&self) -> std::result::Result<(), Self::Error> {
1214        self.install_runtime(include_directory!("runtime/csharp/Bincode"), "Bincode")
1215    }
1216
1217    fn install_bcs_runtime(&self) -> std::result::Result<(), Self::Error> {
1218        self.install_runtime(include_directory!("runtime/csharp/Bcs"), "Bcs")
1219    }
1220}