Skip to main content

ferogram_tl_parser/tl/
definition.rs

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