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            && ty.generic_ref && !params.contains(&ty.name.as_str()) {
228            params.push(&ty.name);
229        }
230    }
231    if params.is_empty() {
232        String::new()
233    } else {
234        format!("<{}>", params.join(&format!("{bounds}, ")) + bounds)
235    }
236}
237
238fn write_struct<W: Write>(
239    out: &mut W,
240    indent: &str,
241    def: &Definition,
242    _meta: &Metadata,
243    config: &Config,
244) -> io::Result<()> {
245    let kind = match def.category {
246        Category::Types     => "constructor",
247        Category::Functions => "method",
248    };
249    writeln!(
250        out,
251        "\n{indent}/// [`{name}`](https://core.telegram.org/{kind}/{name})\n\
252         {indent}///\n\
253         {indent}/// Generated from:\n\
254         {indent}/// ```tl\n\
255         {indent}/// {def}\n\
256         {indent}/// ```",
257        name = def.full_name(),
258    )?;
259
260    if config.impl_debug {
261        writeln!(out, "{indent}#[derive(Debug)]")?;
262    }
263    if config.impl_serde {
264        writeln!(out, "{indent}#[derive(serde::Serialize, serde::Deserialize)]")?;
265    }
266    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
267    writeln!(
268        out,
269        "{indent}pub struct {}{} {{",
270        n::def_type_name(def),
271        generic_list(def, ""),
272    )?;
273
274    for param in &def.params {
275        match &param.ty {
276            ParameterType::Flags => {}  // computed on-the-fly
277            ParameterType::Normal { .. } => {
278                writeln!(
279                    out,
280                    "{indent}    pub {}: {},",
281                    n::param_attr_name(param),
282                    n::param_qual_name(param),
283                )?;
284            }
285        }
286    }
287    writeln!(out, "{indent}}}")
288}
289
290fn write_identifiable<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
291    let gl = generic_list(def, "");
292    writeln!(
293        out,
294        "{indent}impl{gl} crate::Identifiable for {}{gl} {{\n\
295         {indent}    const CONSTRUCTOR_ID: u32 = {:#010x};\n\
296         {indent}}}",
297        n::def_type_name(def),
298        def.id,
299    )
300}
301
302fn write_struct_serializable<W: Write>(
303    out: &mut W,
304    indent: &str,
305    def: &Definition,
306    meta: &Metadata,
307) -> io::Result<()> {
308    let gl_decl = generic_list(def, ": crate::Serializable");
309    let gl_use  = generic_list(def, "");
310
311    writeln!(
312        out,
313        "{indent}impl{gl_decl} crate::Serializable for {}{gl_use} {{",
314        n::def_type_name(def)
315    )?;
316
317    let underscore = if def.category == Category::Types && def.params.is_empty() { "_" } else { "" };
318    writeln!(out, "{indent}    fn serialize(&self, {underscore}buf: &mut impl Extend<u8>) {{")?;
319
320    if def.category == Category::Functions {
321        writeln!(out, "{indent}        use crate::Identifiable;")?;
322        writeln!(out, "{indent}        Self::CONSTRUCTOR_ID.serialize(buf);")?;
323    }
324
325    for param in &def.params {
326        write_param_serialization(out, indent, def, meta, param)?;
327    }
328
329    writeln!(out, "{indent}    }}")?;
330    writeln!(out, "{indent}}}")
331}
332
333fn write_param_serialization<W: Write>(
334    out: &mut W,
335    indent: &str,
336    def: &Definition,
337    meta: &Metadata,
338    param: &layer_tl_parser::tl::Parameter,
339) -> io::Result<()> {
340    use ParameterType::*;
341
342    match &param.ty {
343        Flags => {
344            if meta.is_unused_flag(def, param) {
345                writeln!(out, "{indent}        0u32.serialize(buf);")?;
346                return Ok(());
347            }
348            // Compute the flags bitmask from optional params
349            write!(out, "{indent}        (")?;
350            let mut first = true;
351            for other in &def.params {
352                if let Normal { flag: Some(fl), ty, .. } = &other.ty {
353                    if fl.name != param.name { continue; }
354                    if !first { write!(out, " | ")?; }
355                    first = false;
356                    if ty.name == "true" {
357                        write!(out, "if self.{} {{ 1 << {} }} else {{ 0 }}", n::param_attr_name(other), fl.index)?;
358                    } else {
359                        write!(out, "if self.{}.is_some() {{ 1 << {} }} else {{ 0 }}", n::param_attr_name(other), fl.index)?;
360                    }
361                }
362            }
363            if first { write!(out, "0u32")?; }
364            writeln!(out, ").serialize(buf);")?;
365        }
366        Normal { ty, flag } => {
367            let attr = n::param_attr_name(param);
368            if flag.is_some() {
369                if ty.name == "true" {
370                    // bool flag — nothing to serialize, it's in the flags word
371                } else {
372                    writeln!(out, "{indent}        if let Some(v) = &self.{attr} {{ v.serialize(buf); }}")?;
373                }
374            } else {
375                writeln!(out, "{indent}        self.{attr}.serialize(buf);")?;
376            }
377        }
378    }
379    Ok(())
380}
381
382fn write_struct_deserializable<W: Write>(
383    out: &mut W,
384    indent: &str,
385    def: &Definition,
386) -> io::Result<()> {
387    let gl_decl = generic_list(def, ": crate::Deserializable");
388    let gl_use  = generic_list(def, "");
389
390    // Empty structs never read from `buf`. Name it `_buf` to suppress the
391    // unused-variable warning in the generated output.
392    let buf_name = if def.params.is_empty() { "_buf" } else { "buf" };
393
394    writeln!(
395        out,
396        "{indent}impl{gl_decl} crate::Deserializable for {}{gl_use} {{",
397        n::def_type_name(def)
398    )?;
399    writeln!(
400        out,
401        "{indent}    fn deserialize({buf_name}: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
402    )?;
403
404    // Read flags first so optional params can check them
405    let flag_params: Vec<_> = def.params.iter()
406        .filter(|p| p.ty == ParameterType::Flags)
407        .collect();
408
409    for fp in &flag_params {
410        writeln!(
411            out,
412            "{indent}        let _{} = u32::deserialize(buf)?;",
413            n::param_attr_name(fp)
414        )?;
415    }
416
417    // Now deserialize each non-flag param
418    for param in &def.params {
419        if param.ty == ParameterType::Flags {
420            continue; // already done above
421        }
422        if let ParameterType::Normal { ty, flag } = &param.ty {
423            let attr = n::param_attr_name(param);
424            if let Some(fl) = flag {
425                if ty.name == "true" {
426                    writeln!(
427                        out,
428                        "{indent}        let {attr} = (_{} & (1 << {})) != 0;",
429                        fl.name, fl.index
430                    )?;
431                } else {
432                    writeln!(
433                        out,
434                        "{indent}        let {attr} = if (_{} & (1 << {})) != 0 {{ Some({}::deserialize(buf)?) }} else {{ None }};",
435                        fl.name, fl.index, n::type_item_path(ty)
436                    )?;
437                }
438            } else {
439                writeln!(
440                    out,
441                    "{indent}        let {attr} = {}::deserialize(buf)?;",
442                    n::type_item_path(ty)
443                )?;
444            }
445        }
446    }
447
448    writeln!(out, "{indent}        Ok(Self {{")?;
449    for param in &def.params {
450        if param.ty != ParameterType::Flags {
451            let attr = n::param_attr_name(param);
452            writeln!(out, "{indent}            {attr},")?;
453        }
454    }
455    writeln!(out, "{indent}        }})")?;
456    writeln!(out, "{indent}    }}")?;
457    writeln!(out, "{indent}}}")
458}
459
460fn write_remote_call<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
461    // Generic functions (e.g. invokeWithLayer<X>) need the type parameter on
462    // the impl header and on the struct name, just like every other write_* helper.
463    let gl_decl = generic_list(def, ": crate::Serializable + crate::Deserializable");
464    let gl_use  = generic_list(def, "");
465    writeln!(
466        out,
467        "{indent}impl{gl_decl} crate::RemoteCall for {}{gl_use} {{",
468        n::def_type_name(def)
469    )?;
470    writeln!(
471        out,
472        "{indent}    type Return = {};",
473        n::type_qual_name(&def.ty)
474    )?;
475    writeln!(out, "{indent}}}")
476}
477
478// ─── Enum generation ──────────────────────────────────────────────────────────
479
480fn write_enums_mod<W: Write>(
481    defs: &[Definition],
482    config: &Config,
483    meta: &Metadata,
484    out: &mut W,
485) -> io::Result<()> {
486    writeln!(out, "// @generated — do not edit by hand")?;
487    writeln!(out, "pub mod enums {{")?;
488
489    let grouped = grouper::group_types_by_ns(defs);
490    let mut keys: Vec<&Option<String>> = grouped.keys().collect();
491    keys.sort();
492
493    for key in keys {
494        let types = &grouped[key];
495        let indent = if let Some(ns) = key {
496            writeln!(out, "    pub mod {ns} {{")?;
497            "        ".to_owned()
498        } else {
499            "    ".to_owned()
500        };
501
502        for ty in types.iter().filter(|t| !is_builtin(&t.name)) {
503            write_enum(out, &indent, ty, meta, config)?;
504            write_enum_serializable(out, &indent, ty, meta)?;
505            write_enum_deserializable(out, &indent, ty, meta)?;
506            if config.impl_from_type {
507                write_impl_from(out, &indent, ty, meta)?;
508            }
509            if config.impl_from_enum {
510                write_impl_try_from(out, &indent, ty, meta)?;
511            }
512        }
513
514        if key.is_some() {
515            writeln!(out, "    }}")?;
516        }
517    }
518
519    writeln!(out, "}}")
520}
521
522fn write_enum<W: Write>(
523    out: &mut W,
524    indent: &str,
525    ty: &layer_tl_parser::tl::Type,
526    meta: &Metadata,
527    config: &Config,
528) -> io::Result<()> {
529    writeln!(
530        out,
531        "\n{indent}/// [`{name}`](https://core.telegram.org/type/{name})",
532        name = n::type_name(ty)
533    )?;
534    if config.impl_debug {
535        writeln!(out, "{indent}#[derive(Debug)]")?;
536    }
537    if config.impl_serde {
538        writeln!(out, "{indent}#[derive(serde::Serialize, serde::Deserialize)]")?;
539    }
540    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
541    writeln!(out, "{indent}pub enum {} {{", n::type_name(ty))?;
542
543    for def in meta.defs_for_type(ty) {
544        let variant = n::def_variant_name(def);
545        if def.params.is_empty() {
546            writeln!(out, "{indent}    {variant},")?;
547        } else if meta.is_recursive(def) {
548            writeln!(out, "{indent}    {variant}(Box<{}>),", n::def_qual_name(def))?;
549        } else {
550            writeln!(out, "{indent}    {variant}({})," , n::def_qual_name(def))?;
551        }
552    }
553
554    writeln!(out, "{indent}}}")
555}
556
557fn write_enum_serializable<W: Write>(
558    out: &mut W,
559    indent: &str,
560    ty: &layer_tl_parser::tl::Type,
561    meta: &Metadata,
562) -> io::Result<()> {
563    writeln!(
564        out,
565        "{indent}impl crate::Serializable for {} {{",
566        n::type_name(ty)
567    )?;
568    writeln!(out, "{indent}    fn serialize(&self, buf: &mut impl Extend<u8>) {{")?;
569    writeln!(out, "{indent}        use crate::Identifiable;")?;
570    writeln!(out, "{indent}        match self {{")?;
571
572    for def in meta.defs_for_type(ty) {
573        let variant = n::def_variant_name(def);
574        let bind = if def.params.is_empty() { "" } else { "(x)" };
575        writeln!(out, "{indent}            Self::{variant}{bind} => {{")?;
576        writeln!(out, "{indent}                {}::CONSTRUCTOR_ID.serialize(buf);", n::def_qual_name(def))?;
577        if !def.params.is_empty() {
578            writeln!(out, "{indent}                x.serialize(buf);")?;
579        }
580        writeln!(out, "{indent}            }}")?;
581    }
582
583    writeln!(out, "{indent}        }}")?;
584    writeln!(out, "{indent}    }}")?;
585    writeln!(out, "{indent}}}")
586}
587
588fn write_enum_deserializable<W: Write>(
589    out: &mut W,
590    indent: &str,
591    ty: &layer_tl_parser::tl::Type,
592    meta: &Metadata,
593) -> io::Result<()> {
594    writeln!(
595        out,
596        "{indent}impl crate::Deserializable for {} {{",
597        n::type_name(ty)
598    )?;
599    writeln!(
600        out,
601        "{indent}    fn deserialize(buf: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
602    )?;
603    writeln!(out, "{indent}        use crate::Identifiable;")?;
604    writeln!(out, "{indent}        let id = u32::deserialize(buf)?;")?;
605    writeln!(out, "{indent}        Ok(match id {{")?;
606
607    for def in meta.defs_for_type(ty) {
608        let variant = n::def_variant_name(def);
609        let qual    = n::def_qual_name(def);
610        if def.params.is_empty() {
611            writeln!(out, "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant},")?;
612        } else if meta.is_recursive(def) {
613            writeln!(out, "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}(Box::new({qual}::deserialize(buf)?)),")?;
614        } else {
615            writeln!(out, "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}({qual}::deserialize(buf)?),")?;
616        }
617    }
618
619    writeln!(
620        out,
621        "{indent}            _ => return Err(crate::deserialize::Error::UnexpectedConstructor {{ id }}),"
622    )?;
623    writeln!(out, "{indent}        }})")?;
624    writeln!(out, "{indent}    }}")?;
625    writeln!(out, "{indent}}}")
626}
627
628fn write_impl_from<W: Write>(
629    out: &mut W,
630    indent: &str,
631    ty: &layer_tl_parser::tl::Type,
632    meta: &Metadata,
633) -> io::Result<()> {
634    for def in meta.defs_for_type(ty) {
635        let enum_name = n::type_name(ty);
636        let qual      = n::def_qual_name(def);
637        let variant   = n::def_variant_name(def);
638
639        writeln!(out, "{indent}impl From<{qual}> for {enum_name} {{")?;
640        let underscore = if def.params.is_empty() { "_" } else { "" };
641        writeln!(out, "{indent}    fn from({underscore}x: {qual}) -> Self {{")?;
642        if def.params.is_empty() {
643            writeln!(out, "{indent}        Self::{variant}")?;
644        } else if meta.is_recursive(def) {
645            writeln!(out, "{indent}        Self::{variant}(Box::new(x))")?;
646        } else {
647            writeln!(out, "{indent}        Self::{variant}(x)")?;
648        }
649        writeln!(out, "{indent}    }}")?;
650        writeln!(out, "{indent}}}")?;
651    }
652    Ok(())
653}
654
655fn write_impl_try_from<W: Write>(
656    out: &mut W,
657    indent: &str,
658    ty: &layer_tl_parser::tl::Type,
659    meta: &Metadata,
660) -> io::Result<()> {
661    let enum_name = n::type_name(ty);
662    for def in meta.defs_for_type(ty) {
663        if def.params.is_empty() { continue; }
664        let qual    = n::def_qual_name(def);
665        let variant = n::def_variant_name(def);
666
667        writeln!(out, "{indent}impl TryFrom<{enum_name}> for {qual} {{")?;
668        writeln!(out, "{indent}    type Error = {enum_name};")?;
669        writeln!(out, "{indent}    #[allow(unreachable_patterns)]")?;
670        writeln!(out, "{indent}    fn try_from(v: {enum_name}) -> Result<Self, Self::Error> {{")?;
671        writeln!(out, "{indent}        match v {{")?;
672        if meta.is_recursive(def) {
673            writeln!(out, "{indent}            {enum_name}::{variant}(x) => Ok(*x),")?;
674        } else {
675            writeln!(out, "{indent}            {enum_name}::{variant}(x) => Ok(x),")?;
676        }
677        writeln!(out, "{indent}            other => Err(other),")?;
678        writeln!(out, "{indent}        }}")?;
679        writeln!(out, "{indent}    }}")?;
680        writeln!(out, "{indent}}}")?;
681    }
682    Ok(())
683}