Skip to main content

layer_tl_gen/
codegen.rs

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