Skip to main content

ferogram_tl_gen/
codegen.rs

1// Copyright (c) Ankit Chaubey <ankitchaubey.dev@gmail.com>
2//
3// ferogram: async Telegram MTProto client in Rust
4// https://github.com/ankit-chaubey/ferogram
5//
6// Licensed under either the MIT License or the Apache License 2.0.
7// See the LICENSE-MIT or LICENSE-APACHE file in this repository:
8// https://github.com/ankit-chaubey/ferogram
9//
10// Feel free to use, modify, and share this code.
11// Please keep this notice when redistributing.
12
13use std::fs::File;
14use std::io::{self, Write};
15use std::path::Path;
16
17use ferogram_tl_parser::tl::{Category, Definition, ParameterType};
18
19use crate::grouper;
20use crate::metadata::Metadata;
21use crate::namegen as n;
22
23/// Generation configuration.
24pub struct Config {
25    /// Emit `name_for_id(id) -> Option<&'static str>` in the common module.
26    pub gen_name_for_id: bool,
27    /// Also implement `Deserializable` for function types (useful for servers).
28    pub deserializable_functions: bool,
29    /// Derive `Debug` on all generated types.
30    pub impl_debug: bool,
31    /// Emit `From<types::Foo> for enums::Bar` impls.
32    pub impl_from_type: bool,
33    /// Emit `TryFrom<enums::Bar> for types::Foo` impls.
34    pub impl_from_enum: bool,
35    /// Derive `serde::{Serialize, Deserialize}` on all types.
36    pub impl_serde: bool,
37}
38
39impl Default for Config {
40    fn default() -> Self {
41        Self {
42            gen_name_for_id: false,
43            deserializable_functions: false,
44            impl_debug: true,
45            impl_from_type: true,
46            impl_from_enum: true,
47            impl_serde: false,
48        }
49    }
50}
51
52// Outputs
53
54/// Writers for each generated Rust module.
55pub struct Outputs<W: Write> {
56    /// Receives the layer constant, `name_for_id`, etc.
57    pub common: W,
58    /// Receives `pub mod types { ... }` (concrete constructors as structs).
59    pub types: W,
60    /// Receives `pub mod functions { ... }` (RPC functions as structs).
61    pub functions: W,
62    /// Receives `pub mod enums { ... }` (boxed types as enums).
63    pub enums: W,
64}
65
66impl Outputs<File> {
67    /// Convenience constructor that opens files inside `out_dir`.
68    pub fn from_dir(out_dir: &str) -> io::Result<Self> {
69        let p = Path::new(out_dir);
70        Ok(Self {
71            common: File::create(p.join("generated_common.rs"))?,
72            types: File::create(p.join("generated_types.rs"))?,
73            functions: File::create(p.join("generated_functions.rs"))?,
74            enums: File::create(p.join("generated_enums.rs"))?,
75        })
76    }
77}
78
79impl<W: Write> Outputs<W> {
80    /// Flush all writers.
81    pub fn flush(&mut self) -> io::Result<()> {
82        self.common.flush()?;
83        self.types.flush()?;
84        self.functions.flush()?;
85        self.enums.flush()
86    }
87}
88
89// Special-cased primitives
90
91/// These TL types are handled as Rust primitives; we never emit structs/enums.
92const BUILTIN_TYPES: &[&str] = &["Bool", "True"];
93
94fn is_builtin(ty_name: &str) -> bool {
95    BUILTIN_TYPES.contains(&ty_name)
96}
97
98// Public API
99
100/// Generate Rust source code from a slice of parsed TL definitions.
101///
102/// Write results into `outputs`. Call `outputs.flush()` when done.
103pub fn generate<W: Write>(
104    defs: &[Definition],
105    config: &Config,
106    outputs: &mut Outputs<W>,
107) -> io::Result<()> {
108    let meta = Metadata::build(defs);
109
110    write_common(defs, config, &mut outputs.common)?;
111    write_types_mod(defs, config, &meta, &mut outputs.types)?;
112    write_functions_mod(defs, config, &meta, &mut outputs.functions)?;
113    write_enums_mod(defs, config, &meta, &mut outputs.enums)?;
114
115    Ok(())
116}
117
118// Common module
119
120fn write_common<W: Write>(defs: &[Definition], config: &Config, out: &mut W) -> io::Result<()> {
121    // Extract LAYER constant from the first `// LAYER N` comment heuristic
122    // for now we derive it from the highest layer seen in definitions or emit 0.
123    writeln!(out, "// @generated: do not edit by hand")?;
124    writeln!(out, "// Re-run the build script to regenerate.")?;
125    writeln!(out)?;
126    writeln!(out, "/// The API layer this code was generated from.")?;
127    writeln!(out, "pub const LAYER: i32 = 0; // update via build.rs")?;
128    writeln!(out)?;
129
130    if config.gen_name_for_id {
131        writeln!(out, "/// Returns the TL name for a known constructor ID.")?;
132        writeln!(
133            out,
134            "pub fn name_for_id(id: u32) -> Option<&'static str> {{"
135        )?;
136        writeln!(out, "    match id {{")?;
137        for def in defs {
138            writeln!(
139                out,
140                "        {:#010x} => Some(\"{}\"),",
141                def.id,
142                def.full_name()
143            )?;
144        }
145        writeln!(out, "        _ => None,")?;
146        writeln!(out, "    }}")?;
147        writeln!(out, "}}")?;
148    }
149
150    Ok(())
151}
152
153// Struct generation (types + functions)
154
155fn write_types_mod<W: Write>(
156    defs: &[Definition],
157    config: &Config,
158    meta: &Metadata,
159    out: &mut W,
160) -> io::Result<()> {
161    writeln!(out, "// @generated: do not edit by hand")?;
162    writeln!(out, "pub mod types {{")?;
163
164    let grouped = grouper::group_by_ns(defs, Category::Types);
165    let mut namespaces: Vec<&String> = grouped.keys().collect();
166    namespaces.sort();
167
168    for ns in namespaces {
169        let bucket = &grouped[ns];
170        let indent: String = if ns.is_empty() {
171            "    ".to_owned()
172        } else {
173            writeln!(out, "    pub mod {ns} {{")?;
174            "        ".to_owned()
175        };
176
177        for def in bucket {
178            write_struct(out, &indent, def, meta, config)?;
179            write_identifiable(out, &indent, def)?;
180            write_struct_serializable(out, &indent, def, meta)?;
181            write_struct_deserializable(out, &indent, def, meta)?;
182        }
183
184        if !ns.is_empty() {
185            writeln!(out, "    }}")?;
186        }
187    }
188
189    writeln!(out, "}}")
190}
191
192fn write_functions_mod<W: Write>(
193    defs: &[Definition],
194    config: &Config,
195    meta: &Metadata,
196    out: &mut W,
197) -> io::Result<()> {
198    writeln!(out, "// @generated: do not edit by hand")?;
199    writeln!(out, "pub mod functions {{")?;
200
201    let grouped = grouper::group_by_ns(defs, Category::Functions);
202    let mut namespaces: Vec<&String> = grouped.keys().collect();
203    namespaces.sort();
204
205    for ns in namespaces {
206        let bucket = &grouped[ns];
207        let indent: String = if ns.is_empty() {
208            "    ".to_owned()
209        } else {
210            writeln!(out, "    pub mod {ns} {{")?;
211            "        ".to_owned()
212        };
213
214        for def in bucket {
215            write_struct(out, &indent, def, meta, config)?;
216            write_identifiable(out, &indent, def)?;
217            write_struct_serializable(out, &indent, def, meta)?;
218            if config.deserializable_functions {
219                write_struct_deserializable(out, &indent, def, meta)?;
220            }
221            write_remote_call(out, &indent, def, meta)?;
222        }
223
224        if !ns.is_empty() {
225            writeln!(out, "    }}")?;
226        }
227    }
228
229    writeln!(out, "}}")
230}
231
232// Struct pieces
233
234fn generic_list(def: &Definition, bounds: &str) -> String {
235    let mut params: Vec<&str> = Vec::new();
236    for p in &def.params {
237        if let ParameterType::Normal { ty, .. } = &p.ty
238            && ty.generic_ref
239            && !params.contains(&ty.name.as_str())
240        {
241            params.push(&ty.name);
242        }
243    }
244    if params.is_empty() {
245        String::new()
246    } else {
247        format!("<{}>", params.join(&format!("{bounds}, ")) + bounds)
248    }
249}
250
251fn write_struct<W: Write>(
252    out: &mut W,
253    indent: &str,
254    def: &Definition,
255    meta: &Metadata,
256    config: &Config,
257) -> io::Result<()> {
258    let kind = match def.category {
259        Category::Types => "constructor",
260        Category::Functions => "method",
261    };
262    writeln!(
263        out,
264        "\n{indent}/// [`{name}`](https://core.telegram.org/{kind}/{name})\n\
265         {indent}///\n\
266         {indent}/// Generated from:\n\
267         {indent}/// ```tl\n\
268         {indent}/// {def}\n\
269         {indent}/// ```",
270        name = def.full_name(),
271    )?;
272
273    if config.impl_debug {
274        writeln!(out, "{indent}#[derive(Debug)]")?;
275    }
276    if config.impl_serde {
277        writeln!(
278            out,
279            "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
280        )?;
281    }
282    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
283    writeln!(
284        out,
285        "{indent}pub struct {}{} {{",
286        n::def_type_name(def),
287        generic_list(def, ""),
288    )?;
289
290    for param in &def.params {
291        match &param.ty {
292            ParameterType::Flags => {} // computed on-the-fly
293            ParameterType::Normal { .. } => {
294                writeln!(
295                    out,
296                    "{indent}    pub {}: {},",
297                    n::param_attr_name(param),
298                    n::param_qual_name(param, meta),
299                )?;
300            }
301        }
302    }
303    writeln!(out, "{indent}}}")
304}
305
306fn write_identifiable<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
307    let gl = generic_list(def, "");
308    writeln!(
309        out,
310        "{indent}impl{gl} crate::Identifiable for {}{gl} {{\n\
311         {indent}    const CONSTRUCTOR_ID: u32 = {:#010x};\n\
312         {indent}}}",
313        n::def_type_name(def),
314        def.id,
315    )
316}
317
318fn write_struct_serializable<W: Write>(
319    out: &mut W,
320    indent: &str,
321    def: &Definition,
322    meta: &Metadata,
323) -> io::Result<()> {
324    let gl_decl = generic_list(def, ": crate::Serializable");
325    let gl_use = generic_list(def, "");
326
327    writeln!(
328        out,
329        "{indent}impl{gl_decl} crate::Serializable for {}{gl_use} {{",
330        n::def_type_name(def)
331    )?;
332
333    let underscore = if def.category == Category::Types && def.params.is_empty() {
334        "_"
335    } else {
336        ""
337    };
338    writeln!(
339        out,
340        "{indent}    fn serialize(&self, {underscore}buf: &mut impl Extend<u8>) {{"
341    )?;
342
343    if def.category == Category::Functions {
344        writeln!(out, "{indent}        use crate::Identifiable;")?;
345        writeln!(out, "{indent}        Self::CONSTRUCTOR_ID.serialize(buf);")?;
346    }
347
348    for param in &def.params {
349        write_param_serialization(out, indent, def, meta, param)?;
350    }
351
352    writeln!(out, "{indent}    }}")?;
353    writeln!(out, "{indent}}}")
354}
355
356fn write_param_serialization<W: Write>(
357    out: &mut W,
358    indent: &str,
359    def: &Definition,
360    meta: &Metadata,
361    param: &ferogram_tl_parser::tl::Parameter,
362) -> io::Result<()> {
363    use ParameterType::*;
364
365    match &param.ty {
366        Flags => {
367            if meta.is_unused_flag(def, param) {
368                writeln!(out, "{indent}        0u32.serialize(buf);")?;
369                return Ok(());
370            }
371            // Compute the flags bitmask from optional params
372            write!(out, "{indent}        (")?;
373            let mut first = true;
374            for other in &def.params {
375                if let Normal {
376                    flag: Some(fl), ty, ..
377                } = &other.ty
378                {
379                    if fl.name != param.name {
380                        continue;
381                    }
382                    if !first {
383                        write!(out, " | ")?;
384                    }
385                    first = false;
386                    if ty.name == "true" {
387                        write!(
388                            out,
389                            "if self.{} {{ 1 << {} }} else {{ 0 }}",
390                            n::param_attr_name(other),
391                            fl.index
392                        )?;
393                    } else {
394                        write!(
395                            out,
396                            "if self.{}.is_some() {{ 1 << {} }} else {{ 0 }}",
397                            n::param_attr_name(other),
398                            fl.index
399                        )?;
400                    }
401                }
402            }
403            if first {
404                write!(out, "0u32")?;
405            }
406            writeln!(out, ").serialize(buf);")?;
407        }
408        Normal { ty, flag } => {
409            let attr = n::param_attr_name(param);
410            if flag.is_some() {
411                if ty.name == "true" {
412                    // bool flag: nothing to serialize, it's in the flags word
413                } else {
414                    writeln!(
415                        out,
416                        "{indent}        if let Some(v) = &self.{attr} {{ v.serialize(buf); }}"
417                    )?;
418                }
419            } else {
420                writeln!(out, "{indent}        self.{attr}.serialize(buf);")?;
421            }
422        }
423    }
424    Ok(())
425}
426
427fn write_struct_deserializable<W: Write>(
428    out: &mut W,
429    indent: &str,
430    def: &Definition,
431    meta: &Metadata,
432) -> io::Result<()> {
433    let gl_decl = generic_list(def, ": crate::Deserializable");
434    let gl_use = generic_list(def, "");
435
436    // Empty structs never read from `buf`. Name it `_buf` to suppress the
437    // unused-variable warning in the generated output.
438    let buf_name = if def.params.is_empty() { "_buf" } else { "buf" };
439
440    writeln!(
441        out,
442        "{indent}impl{gl_decl} crate::Deserializable for {}{gl_use} {{",
443        n::def_type_name(def)
444    )?;
445    writeln!(
446        out,
447        "{indent}    fn deserialize({buf_name}: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
448    )?;
449
450    // Debug: entry banner (only for non-empty structs; empty ones use _buf)
451    let struct_name = n::def_type_name(def);
452    if !def.params.is_empty() {
453        writeln!(
454            out,
455            "{indent}        if crate::deserialize::tl_debug() {{ \
456             eprintln!(\"[TL] >>  {}::deserialize  pos={{}}\", buf.pos()); }}",
457            struct_name
458        )?;
459    }
460
461    // Deserialize params in exact TL schema order.
462    // Flags fields are read inline where they appear - never hoisted.
463    for param in &def.params {
464        match &param.ty {
465            ParameterType::Flags => {
466                let fp_attr = n::param_attr_name(param);
467                writeln!(
468                    out,
469                    "{indent}        if crate::deserialize::tl_debug() {{ \
470                     eprintln!(\"[TL]   {struct_name}.{fp_attr} (flags) pos={{}}\", buf.pos()); }}"
471                )?;
472                writeln!(
473                    out,
474                    "{indent}        let _{fp_attr} = u32::deserialize(buf)?;"
475                )?;
476                writeln!(
477                    out,
478                    "{indent}        if crate::deserialize::tl_debug() {{ \
479                     eprintln!(\"[TL]   {struct_name}.{fp_attr} = {{:#034b}}  pos={{}}\", _{fp_attr}, buf.pos()); }}"
480                )?;
481            }
482            ParameterType::Normal { ty, flag } => {
483                let attr = n::param_attr_name(param);
484                // before
485                writeln!(
486                    out,
487                    "{indent}        if crate::deserialize::tl_debug() {{ \
488                     eprintln!(\"[TL]   {struct_name}.{attr} pos={{}}\", buf.pos()); }}"
489                )?;
490                if let Some(fl) = flag {
491                    if ty.name == "true" {
492                        writeln!(
493                            out,
494                            "{indent}        let {attr} = (_{} & (1 << {})) != 0;",
495                            fl.name, fl.index
496                        )?;
497                    } else {
498                        writeln!(
499                            out,
500                            "{indent}        let {attr} = if (_{} & (1 << {})) != 0 {{ Some({}::deserialize(buf)?) }} else {{ None }};",
501                            fl.name,
502                            fl.index,
503                            n::type_item_path(ty, meta)
504                        )?;
505                    }
506                } else {
507                    writeln!(
508                        out,
509                        "{indent}        let {attr} = {}::deserialize(buf)?;",
510                        n::type_item_path(ty, meta)
511                    )?;
512                }
513                // after
514                writeln!(
515                    out,
516                    "{indent}        if crate::deserialize::tl_debug() {{ \
517                     eprintln!(\"[TL]   {struct_name}.{attr} done  pos={{}}\", buf.pos()); }}"
518                )?;
519            }
520        }
521    }
522
523    // Debug: exit banner (only for non-empty structs; empty ones use _buf)
524    if !def.params.is_empty() {
525        writeln!(
526            out,
527            "{indent}        if crate::deserialize::tl_debug() {{ \
528             eprintln!(\"[TL] <<  {struct_name}::deserialize done  pos={{}}\", buf.pos()); }}"
529        )?;
530    }
531
532    writeln!(out, "{indent}        Ok(Self {{")?;
533    for param in &def.params {
534        if param.ty != ParameterType::Flags {
535            let attr = n::param_attr_name(param);
536            writeln!(out, "{indent}            {attr},")?;
537        }
538    }
539    writeln!(out, "{indent}        }})")?;
540    writeln!(out, "{indent}    }}")?;
541    writeln!(out, "{indent}}}")
542}
543
544fn write_remote_call<W: Write>(
545    out: &mut W,
546    indent: &str,
547    def: &Definition,
548    meta: &Metadata,
549) -> io::Result<()> {
550    // Generic functions (e.g. invokeWithLayer<X>) need the type parameter on
551    // the impl header and on the struct name, just like every other write_* helper.
552    let gl_decl = generic_list(def, ": crate::Serializable + crate::Deserializable");
553    let gl_use = generic_list(def, "");
554    writeln!(
555        out,
556        "{indent}impl{gl_decl} crate::RemoteCall for {}{gl_use} {{",
557        n::def_type_name(def)
558    )?;
559    writeln!(
560        out,
561        "{indent}    type Return = {};",
562        n::type_qual_name(&def.ty, meta)
563    )?;
564    writeln!(out, "{indent}}}")
565}
566
567// Enum generation
568
569fn write_enums_mod<W: Write>(
570    defs: &[Definition],
571    config: &Config,
572    meta: &Metadata,
573    out: &mut W,
574) -> io::Result<()> {
575    writeln!(out, "// @generated: do not edit by hand")?;
576    writeln!(out, "pub mod enums {{")?;
577
578    let grouped = grouper::group_types_by_ns(defs);
579    let mut keys: Vec<&Option<String>> = grouped.keys().collect();
580    keys.sort();
581
582    for key in keys {
583        let types = &grouped[key];
584        let indent = if let Some(ns) = key {
585            writeln!(out, "    pub mod {ns} {{")?;
586            "        ".to_owned()
587        } else {
588            "    ".to_owned()
589        };
590
591        for ty in types.iter().filter(|t| !is_builtin(&t.name)) {
592            write_enum(out, &indent, ty, meta, config)?;
593            write_enum_serializable(out, &indent, ty, meta)?;
594            write_enum_deserializable(out, &indent, ty, meta)?;
595            if config.impl_from_type {
596                write_impl_from(out, &indent, ty, meta)?;
597            }
598            if config.impl_from_enum {
599                write_impl_try_from(out, &indent, ty, meta)?;
600            }
601        }
602
603        if key.is_some() {
604            writeln!(out, "    }}")?;
605        }
606    }
607
608    writeln!(out, "}}")
609}
610
611fn write_enum<W: Write>(
612    out: &mut W,
613    indent: &str,
614    ty: &ferogram_tl_parser::tl::Type,
615    meta: &Metadata,
616    config: &Config,
617) -> io::Result<()> {
618    writeln!(
619        out,
620        "\n{indent}/// [`{name}`](https://core.telegram.org/type/{name})",
621        name = n::type_name(ty)
622    )?;
623    if config.impl_debug {
624        writeln!(out, "{indent}#[derive(Debug)]")?;
625    }
626    if config.impl_serde {
627        writeln!(
628            out,
629            "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
630        )?;
631    }
632    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
633    writeln!(out, "{indent}pub enum {} {{", n::type_name(ty))?;
634
635    for def in meta.defs_for_type(ty) {
636        let variant = n::def_variant_name(def);
637        if def.params.is_empty() {
638            writeln!(out, "{indent}    {variant},")?;
639        } else if meta.is_recursive(def) {
640            writeln!(
641                out,
642                "{indent}    {variant}(Box<{}>),",
643                n::def_qual_name(def)
644            )?;
645        } else {
646            writeln!(out, "{indent}    {variant}({}),", n::def_qual_name(def))?;
647        }
648    }
649
650    writeln!(out, "{indent}}}")
651}
652
653fn write_enum_serializable<W: Write>(
654    out: &mut W,
655    indent: &str,
656    ty: &ferogram_tl_parser::tl::Type,
657    meta: &Metadata,
658) -> io::Result<()> {
659    writeln!(
660        out,
661        "{indent}impl crate::Serializable for {} {{",
662        n::type_name(ty)
663    )?;
664    writeln!(
665        out,
666        "{indent}    fn serialize(&self, buf: &mut impl Extend<u8>) {{"
667    )?;
668    writeln!(out, "{indent}        use crate::Identifiable;")?;
669    writeln!(out, "{indent}        match self {{")?;
670
671    for def in meta.defs_for_type(ty) {
672        let variant = n::def_variant_name(def);
673        let bind = if def.params.is_empty() { "" } else { "(x)" };
674        writeln!(out, "{indent}            Self::{variant}{bind} => {{")?;
675        writeln!(
676            out,
677            "{indent}                {}::CONSTRUCTOR_ID.serialize(buf);",
678            n::def_qual_name(def)
679        )?;
680        if !def.params.is_empty() {
681            writeln!(out, "{indent}                x.serialize(buf);")?;
682        }
683        writeln!(out, "{indent}            }}")?;
684    }
685
686    writeln!(out, "{indent}        }}")?;
687    writeln!(out, "{indent}    }}")?;
688    writeln!(out, "{indent}}}")
689}
690
691fn write_enum_deserializable<W: Write>(
692    out: &mut W,
693    indent: &str,
694    ty: &ferogram_tl_parser::tl::Type,
695    meta: &Metadata,
696) -> io::Result<()> {
697    writeln!(
698        out,
699        "{indent}impl crate::Deserializable for {} {{",
700        n::type_name(ty)
701    )?;
702    writeln!(
703        out,
704        "{indent}    fn deserialize(buf: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
705    )?;
706    let enum_name = n::type_name(ty);
707    writeln!(out, "{indent}        use crate::Identifiable;")?;
708    writeln!(out, "{indent}        let id = u32::deserialize(buf)?;")?;
709    writeln!(
710        out,
711        "{indent}        if crate::deserialize::tl_debug() {{ \
712         eprintln!(\"[TL] ENUM  {enum_name}  ctor={{:#010x}}  pos={{}}\", id, buf.pos()); }}"
713    )?;
714    writeln!(out, "{indent}        Ok(match id {{")?;
715
716    for def in meta.defs_for_type(ty) {
717        let variant = n::def_variant_name(def);
718        let qual = n::def_qual_name(def);
719        if def.params.is_empty() {
720            writeln!(
721                out,
722                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant},"
723            )?;
724        } else if meta.is_recursive(def) {
725            writeln!(
726                out,
727                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}(Box::new({qual}::deserialize(buf)?)),"
728            )?;
729        } else {
730            writeln!(
731                out,
732                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}({qual}::deserialize(buf)?),"
733            )?;
734        }
735    }
736
737    writeln!(
738        out,
739        "{indent}            _ => return Err(crate::deserialize::Error::UnexpectedConstructor {{ id }}),"
740    )?;
741    writeln!(out, "{indent}        }})")?;
742    writeln!(out, "{indent}    }}")?;
743    writeln!(out, "{indent}}}")
744}
745
746fn write_impl_from<W: Write>(
747    out: &mut W,
748    indent: &str,
749    ty: &ferogram_tl_parser::tl::Type,
750    meta: &Metadata,
751) -> io::Result<()> {
752    for def in meta.defs_for_type(ty) {
753        let enum_name = n::type_name(ty);
754        let qual = n::def_qual_name(def);
755        let variant = n::def_variant_name(def);
756
757        writeln!(out, "{indent}impl From<{qual}> for {enum_name} {{")?;
758        let underscore = if def.params.is_empty() { "_" } else { "" };
759        writeln!(out, "{indent}    fn from({underscore}x: {qual}) -> Self {{")?;
760        if def.params.is_empty() {
761            writeln!(out, "{indent}        Self::{variant}")?;
762        } else if meta.is_recursive(def) {
763            writeln!(out, "{indent}        Self::{variant}(Box::new(x))")?;
764        } else {
765            writeln!(out, "{indent}        Self::{variant}(x)")?;
766        }
767        writeln!(out, "{indent}    }}")?;
768        writeln!(out, "{indent}}}")?;
769    }
770    Ok(())
771}
772
773fn write_impl_try_from<W: Write>(
774    out: &mut W,
775    indent: &str,
776    ty: &ferogram_tl_parser::tl::Type,
777    meta: &Metadata,
778) -> io::Result<()> {
779    let enum_name = n::type_name(ty);
780    for def in meta.defs_for_type(ty) {
781        if def.params.is_empty() {
782            continue;
783        }
784        let qual = n::def_qual_name(def);
785        let variant = n::def_variant_name(def);
786
787        writeln!(out, "{indent}impl TryFrom<{enum_name}> for {qual} {{")?;
788        writeln!(out, "{indent}    type Error = {enum_name};")?;
789        writeln!(out, "{indent}    #[allow(unreachable_patterns)]")?;
790        writeln!(
791            out,
792            "{indent}    fn try_from(v: {enum_name}) -> Result<Self, Self::Error> {{"
793        )?;
794        writeln!(out, "{indent}        match v {{")?;
795        if meta.is_recursive(def) {
796            writeln!(
797                out,
798                "{indent}            {enum_name}::{variant}(x) => Ok(*x),"
799            )?;
800        } else {
801            writeln!(
802                out,
803                "{indent}            {enum_name}::{variant}(x) => Ok(x),"
804            )?;
805        }
806        writeln!(out, "{indent}            other => Err(other),")?;
807        writeln!(out, "{indent}        }}")?;
808        writeln!(out, "{indent}    }}")?;
809        writeln!(out, "{indent}}}")?;
810    }
811    Ok(())
812}