1use std::fmt;
2use std::str::FromStr;
3
4use crate::errors::ParamParseError;
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8pub struct Type {
9 pub namespace: Vec<String>,
11
12 pub name: String,
14
15 pub bare: bool,
17
18 pub generic_ref: bool,
20
21 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 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 fn from_str(raw: &str) -> Result<Self, Self::Err> {
65 let (raw, generic_ref) = match raw.strip_prefix('!') {
67 Some(r) => (r, true),
68 None => (raw, false),
69 };
70
71 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 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}