Skip to main content

layer_tl_parser/tl/
definition.rs

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