Skip to main content

layer_tl_parser/tl/
definition.rs

1use std::fmt;
2use std::str::FromStr;
3
4use crate::errors::{ParamParseError, ParseError};
5use crate::tl::{Category, Flag, Parameter, ParameterType, Type};
6use crate::utils::tl_id;
7
8/// A single TL definition — either a constructor or a function.
9///
10/// For example:
11/// ```text
12/// user#12345 id:long first_name:string = User;
13/// ```
14/// becomes a `Definition` with `name = "user"`, `id = 0x12345`,
15/// `params = [id:long, first_name:string]` and `ty = User`.
16#[derive(Clone, Debug, PartialEq)]
17pub struct Definition {
18    /// Namespace parts.  Empty when the definition is in the global namespace.
19    pub namespace: Vec<String>,
20
21    /// The constructor/method name (e.g. `"user"`, `"messages.sendMessage"`).
22    pub name: String,
23
24    /// 32-bit constructor ID, either parsed from `#XXXXXXXX` or CRC32-derived.
25    pub id: u32,
26
27    /// Ordered list of parameters.
28    pub params: Vec<Parameter>,
29
30    /// The boxed type this definition belongs to (e.g. `User`).
31    pub ty: Type,
32
33    /// Whether this is a data constructor or an RPC function.
34    pub category: Category,
35}
36
37impl Definition {
38    /// Returns `namespace.name` joined with dots.
39    pub fn full_name(&self) -> String {
40        let cap = self.namespace.iter().map(|ns| ns.len() + 1).sum::<usize>() + self.name.len();
41        let mut s = String::with_capacity(cap);
42        for ns in &self.namespace {
43            s.push_str(ns);
44            s.push('.');
45        }
46        s.push_str(&self.name);
47        s
48    }
49}
50
51impl fmt::Display for Definition {
52    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53        for ns in &self.namespace {
54            write!(f, "{ns}.")?;
55        }
56        write!(f, "{}#{:x}", self.name, self.id)?;
57
58        // Emit any `{X:Type}` generic parameter defs that appear in params
59        let mut generics: Vec<&str> = Vec::new();
60        for p in &self.params {
61            if let ParameterType::Normal { ty, .. } = &p.ty {
62                ty.collect_generic_refs(&mut generics);
63            }
64        }
65        generics.sort_unstable();
66        generics.dedup();
67        for g in generics {
68            write!(f, " {{{g}:Type}}")?;
69        }
70
71        for p in &self.params {
72            write!(f, " {p}")?;
73        }
74        write!(f, " = {}", self.ty)
75    }
76}
77
78impl FromStr for Definition {
79    type Err = ParseError;
80
81    fn from_str(raw: &str) -> Result<Self, Self::Err> {
82        let raw = raw.trim();
83        if raw.is_empty() {
84            return Err(ParseError::Empty);
85        }
86
87        // Split at `=`
88        let (lhs, ty_str) = raw.split_once('=').ok_or(ParseError::MissingType)?;
89        let lhs = lhs.trim();
90        let ty_str = ty_str.trim().trim_end_matches(';').trim();
91
92        if ty_str.is_empty() {
93            return Err(ParseError::MissingType);
94        }
95
96        let mut ty = Type::from_str(ty_str).map_err(|_| ParseError::MissingType)?;
97
98        // Split head (name + optional id) from parameter tokens
99        let (head, rest) = match lhs.split_once(|c: char| c.is_whitespace()) {
100            Some((h, r)) => (h.trim_end(), r.trim_start()),
101            None => (lhs, ""),
102        };
103
104        // Parse optional `#id`
105        let (full_name, explicit_id) = match head.split_once('#') {
106            Some((n, id)) => (n, Some(id)),
107            None => (head, None),
108        };
109
110        // Parse namespace
111        let (namespace, name) = match full_name.rsplit_once('.') {
112            Some((ns_part, n)) => (
113                ns_part.split('.').map(String::from).collect::<Vec<_>>(),
114                n,
115            ),
116            None => (Vec::new(), full_name),
117        };
118
119        if namespace.iter().any(|p| p.is_empty()) || name.is_empty() {
120            return Err(ParseError::MissingName);
121        }
122
123        let id = match explicit_id {
124            Some(hex) => u32::from_str_radix(hex.trim(), 16).map_err(ParseError::InvalidId)?,
125            None => tl_id(raw),
126        };
127
128        // Parse parameters
129        let mut type_defs: Vec<String> = Vec::new();
130        let mut flag_defs: Vec<String> = Vec::new();
131
132        let params = rest
133            .split_whitespace()
134            .filter_map(|token| match Parameter::from_str(token) {
135                // `{X:Type}` → record the generic name and skip
136                Err(ParamParseError::TypeDef { name }) => {
137                    type_defs.push(name);
138                    None
139                }
140                Ok(p) => {
141                    match &p {
142                        Parameter { ty: ParameterType::Flags, .. } => {
143                            flag_defs.push(p.name.clone());
144                        }
145                        // Validate generic ref is declared
146                        Parameter {
147                            ty: ParameterType::Normal {
148                                ty: Type { name: tn, generic_ref: true, .. }, ..
149                            }, ..
150                        } if !type_defs.contains(tn) => {
151                            return Some(Err(ParseError::InvalidParam(ParamParseError::MissingDef)));
152                        }
153                        // Validate flag field is declared
154                        Parameter {
155                            ty: ParameterType::Normal {
156                                flag: Some(Flag { name: fn_, .. }), ..
157                            }, ..
158                        } if !flag_defs.contains(fn_) => {
159                            return Some(Err(ParseError::InvalidParam(ParamParseError::MissingDef)));
160                        }
161                        _ => {}
162                    }
163                    Some(Ok(p))
164                }
165                Err(ParamParseError::NotImplemented) => Some(Err(ParseError::NotImplemented)),
166                Err(e) => Some(Err(ParseError::InvalidParam(e))),
167            })
168            .collect::<Result<Vec<_>, ParseError>>()?;
169
170        // If the return type is itself a declared generic, mark it
171        if type_defs.contains(&ty.name) {
172            ty.generic_ref = true;
173        }
174
175        Ok(Definition {
176            namespace,
177            name: name.to_owned(),
178            id,
179            params,
180            ty,
181            category: Category::Types, // caller sets the real category
182        })
183    }
184}