Skip to main content

layer_tl_gen/
codegen.rs

1//! The public code-generation API.
2
3use std::fs::File;
4use std::io::{self, Write};
5use std::path::Path;
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!(
125            out,
126            "pub fn name_for_id(id: u32) -> Option<&'static str> {{"
127        )?;
128        writeln!(out, "    match id {{")?;
129        for def in defs {
130            writeln!(
131                out,
132                "        {:#010x} => Some(\"{}\"),",
133                def.id,
134                def.full_name()
135            )?;
136        }
137        writeln!(out, "        _ => None,")?;
138        writeln!(out, "    }}")?;
139        writeln!(out, "}}")?;
140    }
141
142    Ok(())
143}
144
145// Struct generation (types + functions)
146
147fn write_types_mod<W: Write>(
148    defs: &[Definition],
149    config: &Config,
150    meta: &Metadata,
151    out: &mut W,
152) -> io::Result<()> {
153    writeln!(out, "// @generated: do not edit by hand")?;
154    writeln!(out, "pub mod types {{")?;
155
156    let grouped = grouper::group_by_ns(defs, Category::Types);
157    let mut namespaces: Vec<&String> = grouped.keys().collect();
158    namespaces.sort();
159
160    for ns in namespaces {
161        let bucket = &grouped[ns];
162        let indent = if ns.is_empty() {
163            "    ".to_owned()
164        } else {
165            writeln!(out, "    pub mod {ns} {{")?;
166            "        ".to_owned()
167        };
168
169        for def in bucket {
170            write_struct(out, &indent, def, meta, config)?;
171            write_identifiable(out, &indent, def)?;
172            write_struct_serializable(out, &indent, def, meta)?;
173            write_struct_deserializable(out, &indent, def)?;
174        }
175
176        if !ns.is_empty() {
177            writeln!(out, "    }}")?;
178        }
179    }
180
181    writeln!(out, "}}")
182}
183
184fn write_functions_mod<W: Write>(
185    defs: &[Definition],
186    config: &Config,
187    meta: &Metadata,
188    out: &mut W,
189) -> io::Result<()> {
190    writeln!(out, "// @generated: do not edit by hand")?;
191    writeln!(out, "pub mod functions {{")?;
192
193    let grouped = grouper::group_by_ns(defs, Category::Functions);
194    let mut namespaces: Vec<&String> = grouped.keys().collect();
195    namespaces.sort();
196
197    for ns in namespaces {
198        let bucket = &grouped[ns];
199        let indent = if ns.is_empty() {
200            "    ".to_owned()
201        } else {
202            writeln!(out, "    pub mod {ns} {{")?;
203            "        ".to_owned()
204        };
205
206        for def in bucket {
207            write_struct(out, &indent, def, meta, config)?;
208            write_identifiable(out, &indent, def)?;
209            write_struct_serializable(out, &indent, def, meta)?;
210            if config.deserializable_functions {
211                write_struct_deserializable(out, &indent, def)?;
212            }
213            write_remote_call(out, &indent, def)?;
214        }
215
216        if !ns.is_empty() {
217            writeln!(out, "    }}")?;
218        }
219    }
220
221    writeln!(out, "}}")
222}
223
224// Struct pieces
225
226fn generic_list(def: &Definition, bounds: &str) -> String {
227    let mut params: Vec<&str> = Vec::new();
228    for p in &def.params {
229        if let ParameterType::Normal { ty, .. } = &p.ty
230            && ty.generic_ref
231            && !params.contains(&ty.name.as_str())
232        {
233            params.push(&ty.name);
234        }
235    }
236    if params.is_empty() {
237        String::new()
238    } else {
239        format!("<{}>", params.join(&format!("{bounds}, ")) + bounds)
240    }
241}
242
243fn write_struct<W: Write>(
244    out: &mut W,
245    indent: &str,
246    def: &Definition,
247    _meta: &Metadata,
248    config: &Config,
249) -> io::Result<()> {
250    let kind = match def.category {
251        Category::Types => "constructor",
252        Category::Functions => "method",
253    };
254    writeln!(
255        out,
256        "\n{indent}/// [`{name}`](https://core.telegram.org/{kind}/{name})\n\
257         {indent}///\n\
258         {indent}/// Generated from:\n\
259         {indent}/// ```tl\n\
260         {indent}/// {def}\n\
261         {indent}/// ```",
262        name = def.full_name(),
263    )?;
264
265    if config.impl_debug {
266        writeln!(out, "{indent}#[derive(Debug)]")?;
267    }
268    if config.impl_serde {
269        writeln!(
270            out,
271            "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
272        )?;
273    }
274    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
275    writeln!(
276        out,
277        "{indent}pub struct {}{} {{",
278        n::def_type_name(def),
279        generic_list(def, ""),
280    )?;
281
282    for param in &def.params {
283        match &param.ty {
284            ParameterType::Flags => {} // computed on-the-fly
285            ParameterType::Normal { .. } => {
286                writeln!(
287                    out,
288                    "{indent}    pub {}: {},",
289                    n::param_attr_name(param),
290                    n::param_qual_name(param),
291                )?;
292            }
293        }
294    }
295    writeln!(out, "{indent}}}")
296}
297
298fn write_identifiable<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
299    let gl = generic_list(def, "");
300    writeln!(
301        out,
302        "{indent}impl{gl} crate::Identifiable for {}{gl} {{\n\
303         {indent}    const CONSTRUCTOR_ID: u32 = {:#010x};\n\
304         {indent}}}",
305        n::def_type_name(def),
306        def.id,
307    )
308}
309
310fn write_struct_serializable<W: Write>(
311    out: &mut W,
312    indent: &str,
313    def: &Definition,
314    meta: &Metadata,
315) -> io::Result<()> {
316    let gl_decl = generic_list(def, ": crate::Serializable");
317    let gl_use = generic_list(def, "");
318
319    writeln!(
320        out,
321        "{indent}impl{gl_decl} crate::Serializable for {}{gl_use} {{",
322        n::def_type_name(def)
323    )?;
324
325    let underscore = if def.category == Category::Types && def.params.is_empty() {
326        "_"
327    } else {
328        ""
329    };
330    writeln!(
331        out,
332        "{indent}    fn serialize(&self, {underscore}buf: &mut impl Extend<u8>) {{"
333    )?;
334
335    if def.category == Category::Functions {
336        writeln!(out, "{indent}        use crate::Identifiable;")?;
337        writeln!(out, "{indent}        Self::CONSTRUCTOR_ID.serialize(buf);")?;
338    }
339
340    for param in &def.params {
341        write_param_serialization(out, indent, def, meta, param)?;
342    }
343
344    writeln!(out, "{indent}    }}")?;
345    writeln!(out, "{indent}}}")
346}
347
348fn write_param_serialization<W: Write>(
349    out: &mut W,
350    indent: &str,
351    def: &Definition,
352    meta: &Metadata,
353    param: &layer_tl_parser::tl::Parameter,
354) -> io::Result<()> {
355    use ParameterType::*;
356
357    match &param.ty {
358        Flags => {
359            if meta.is_unused_flag(def, param) {
360                writeln!(out, "{indent}        0u32.serialize(buf);")?;
361                return Ok(());
362            }
363            // Compute the flags bitmask from optional params
364            write!(out, "{indent}        (")?;
365            let mut first = true;
366            for other in &def.params {
367                if let Normal {
368                    flag: Some(fl), ty, ..
369                } = &other.ty
370                {
371                    if fl.name != param.name {
372                        continue;
373                    }
374                    if !first {
375                        write!(out, " | ")?;
376                    }
377                    first = false;
378                    if ty.name == "true" {
379                        write!(
380                            out,
381                            "if self.{} {{ 1 << {} }} else {{ 0 }}",
382                            n::param_attr_name(other),
383                            fl.index
384                        )?;
385                    } else {
386                        write!(
387                            out,
388                            "if self.{}.is_some() {{ 1 << {} }} else {{ 0 }}",
389                            n::param_attr_name(other),
390                            fl.index
391                        )?;
392                    }
393                }
394            }
395            if first {
396                write!(out, "0u32")?;
397            }
398            writeln!(out, ").serialize(buf);")?;
399        }
400        Normal { ty, flag } => {
401            let attr = n::param_attr_name(param);
402            if flag.is_some() {
403                if ty.name == "true" {
404                    // bool flag: nothing to serialize, it's in the flags word
405                } else {
406                    writeln!(
407                        out,
408                        "{indent}        if let Some(v) = &self.{attr} {{ v.serialize(buf); }}"
409                    )?;
410                }
411            } else {
412                writeln!(out, "{indent}        self.{attr}.serialize(buf);")?;
413            }
414        }
415    }
416    Ok(())
417}
418
419fn write_struct_deserializable<W: Write>(
420    out: &mut W,
421    indent: &str,
422    def: &Definition,
423) -> io::Result<()> {
424    let gl_decl = generic_list(def, ": crate::Deserializable");
425    let gl_use = generic_list(def, "");
426
427    // Empty structs never read from `buf`. Name it `_buf` to suppress the
428    // unused-variable warning in the generated output.
429    let buf_name = if def.params.is_empty() { "_buf" } else { "buf" };
430
431    writeln!(
432        out,
433        "{indent}impl{gl_decl} crate::Deserializable for {}{gl_use} {{",
434        n::def_type_name(def)
435    )?;
436    writeln!(
437        out,
438        "{indent}    fn deserialize({buf_name}: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
439    )?;
440
441    // Read flags first so optional params can check them
442    let flag_params: Vec<_> = def
443        .params
444        .iter()
445        .filter(|p| p.ty == ParameterType::Flags)
446        .collect();
447
448    for fp in &flag_params {
449        writeln!(
450            out,
451            "{indent}        let _{} = u32::deserialize(buf)?;",
452            n::param_attr_name(fp)
453        )?;
454    }
455
456    // Now deserialize each non-flag param
457    for param in &def.params {
458        if param.ty == ParameterType::Flags {
459            continue; // already done above
460        }
461        if let ParameterType::Normal { ty, flag } = &param.ty {
462            let attr = n::param_attr_name(param);
463            if let Some(fl) = flag {
464                if ty.name == "true" {
465                    writeln!(
466                        out,
467                        "{indent}        let {attr} = (_{} & (1 << {})) != 0;",
468                        fl.name, fl.index
469                    )?;
470                } else {
471                    writeln!(
472                        out,
473                        "{indent}        let {attr} = if (_{} & (1 << {})) != 0 {{ Some({}::deserialize(buf)?) }} else {{ None }};",
474                        fl.name,
475                        fl.index,
476                        n::type_item_path(ty)
477                    )?;
478                }
479            } else {
480                writeln!(
481                    out,
482                    "{indent}        let {attr} = {}::deserialize(buf)?;",
483                    n::type_item_path(ty)
484                )?;
485            }
486        }
487    }
488
489    writeln!(out, "{indent}        Ok(Self {{")?;
490    for param in &def.params {
491        if param.ty != ParameterType::Flags {
492            let attr = n::param_attr_name(param);
493            writeln!(out, "{indent}            {attr},")?;
494        }
495    }
496    writeln!(out, "{indent}        }})")?;
497    writeln!(out, "{indent}    }}")?;
498    writeln!(out, "{indent}}}")
499}
500
501fn write_remote_call<W: Write>(out: &mut W, indent: &str, def: &Definition) -> io::Result<()> {
502    // Generic functions (e.g. invokeWithLayer<X>) need the type parameter on
503    // the impl header and on the struct name, just like every other write_* helper.
504    let gl_decl = generic_list(def, ": crate::Serializable + crate::Deserializable");
505    let gl_use = generic_list(def, "");
506    writeln!(
507        out,
508        "{indent}impl{gl_decl} crate::RemoteCall for {}{gl_use} {{",
509        n::def_type_name(def)
510    )?;
511    writeln!(
512        out,
513        "{indent}    type Return = {};",
514        n::type_qual_name(&def.ty)
515    )?;
516    writeln!(out, "{indent}}}")
517}
518
519// Enum generation
520
521fn write_enums_mod<W: Write>(
522    defs: &[Definition],
523    config: &Config,
524    meta: &Metadata,
525    out: &mut W,
526) -> io::Result<()> {
527    writeln!(out, "// @generated: do not edit by hand")?;
528    writeln!(out, "pub mod enums {{")?;
529
530    let grouped = grouper::group_types_by_ns(defs);
531    let mut keys: Vec<&Option<String>> = grouped.keys().collect();
532    keys.sort();
533
534    for key in keys {
535        let types = &grouped[key];
536        let indent = if let Some(ns) = key {
537            writeln!(out, "    pub mod {ns} {{")?;
538            "        ".to_owned()
539        } else {
540            "    ".to_owned()
541        };
542
543        for ty in types.iter().filter(|t| !is_builtin(&t.name)) {
544            write_enum(out, &indent, ty, meta, config)?;
545            write_enum_serializable(out, &indent, ty, meta)?;
546            write_enum_deserializable(out, &indent, ty, meta)?;
547            if config.impl_from_type {
548                write_impl_from(out, &indent, ty, meta)?;
549            }
550            if config.impl_from_enum {
551                write_impl_try_from(out, &indent, ty, meta)?;
552            }
553        }
554
555        if key.is_some() {
556            writeln!(out, "    }}")?;
557        }
558    }
559
560    writeln!(out, "}}")
561}
562
563fn write_enum<W: Write>(
564    out: &mut W,
565    indent: &str,
566    ty: &layer_tl_parser::tl::Type,
567    meta: &Metadata,
568    config: &Config,
569) -> io::Result<()> {
570    writeln!(
571        out,
572        "\n{indent}/// [`{name}`](https://core.telegram.org/type/{name})",
573        name = n::type_name(ty)
574    )?;
575    if config.impl_debug {
576        writeln!(out, "{indent}#[derive(Debug)]")?;
577    }
578    if config.impl_serde {
579        writeln!(
580            out,
581            "{indent}#[derive(serde::Serialize, serde::Deserialize)]"
582        )?;
583    }
584    writeln!(out, "{indent}#[derive(Clone, PartialEq)]")?;
585    writeln!(out, "{indent}pub enum {} {{", n::type_name(ty))?;
586
587    for def in meta.defs_for_type(ty) {
588        let variant = n::def_variant_name(def);
589        if def.params.is_empty() {
590            writeln!(out, "{indent}    {variant},")?;
591        } else if meta.is_recursive(def) {
592            writeln!(
593                out,
594                "{indent}    {variant}(Box<{}>),",
595                n::def_qual_name(def)
596            )?;
597        } else {
598            writeln!(out, "{indent}    {variant}({}),", n::def_qual_name(def))?;
599        }
600    }
601
602    writeln!(out, "{indent}}}")
603}
604
605fn write_enum_serializable<W: Write>(
606    out: &mut W,
607    indent: &str,
608    ty: &layer_tl_parser::tl::Type,
609    meta: &Metadata,
610) -> io::Result<()> {
611    writeln!(
612        out,
613        "{indent}impl crate::Serializable for {} {{",
614        n::type_name(ty)
615    )?;
616    writeln!(
617        out,
618        "{indent}    fn serialize(&self, buf: &mut impl Extend<u8>) {{"
619    )?;
620    writeln!(out, "{indent}        use crate::Identifiable;")?;
621    writeln!(out, "{indent}        match self {{")?;
622
623    for def in meta.defs_for_type(ty) {
624        let variant = n::def_variant_name(def);
625        let bind = if def.params.is_empty() { "" } else { "(x)" };
626        writeln!(out, "{indent}            Self::{variant}{bind} => {{")?;
627        writeln!(
628            out,
629            "{indent}                {}::CONSTRUCTOR_ID.serialize(buf);",
630            n::def_qual_name(def)
631        )?;
632        if !def.params.is_empty() {
633            writeln!(out, "{indent}                x.serialize(buf);")?;
634        }
635        writeln!(out, "{indent}            }}")?;
636    }
637
638    writeln!(out, "{indent}        }}")?;
639    writeln!(out, "{indent}    }}")?;
640    writeln!(out, "{indent}}}")
641}
642
643fn write_enum_deserializable<W: Write>(
644    out: &mut W,
645    indent: &str,
646    ty: &layer_tl_parser::tl::Type,
647    meta: &Metadata,
648) -> io::Result<()> {
649    writeln!(
650        out,
651        "{indent}impl crate::Deserializable for {} {{",
652        n::type_name(ty)
653    )?;
654    writeln!(
655        out,
656        "{indent}    fn deserialize(buf: crate::deserialize::Buffer) -> crate::deserialize::Result<Self> {{"
657    )?;
658    writeln!(out, "{indent}        use crate::Identifiable;")?;
659    writeln!(out, "{indent}        let id = u32::deserialize(buf)?;")?;
660    writeln!(out, "{indent}        Ok(match id {{")?;
661
662    for def in meta.defs_for_type(ty) {
663        let variant = n::def_variant_name(def);
664        let qual = n::def_qual_name(def);
665        if def.params.is_empty() {
666            writeln!(
667                out,
668                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant},"
669            )?;
670        } else if meta.is_recursive(def) {
671            writeln!(
672                out,
673                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}(Box::new({qual}::deserialize(buf)?)),"
674            )?;
675        } else {
676            writeln!(
677                out,
678                "{indent}            {qual}::CONSTRUCTOR_ID => Self::{variant}({qual}::deserialize(buf)?),"
679            )?;
680        }
681    }
682
683    writeln!(
684        out,
685        "{indent}            _ => return Err(crate::deserialize::Error::UnexpectedConstructor {{ id }}),"
686    )?;
687    writeln!(out, "{indent}        }})")?;
688    writeln!(out, "{indent}    }}")?;
689    writeln!(out, "{indent}}}")
690}
691
692fn write_impl_from<W: Write>(
693    out: &mut W,
694    indent: &str,
695    ty: &layer_tl_parser::tl::Type,
696    meta: &Metadata,
697) -> io::Result<()> {
698    for def in meta.defs_for_type(ty) {
699        let enum_name = n::type_name(ty);
700        let qual = n::def_qual_name(def);
701        let variant = n::def_variant_name(def);
702
703        writeln!(out, "{indent}impl From<{qual}> for {enum_name} {{")?;
704        let underscore = if def.params.is_empty() { "_" } else { "" };
705        writeln!(out, "{indent}    fn from({underscore}x: {qual}) -> Self {{")?;
706        if def.params.is_empty() {
707            writeln!(out, "{indent}        Self::{variant}")?;
708        } else if meta.is_recursive(def) {
709            writeln!(out, "{indent}        Self::{variant}(Box::new(x))")?;
710        } else {
711            writeln!(out, "{indent}        Self::{variant}(x)")?;
712        }
713        writeln!(out, "{indent}    }}")?;
714        writeln!(out, "{indent}}}")?;
715    }
716    Ok(())
717}
718
719fn write_impl_try_from<W: Write>(
720    out: &mut W,
721    indent: &str,
722    ty: &layer_tl_parser::tl::Type,
723    meta: &Metadata,
724) -> io::Result<()> {
725    let enum_name = n::type_name(ty);
726    for def in meta.defs_for_type(ty) {
727        if def.params.is_empty() {
728            continue;
729        }
730        let qual = n::def_qual_name(def);
731        let variant = n::def_variant_name(def);
732
733        writeln!(out, "{indent}impl TryFrom<{enum_name}> for {qual} {{")?;
734        writeln!(out, "{indent}    type Error = {enum_name};")?;
735        writeln!(out, "{indent}    #[allow(unreachable_patterns)]")?;
736        writeln!(
737            out,
738            "{indent}    fn try_from(v: {enum_name}) -> Result<Self, Self::Error> {{"
739        )?;
740        writeln!(out, "{indent}        match v {{")?;
741        if meta.is_recursive(def) {
742            writeln!(
743                out,
744                "{indent}            {enum_name}::{variant}(x) => Ok(*x),"
745            )?;
746        } else {
747            writeln!(
748                out,
749                "{indent}            {enum_name}::{variant}(x) => Ok(x),"
750            )?;
751        }
752        writeln!(out, "{indent}            other => Err(other),")?;
753        writeln!(out, "{indent}        }}")?;
754        writeln!(out, "{indent}    }}")?;
755        writeln!(out, "{indent}}}")?;
756    }
757    Ok(())
758}