Skip to main content

ferogram_tl_parser/tl/
definition.rs

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