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}