Skip to main content

layer_tl_parser/tl/
ty.rs

1use std::fmt;
2use std::str::FromStr;
3
4use crate::errors::ParamParseError;
5
6/// The type of a definition or a parameter, e.g. `ns.Vector<!X>`.
7#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8pub struct Type {
9    /// Namespace components, e.g. `["upload"]` for `upload.File`.
10    pub namespace: Vec<String>,
11
12    /// The bare type name, e.g. `"Vector"`.
13    pub name: String,
14
15    /// `true` when the first letter of the name is lowercase (bare type).
16    pub bare: bool,
17
18    /// `true` when this type is a generic parameter reference (prefixed with `!`).
19    pub generic_ref: bool,
20
21    /// The generic argument, e.g. `long` in `Vector<long>`.
22    pub generic_arg: Option<Box<Type>>,
23}
24
25impl fmt::Display for Type {
26    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
27        for ns in &self.namespace {
28            write!(f, "{ns}.")?;
29        }
30        if self.generic_ref {
31            write!(f, "!")?;
32        }
33        write!(f, "{}", self.name)?;
34        if let Some(arg) = &self.generic_arg {
35            write!(f, "<{arg}>")?;
36        }
37        Ok(())
38    }
39}
40
41impl Type {
42    /// Collect all nested generic references into `output`.
43    pub(crate) fn collect_generic_refs<'a>(&'a self, output: &mut Vec<&'a str>) {
44        if self.generic_ref {
45            output.push(&self.name);
46        }
47        if let Some(arg) = &self.generic_arg {
48            arg.collect_generic_refs(output);
49        }
50    }
51}
52
53impl FromStr for Type {
54    type Err = ParamParseError;
55
56    /// Parses a TL type expression such as `ns.Vector<!X>`.
57    ///
58    /// # Examples
59    /// ```
60    /// use layer_tl_parser::tl::Type;
61    /// assert!("Vector<long>".parse::<Type>().is_ok());
62    /// assert!("!X".parse::<Type>().is_ok());
63    /// ```
64    fn from_str(raw: &str) -> Result<Self, Self::Err> {
65        // Strip leading `!` → generic reference
66        let (raw, generic_ref) = match raw.strip_prefix('!') {
67            Some(r) => (r, true),
68            None => (raw, false),
69        };
70
71        // Split off `<generic_arg>`
72        let (name_part, generic_arg) = match raw.split_once('<') {
73            Some((name, rest)) => match rest.strip_suffix('>') {
74                Some(arg) => (name, Some(Box::new(Type::from_str(arg)?))),
75                None => return Err(ParamParseError::InvalidGeneric),
76            },
77            None => (raw, None),
78        };
79
80        // Split namespace from name
81        let (namespace, name) = match name_part.rsplit_once('.') {
82            Some((ns_part, n)) => (
83                ns_part.split('.').map(String::from).collect::<Vec<_>>(),
84                n,
85            ),
86            None => (Vec::new(), name_part),
87        };
88
89        if namespace.iter().any(|p| p.is_empty()) {
90            return Err(ParamParseError::Empty);
91        }
92
93        let first = name.chars().next().ok_or(ParamParseError::Empty)?;
94        let bare = first.is_ascii_lowercase();
95
96        Ok(Self {
97            namespace,
98            name: name.to_owned(),
99            bare,
100            generic_ref,
101            generic_arg,
102        })
103    }
104}