1use std::fmt;
10use std::str::FromStr;
11
12use crate::errors::ParamParseError;
13
14#[derive(Debug, PartialEq, Eq, Hash)]
16pub struct Type {
17 pub namespace: Vec<String>,
19
20 pub name: String,
22
23 pub bare: bool,
25
26 pub generic_ref: bool,
28
29 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 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 fn from_str(ty: &str) -> Result<Self, Self::Err> {
75 let (ty, generic_ref) = match ty.strip_prefix('!') {
77 Some(ty) => (ty, true),
78 None => (ty, false),
79 };
80
81 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 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}