Skip to main content

grammers_tl_parser/tl/
ty.rs

1// Copyright 2020 - developers of the `grammers` project.
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9use std::fmt;
10use std::str::FromStr;
11
12use crate::errors::ParamParseError;
13
14/// The type of a definition or a parameter.
15#[derive(Debug, PartialEq, Eq, Hash)]
16pub struct Type {
17    /// The namespace components of the type.
18    pub namespace: Vec<String>,
19
20    /// The name of the type.
21    pub name: String,
22
23    /// Whether this type is bare or boxed.
24    pub bare: bool,
25
26    /// Whether the type name refers to a generic definition.
27    pub generic_ref: bool,
28
29    /// If the type has a generic argument, which is its type.
30    pub generic_arg: Option<Box<Type>>,
31}
32
33impl fmt::Display for Type {
34    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
35        for ns in self.namespace.iter() {
36            write!(f, "{ns}.")?;
37        }
38        if self.generic_ref {
39            write!(f, "!")?;
40        }
41        write!(f, "{}", self.name)?;
42        if let Some(generic_arg) = &self.generic_arg {
43            write!(f, "<{generic_arg}>")?;
44        }
45        Ok(())
46    }
47}
48
49impl Type {
50    /// Find all the nested generic references in this type, and appends them
51    /// to the input vector.Box
52    pub(crate) fn find_generic_refs<'a>(&'a self, output: &mut Vec<&'a str>) {
53        if self.generic_ref {
54            output.push(&self.name);
55        }
56        if let Some(generic_arg) = &self.generic_arg {
57            generic_arg.find_generic_refs(output);
58        }
59    }
60}
61
62impl FromStr for Type {
63    type Err = ParamParseError;
64
65    /// Parses a type.
66    ///
67    /// # Examples
68    ///
69    /// ```
70    /// use grammers_tl_parser::tl::Type;
71    ///
72    /// assert!("vector<int>".parse::<Type>().is_ok());
73    /// ```
74    fn from_str(ty: &str) -> Result<Self, Self::Err> {
75        // Parse `!type`
76        let (ty, generic_ref) = match ty.strip_prefix('!') {
77            Some(ty) => (ty, true),
78            None => (ty, false),
79        };
80
81        // Parse `type<generic_arg>`
82        let (name, generic_arg) = match ty.split_once('<') {
83            Some((name, generic_arg)) => match generic_arg.strip_suffix('>') {
84                Some(generic_arg) => (name, Some(Box::new(Type::from_str(generic_arg)?))),
85                None => return Err(ParamParseError::InvalidGeneric),
86            },
87            None => (ty, None),
88        };
89
90        // Parse `ns1.ns2.name`
91        let (namespace, name) = match name.rsplit_once('.') {
92            Some((namespace, name)) => (
93                namespace
94                    .split('.')
95                    .map(|part| part.to_owned())
96                    .collect::<Vec<_>>(),
97                name,
98            ),
99            None => (Vec::new(), name),
100        };
101        let (false, Some(first_name_char)) = (
102            namespace.iter().any(|part| part.is_empty()),
103            name.chars().next(),
104        ) else {
105            return Err(ParamParseError::Empty);
106        };
107
108        let bare = first_name_char.is_ascii_lowercase();
109
110        Ok(Self {
111            namespace,
112            name: name.to_owned(),
113            bare,
114            generic_ref,
115            generic_arg,
116        })
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123
124    #[test]
125    fn check_empty_simple() {
126        assert_eq!(Type::from_str(""), Err(ParamParseError::Empty));
127    }
128
129    #[test]
130    fn check_simple() {
131        assert_eq!(
132            Type::from_str("foo"),
133            Ok(Type {
134                namespace: vec![],
135                name: "foo".into(),
136                bare: true,
137                generic_ref: false,
138                generic_arg: None,
139            })
140        );
141    }
142
143    #[test]
144    fn check_empty_namespaced() {
145        assert_eq!(Type::from_str("."), Err(ParamParseError::Empty));
146        assert_eq!(Type::from_str(".."), Err(ParamParseError::Empty));
147        assert_eq!(Type::from_str(".foo"), Err(ParamParseError::Empty));
148        assert_eq!(Type::from_str("foo."), Err(ParamParseError::Empty));
149        assert_eq!(Type::from_str("foo..foo"), Err(ParamParseError::Empty));
150        assert_eq!(Type::from_str(".foo."), Err(ParamParseError::Empty));
151    }
152
153    #[test]
154    fn check_namespaced() {
155        assert_eq!(
156            Type::from_str("foo.bar.baz"),
157            Ok(Type {
158                namespace: vec!["foo".into(), "bar".into()],
159                name: "baz".into(),
160                bare: true,
161                generic_ref: false,
162                generic_arg: None,
163            })
164        );
165    }
166
167    #[test]
168    fn check_bare() {
169        assert!(matches!(Type::from_str("foo"), Ok(Type { bare: true, .. })));
170        assert!(matches!(
171            Type::from_str("Foo"),
172            Ok(Type { bare: false, .. })
173        ));
174        assert!(matches!(
175            Type::from_str("Foo.bar"),
176            Ok(Type { bare: true, .. })
177        ));
178        assert!(matches!(
179            Type::from_str("Foo.Bar"),
180            Ok(Type { bare: false, .. })
181        ));
182        assert!(matches!(
183            Type::from_str("foo.Bar"),
184            Ok(Type { bare: false, .. })
185        ));
186        assert!(matches!(
187            Type::from_str("!bar"),
188            Ok(Type { bare: true, .. })
189        ));
190        assert!(matches!(
191            Type::from_str("!foo.Bar"),
192            Ok(Type { bare: false, .. })
193        ));
194    }
195
196    #[test]
197    fn check_generic_ref() {
198        assert!(matches!(
199            Type::from_str("f"),
200            Ok(Type {
201                generic_ref: false,
202                ..
203            })
204        ));
205        assert!(matches!(
206            Type::from_str("!f"),
207            Ok(Type {
208                generic_ref: true,
209                ..
210            })
211        ));
212        assert!(matches!(
213            Type::from_str("!Foo"),
214            Ok(Type {
215                generic_ref: true,
216                ..
217            })
218        ));
219        assert!(matches!(
220            Type::from_str("!X"),
221            Ok(Type {
222                generic_ref: true,
223                ..
224            })
225        ));
226    }
227
228    #[test]
229    fn check_generic_arg() {
230        assert!(matches!(
231            Type::from_str("foo.bar"),
232            Ok(Type {
233                generic_arg: None,
234                ..
235            })
236        ));
237        assert!(matches!(
238            Type::from_str("foo<bar>"),
239            Ok(Type {
240                generic_arg: Some(x),
241                ..
242            }) if *x == "bar".parse().unwrap(),
243        ));
244        assert!(matches!(
245            Type::from_str("foo<bar.Baz>"),
246            Ok(Type {
247                generic_arg: Some(x),
248                ..
249            }) if *x == "bar.Baz".parse().unwrap(),
250        ));
251        assert!(matches!(
252            Type::from_str("foo<!bar.baz>"),
253            Ok(Type {
254                generic_arg: Some(x),
255                ..
256            }) if *x == "!bar.baz".parse().unwrap(),
257        ));
258        assert!(matches!(
259            Type::from_str("foo<bar<baz>>"),
260            Ok(Type {
261                generic_arg: Some(x),
262                ..
263            }) if *x == "bar<baz>".parse().unwrap(),
264        ));
265    }
266}