Skip to main content

tdlib_rs_parser/tl/
definition.rs

1// Copyright 2020 - developers of the `grammers` project.
2// Copyright 2022 - developers of the `tdlib-rs` project.
3// Copyright 2024 - developers of the `tgt` and `tdlib-rs` projects.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10use std::collections::HashMap;
11use std::fmt;
12use std::str::FromStr;
13
14use crate::errors::{ParamParseError, ParseError};
15use crate::tl::{Category, Parameter, Type};
16
17/// A [Type Language] definition.
18///
19/// [Type Language]: https://core.telegram.org/mtproto/TL
20#[derive(Debug, PartialEq)]
21pub struct Definition {
22    /// The name of this definition. Also known as "predicate" or "method".
23    pub name: String,
24
25    /// The description of this definition.
26    pub description: String,
27
28    /// A possibly-empty list of parameters this definition has.
29    pub params: Vec<Parameter>,
30
31    /// The type to which this definition belongs to.
32    pub ty: Type,
33
34    /// The category to which this definition belongs to.
35    pub category: Category,
36}
37
38impl fmt::Display for Definition {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        write!(f, "{}", self.name)?;
41
42        for param in self.params.iter() {
43            write!(f, " {param}")?;
44        }
45        write!(f, " = {}", self.ty)?;
46        Ok(())
47    }
48}
49
50impl FromStr for Definition {
51    type Err = ParseError;
52
53    /// Parses a [Type Language] definition.
54    ///
55    /// # Examples
56    ///
57    /// ```
58    /// use tdlib_rs_parser::tl::Definition;
59    ///
60    /// assert!("sendMessage chat_id:int message:string = Message".parse::<Definition>().is_ok());
61    /// ```
62    ///
63    /// [Type Language]: https://core.telegram.org/mtproto/TL
64    fn from_str(definition: &str) -> Result<Self, Self::Err> {
65        if definition.trim().is_empty() {
66            return Err(ParseError::Empty);
67        }
68
69        let (definition, mut docs) = {
70            let mut docs = HashMap::new();
71            let mut comments_end = 0;
72
73            if let Some(start) = definition.rfind("//")
74                && let Some(end) = definition[start..].find('\n')
75            {
76                comments_end = start + end;
77            }
78
79            let mut offset = 0;
80            while let Some(start) = definition[offset..].find('@') {
81                let start = start + offset;
82                let end = if let Some(end) = definition[start + 1..].find('@') {
83                    start + 1 + end
84                } else {
85                    comments_end
86                };
87
88                let comment = definition[start + 1..end].replace("//-", "");
89                let comment = comment.replace("//", "").trim().to_owned();
90                if let Some((name, content)) = comment.split_once(' ') {
91                    docs.insert(name.into(), content.into());
92                } else {
93                    docs.insert(comment, String::new());
94                }
95
96                offset = end;
97            }
98
99            (&definition[comments_end..], docs)
100        };
101
102        // Parse `(left = ty)`
103        let (left, ty) = {
104            let mut it = definition.split('=');
105            let ls = it.next().unwrap(); // split() always return at least one
106            if let Some(t) = it.next() {
107                (ls.trim(), t.trim())
108            } else {
109                return Err(ParseError::MissingType);
110            }
111        };
112
113        let ty = Type::from_str(ty).map_err(|_| ParseError::MissingType)?;
114
115        // Parse `name middle`
116        let (name, middle) = {
117            if let Some(pos) = left.find(' ') {
118                (left[..pos].trim(), left[pos..].trim())
119            } else {
120                (left.trim(), "")
121            }
122        };
123        if name.is_empty() {
124            return Err(ParseError::MissingName);
125        }
126
127        // Parse `description`
128        let description = docs.remove("description").unwrap_or_default();
129
130        // Parse `middle`
131        let params = middle
132            .split_whitespace()
133            .map(Parameter::from_str)
134            .filter_map(|p| {
135                let mut result = match p {
136                    // Any parameter that's okay should just be passed as-is.
137                    Ok(p) => Some(Ok(p)),
138
139                    // Unimplenented parameters are unimplemented definitions.
140                    Err(ParamParseError::NotImplemented) => Some(Err(ParseError::NotImplemented)),
141
142                    // Any error should just become a `ParseError`
143                    Err(x) => Some(Err(ParseError::InvalidParam(x))),
144                };
145
146                if let Some(Ok(ref mut param)) = result {
147                    let name = if param.name == "description" {
148                        "param_description"
149                    } else {
150                        &param.name
151                    };
152
153                    if let Some(description) = docs.remove(name) {
154                        param.description = description;
155                    }
156                }
157
158                result
159            })
160            .collect::<Result<_, ParseError>>()?;
161
162        Ok(Definition {
163            name: name.into(),
164            description,
165            params,
166            ty,
167            category: Category::Types,
168        })
169    }
170}
171
172#[cfg(test)]
173mod tests {
174    use super::*;
175
176    #[test]
177    fn parse_empty_def() {
178        assert_eq!(Definition::from_str(""), Err(ParseError::Empty));
179    }
180
181    #[test]
182    fn parse_no_name() {
183        assert_eq!(Definition::from_str(" = foo"), Err(ParseError::MissingName));
184    }
185
186    #[test]
187    fn parse_no_type() {
188        assert_eq!(Definition::from_str("foo"), Err(ParseError::MissingType));
189        assert_eq!(Definition::from_str("foo = "), Err(ParseError::MissingType));
190    }
191
192    #[test]
193    fn parse_unimplemented() {
194        assert_eq!(
195            Definition::from_str("int ? = Int"),
196            Err(ParseError::NotImplemented)
197        );
198    }
199
200    #[test]
201    fn parse_valid_definition() {
202        let def = Definition::from_str("a=d").unwrap();
203        assert_eq!(def.name, "a");
204        assert_eq!(def.params.len(), 0);
205        assert_eq!(
206            def.ty,
207            Type {
208                name: "d".into(),
209                bare: true,
210                generic_arg: None,
211            }
212        );
213
214        let def = Definition::from_str("a=d<e>").unwrap();
215        assert_eq!(def.name, "a");
216        assert_eq!(def.params.len(), 0);
217        assert_eq!(
218            def.ty,
219            Type {
220                name: "d".into(),
221                bare: true,
222                generic_arg: Some(Box::new("e".parse().unwrap())),
223            }
224        );
225
226        let def = Definition::from_str("a b:c = d").unwrap();
227        assert_eq!(def.name, "a");
228        assert_eq!(def.params.len(), 1);
229        assert_eq!(
230            def.ty,
231            Type {
232                name: "d".into(),
233                bare: true,
234                generic_arg: None,
235            }
236        );
237    }
238
239    #[test]
240    fn parse_multiline_definition() {
241        let def = "
242            first lol:param
243              = t;
244            ";
245
246        assert_eq!(Definition::from_str(def).unwrap().name, "first");
247
248        let def = "
249            second
250              lol:String
251            = t;
252            ";
253
254        assert_eq!(Definition::from_str(def).unwrap().name, "second");
255
256        let def = "
257            third
258
259              lol:String
260
261            =
262                     t;
263            ";
264
265        assert_eq!(Definition::from_str(def).unwrap().name, "third");
266    }
267
268    #[test]
269    fn parse_complete() {
270        let def = "
271            //@description This is a test description
272            name pname:Vector<X> = Type";
273        assert_eq!(
274            Definition::from_str(def),
275            Ok(Definition {
276                name: "name".into(),
277                description: "This is a test description".into(),
278                params: vec![Parameter {
279                    name: "pname".into(),
280                    ty: Type {
281                        name: "Vector".into(),
282                        bare: false,
283                        generic_arg: Some(Box::new(Type {
284                            name: "X".into(),
285                            bare: false,
286                            generic_arg: None,
287                        })),
288                    },
289                    description: String::new(),
290                },],
291                ty: Type {
292                    name: "Type".into(),
293                    bare: false,
294                    generic_arg: None,
295                },
296                category: Category::Types,
297            })
298        );
299    }
300
301    #[test]
302    fn test_to_string() {
303        let def = "name pname:Vector<X> = Type";
304        assert_eq!(Definition::from_str(def).unwrap().to_string(), def);
305    }
306}