tdlib_rs_parser/tl/
definition.rs1use std::collections::HashMap;
11use std::fmt;
12use std::str::FromStr;
13
14use crate::errors::{ParamParseError, ParseError};
15use crate::tl::{Category, Parameter, Type};
16
17#[derive(Debug, PartialEq)]
21pub struct Definition {
22 pub name: String,
24
25 pub description: String,
27
28 pub params: Vec<Parameter>,
30
31 pub ty: Type,
33
34 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 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 let (left, ty) = {
104 let mut it = definition.split('=');
105 let ls = it.next().unwrap(); 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 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 let description = docs.remove("description").unwrap_or_default();
129
130 let params = middle
132 .split_whitespace()
133 .map(Parameter::from_str)
134 .filter_map(|p| {
135 let mut result = match p {
136 Ok(p) => Some(Ok(p)),
138
139 Err(ParamParseError::NotImplemented) => Some(Err(ParseError::NotImplemented)),
141
142 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 ¶m.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}