Skip to main content

tdlib_rs_parser/tl/
ty.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::fmt;
11use std::str::FromStr;
12
13use crate::errors::ParamParseError;
14
15/// The type of a definition or a parameter.
16#[derive(Debug, PartialEq)]
17pub struct Type {
18    /// The name of the type.
19    pub name: String,
20
21    /// Whether this type is bare or boxed.
22    pub bare: bool,
23
24    /// If the type has a generic argument, which is its type.
25    pub generic_arg: Option<Box<Type>>,
26}
27
28impl fmt::Display for Type {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        write!(f, "{}", self.name)?;
31        if let Some(generic_arg) = &self.generic_arg {
32            write!(f, "<{generic_arg}>")?;
33        }
34        Ok(())
35    }
36}
37
38impl FromStr for Type {
39    type Err = ParamParseError;
40
41    /// Parses a type.
42    ///
43    /// # Examples
44    ///
45    /// ```
46    /// use tdlib_rs_parser::tl::Type;
47    ///
48    /// assert!("vector<int>".parse::<Type>().is_ok());
49    /// ```
50    fn from_str(ty: &str) -> Result<Self, Self::Err> {
51        // Parse `type<generic_arg>`
52        let (ty, generic_arg) = if let Some(pos) = ty.find('<') {
53            if !ty.ends_with('>') {
54                return Err(ParamParseError::InvalidGeneric);
55            }
56            (
57                &ty[..pos],
58                Some(Box::new(Type::from_str(&ty[pos + 1..ty.len() - 1])?)),
59            )
60        } else {
61            (ty, None)
62        };
63
64        if ty.is_empty() {
65            return Err(ParamParseError::Empty);
66        }
67
68        // Safe to unwrap because we just checked is not empty
69        let bare = ty.chars().next().unwrap().is_ascii_lowercase();
70
71        Ok(Self {
72            name: ty.into(),
73            bare,
74            generic_arg,
75        })
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82
83    #[test]
84    fn check_empty_simple() {
85        assert_eq!(Type::from_str(""), Err(ParamParseError::Empty));
86    }
87
88    #[test]
89    fn check_simple() {
90        assert_eq!(
91            Type::from_str("foo"),
92            Ok(Type {
93                name: "foo".into(),
94                bare: true,
95                generic_arg: None,
96            })
97        );
98    }
99
100    #[test]
101    fn check_empty() {
102        assert_eq!(Type::from_str(""), Err(ParamParseError::Empty));
103    }
104
105    #[test]
106    fn check_bare() {
107        assert!(matches!(Type::from_str("foo"), Ok(Type { bare: true, .. })));
108        assert!(matches!(
109            Type::from_str("Foo"),
110            Ok(Type { bare: false, .. })
111        ));
112    }
113
114    #[test]
115    fn check_generic_arg() {
116        assert!(matches!(
117            Type::from_str("foo"),
118            Ok(Type {
119                generic_arg: None,
120                ..
121            })
122        ));
123        assert!(match Type::from_str("foo<bar>") {
124            Ok(Type {
125                generic_arg: Some(x),
126                ..
127            }) => *x == "bar".parse().unwrap(),
128            _ => false,
129        });
130        assert!(match Type::from_str("foo<bar>") {
131            Ok(Type {
132                generic_arg: Some(x),
133                ..
134            }) => *x == "bar".parse().unwrap(),
135            _ => false,
136        });
137        assert!(match Type::from_str("foo<bar<baz>>") {
138            Ok(Type {
139                generic_arg: Some(x),
140                ..
141            }) => *x == "bar<baz>".parse().unwrap(),
142            _ => false,
143        });
144    }
145}