Skip to main content

layer_tl_gen/
codegen.rs

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