1use lazy_static::lazy_static;
2use regex::Regex;
3
4use std::{
5 cmp::min,
6 fmt::{Display, Formatter},
7};
8
9use itertools::{chain, izip, Itertools};
10use proc_macro2::{Ident, Span, TokenStream};
11use quote::{quote, ToTokens};
12
13use crate::error::{error, Result};
14
15pub fn ident(name: &str) -> Ident {
17 Ident::new(name, Span::call_site())
18}
19
20pub fn safe_ident(name: &str) -> Ident {
21 syn::parse_str::<Ident>(name).unwrap_or_else(|_| ident(&format!("{name}_")))
22}
23
24#[derive(Clone, Default, Debug, Hash, Eq, PartialEq, Ord, PartialOrd)]
25pub struct TypePath {
26 parts: Vec<Ident>,
27 is_absolute: bool,
28}
29
30impl Display for TypePath {
31 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32 let prefix = if self.is_absolute { "::" } else { "" };
33 let parts_str = self.parts.iter().join("::");
34
35 write!(f, "{prefix}{parts_str}")
36 }
37}
38
39impl From<&Ident> for TypePath {
40 fn from(value: &Ident) -> Self {
41 TypePath::new(value).expect("All Idents are valid TypePaths")
42 }
43}
44
45impl From<Ident> for TypePath {
46 fn from(value: Ident) -> Self {
47 (&value).into()
48 }
49}
50
51impl TypePath {
52 pub fn new<T: ToString>(path: T) -> Result<Self> {
53 let path_str = path.to_string();
54 if path_str.trim().is_empty() {
55 return Ok(Self {
56 parts: vec![],
57 is_absolute: false,
58 });
59 }
60
61 let is_absolute = Self::is_absolute(&path_str);
62
63 let parts = path_str
64 .split("::")
65 .skip(is_absolute as usize)
66 .map(|part| {
67 let trimmed_part = part.trim().to_string();
68 if trimmed_part.is_empty() {
69 return Err(error!("TypePath cannot be constructed from '{path_str}' since it has it has empty parts"))
70 }
71 Ok(ident(&trimmed_part))
72 })
73 .collect::<Result<Vec<_>>>()?;
74
75 Ok(Self { parts, is_absolute })
76 }
77
78 fn len(&self) -> usize {
79 self.parts.len()
80 }
81
82 fn starts_with(&self, path: &TypePath) -> bool {
83 if self.parts.len() < path.parts.len() {
84 false
85 } else {
86 self.parts[..path.parts.len()] == path.parts
87 }
88 }
89
90 pub fn relative_path_from(&self, path: &TypePath) -> TypePath {
91 let our_parent = self.parent();
92
93 let number_of_consecutively_matching_parts = izip!(&our_parent.parts, &path.parts)
94 .enumerate()
95 .find_map(|(matches_so_far, (our_part, their_part))| {
96 (our_part != their_part).then_some(matches_so_far)
97 })
98 .unwrap_or_else(|| min(our_parent.len(), path.len()));
99
100 let prefix = if our_parent.starts_with(path) {
101 vec![ident("self")]
102 } else {
103 vec![ident("super"); path.len() - number_of_consecutively_matching_parts]
104 };
105
106 let non_matching_path_parts = our_parent
107 .parts
108 .iter()
109 .skip(number_of_consecutively_matching_parts)
110 .cloned();
111
112 let type_ident = self.ident().cloned();
113
114 TypePath {
115 parts: chain!(prefix, non_matching_path_parts, type_ident).collect(),
116 is_absolute: false,
117 }
118 }
119
120 pub fn parent(&self) -> TypePath {
121 let parts = if self.parts.is_empty() {
122 vec![]
123 } else {
124 self.parts[..self.parts.len() - 1].to_vec()
125 };
126
127 TypePath {
128 parts,
129 is_absolute: self.is_absolute,
130 }
131 }
132
133 pub fn take_parts(self) -> Vec<Ident> {
134 self.parts
135 }
136
137 pub fn has_multiple_parts(&self) -> bool {
138 self.parts.len() > 1
139 }
140
141 fn is_absolute(path_str: &str) -> bool {
142 path_str.trim_start().starts_with("::")
143 }
144
145 pub fn prepend(self, mut another: TypePath) -> Self {
146 another.parts.extend(self.parts);
147 another
148 }
149 pub fn append(mut self, another: TypePath) -> Self {
150 self.parts.extend(another.parts);
151 self
152 }
153
154 pub fn ident(&self) -> Option<&Ident> {
155 self.parts.last()
156 }
157}
158
159impl ToTokens for TypePath {
160 fn to_tokens(&self, tokens: &mut TokenStream) {
161 let parts = &self.parts;
162 let leading_delimiter = self.is_absolute.then_some(quote! {::});
163
164 tokens.extend(quote! { #leading_delimiter #(#parts)::* });
165 }
166}
167
168pub fn has_tuple_format(type_name: &str) -> bool {
174 type_name.starts_with('(') && type_name.ends_with(')')
175}
176
177pub fn extract_generic_name(type_name: &str) -> Option<String> {
183 lazy_static! {
184 static ref RE: Regex = Regex::new(r"^\s*generic\s+(\S+)\s*$").unwrap();
185 }
186 RE.captures(type_name)
187 .map(|captures| String::from(&captures[1]))
188}
189
190pub fn extract_array_len(type_name: &str) -> Option<usize> {
196 lazy_static! {
197 static ref RE: Regex = Regex::new(r"^\s*\[.+;\s*(\d+)\s*\]\s*$").unwrap();
198 }
199 RE.captures(type_name)
200 .map(|captures| captures[1].to_string())
201 .map(|length: String| {
202 length.parse::<usize>().unwrap_or_else(|_| {
203 panic!("Could not extract array length from {length}! Original field {type_name}")
204 })
205 })
206}
207
208pub fn extract_str_len(type_name: &str) -> Option<usize> {
214 lazy_static! {
215 static ref RE: Regex = Regex::new(r"^\s*str\s*\[\s*(\d+)\s*\]\s*$").unwrap();
216 }
217 RE.captures(type_name)
218 .map(|captures| captures[1].to_string())
219 .map(|length: String| {
220 length.parse::<usize>().unwrap_or_else(|_| {
221 panic!(
222 "Could not extract string length from {length}! Original field '{type_name}'"
223 )
224 })
225 })
226}
227
228pub fn extract_custom_type_name(type_field: &str) -> Option<String> {
234 lazy_static! {
235 static ref RE: Regex = Regex::new(r"\s*(?:struct|enum)\s*(\S*)").unwrap();
236 }
237
238 RE.captures(type_field)
239 .map(|captures| String::from(&captures[1]))
240}
241
242#[cfg(test)]
243mod tests {
244 use super::*;
245
246 #[test]
247 fn can_be_empty() {
248 let empty_path = " ";
249
250 let type_path = TypePath::new(empty_path).unwrap();
251
252 assert!(type_path.take_parts().is_empty());
253 }
254
255 #[test]
256 fn must_have_ident_at_end() {
257 let no_ident = " ::missing_ident:: ";
258
259 let err = TypePath::new(no_ident).expect_err("Should have failed!");
260
261 assert_eq!(
262 err.to_string(),
263 "TypePath cannot be constructed from ' ::missing_ident:: ' since it has it has empty parts"
264 );
265 }
266
267 #[test]
268 fn trims_whitespace() {
269 let path = " some_mod :: ident ";
270
271 let path = TypePath::new(path).expect("Should have passed.");
272
273 assert_eq!(path.parts, vec!["some_mod", "ident"])
274 }
275
276 #[test]
277 fn can_be_prepended_to() {
278 let path = TypePath::new(" some_mod :: ident ").expect("Should have passed.");
279 let another_path = TypePath::new(" something :: else ").expect("the type path is valid");
280
281 let joined = path.prepend(another_path);
282
283 assert_eq!(joined.parts, vec!["something", "else", "some_mod", "ident"])
284 }
285
286 #[test]
287 fn can_handle_absolute_paths() {
288 let absolute_path = " ::std :: vec:: Vec";
289
290 let type_path = TypePath::new(absolute_path);
291
292 type_path.unwrap();
293 }
294
295 #[test]
296 fn leading_delimiter_present_when_path_is_absolute() {
297 let type_path = TypePath::new(" ::std :: vec:: Vec").unwrap();
298
299 let tokens = type_path.to_token_stream();
300
301 let expected = quote! {::std::vec::Vec};
302 assert_eq!(expected.to_string(), tokens.to_string())
303 }
304
305 #[test]
306 fn leading_delimiter_not_present_when_path_is_relative() {
307 let type_path = TypePath::new(" std :: vec:: Vec").unwrap();
308
309 let tokens = type_path.to_token_stream();
310
311 let expected = quote! {std::vec::Vec};
312 assert_eq!(expected.to_string(), tokens.to_string())
313 }
314
315 #[test]
316 fn path_with_two_or_more_parts_has_a_parent() {
317 let type_path = TypePath::new(":: std::Type").unwrap();
318
319 let parent = type_path.parent();
320
321 let expected_parent = TypePath::new("::std").unwrap();
322 assert_eq!(parent, expected_parent)
323 }
324
325 #[test]
326 fn path_with_only_one_part_has_empty_parent() {
327 let type_path = TypePath::new(":: std").unwrap();
328
329 let parent = type_path.parent();
330
331 assert!(parent.take_parts().is_empty());
332 }
333
334 #[test]
335 fn relative_path_from_same_mod() {
336 let deeper_path = TypePath::new("a::b::SomeType").unwrap();
337 let the_same_mod = TypePath::new("a::b").unwrap();
338
339 let relative_path = deeper_path.relative_path_from(&the_same_mod);
340
341 let expected_relative_path = TypePath::new("self::SomeType").unwrap();
342 assert_eq!(relative_path, expected_relative_path);
343 }
344
345 #[test]
346 fn relative_path_from_root_mod() {
347 let deeper_path = TypePath::new("SomeType").unwrap();
348 let root_mod = TypePath::new("").unwrap();
349
350 let relative_path = deeper_path.relative_path_from(&root_mod);
351
352 let expected_relative_path = TypePath::new("self::SomeType").unwrap();
353 assert_eq!(relative_path, expected_relative_path);
354 }
355
356 #[test]
357 fn relative_path_from_deeper_mod() {
358 let a_path = TypePath::new("a::b::SomeType").unwrap();
359 let deeper_mod = TypePath::new("a::b::c::d").unwrap();
360
361 let relative_path = a_path.relative_path_from(&deeper_mod);
362
363 let expected_relative_path = TypePath::new("super::super::SomeType").unwrap();
364 assert_eq!(relative_path, expected_relative_path);
365 }
366
367 #[test]
368 fn relative_path_going_deeper() {
369 let a_path = TypePath::new("a::b::c::SomeType").unwrap();
370 let higher_level_mod = TypePath::new("a").unwrap();
371
372 let relative_path = a_path.relative_path_from(&higher_level_mod);
373
374 let expected_relative_path = TypePath::new("self::b::c::SomeType").unwrap();
375 assert_eq!(relative_path, expected_relative_path);
376 }
377
378 #[test]
379 fn relative_path_up_then_down() {
380 let a_path = TypePath::new("a::b::c::SomeType").unwrap();
381 let sister_path = TypePath::new("d::e").unwrap();
382
383 let relative_path = a_path.relative_path_from(&sister_path);
384
385 let expected_relative_path = TypePath::new("super::super::a::b::c::SomeType").unwrap();
386 assert_eq!(relative_path, expected_relative_path);
387 }
388
389 #[test]
390 fn path_starts_with_another() {
391 let a_path = TypePath::new("a::b::c::d").unwrap();
392 let prefix = TypePath::new("a::b").unwrap();
393
394 assert!(a_path.starts_with(&prefix));
395 }
396 #[test]
397 fn path_does_not_start_with_another() {
398 let a_path = TypePath::new("a::b::c::d").unwrap();
399 let prefix = TypePath::new("c::d").unwrap();
400
401 assert!(!a_path.starts_with(&prefix));
402 }
403
404 #[test]
405 fn start_with_size_guard() {
406 let a_path = TypePath::new("a::b::c").unwrap();
407 let prefix = TypePath::new("a::b::c::d").unwrap();
408
409 assert!(!a_path.starts_with(&prefix));
410 }
411
412 #[test]
413 fn tuples_start_and_end_with_round_brackets() {
414 assert!(has_tuple_format("(_, _)"));
415
416 assert!(!has_tuple_format("(.."));
417
418 assert!(!has_tuple_format("..)"));
419 }
420
421 #[test]
422 fn generic_name_extracted() {
423 let type_name = " generic T ";
424
425 let name = extract_generic_name(type_name).expect("Should have succeeded");
426
427 assert_eq!(name, "T");
428 }
429
430 #[test]
431 fn array_len_extracted() {
432 let type_name = " [ _ ; 8 ] ";
433
434 let size = extract_array_len(type_name).expect("Should have succeeded");
435
436 assert_eq!(size, 8);
437 }
438
439 #[test]
440 fn str_len_extracted() {
441 let type_name = " str [ 10 ] ";
442
443 let str_len = extract_str_len(type_name).expect("Should have succeeded");
444
445 assert_eq!(str_len, 10);
446 }
447
448 #[test]
449 fn custom_struct_type_name_extracted() {
450 let type_name = " struct SomeStruct ";
451
452 let struct_name = extract_custom_type_name(type_name).expect("Should have succeeded");
453
454 assert_eq!(struct_name, "SomeStruct");
455 }
456
457 #[test]
458 fn custom_enum_type_name_extracted() {
459 let type_name = " enum SomeEnum ";
460
461 let enum_name = extract_custom_type_name(type_name).expect("Should have succeeded");
462
463 assert_eq!(enum_name, "SomeEnum");
464 }
465}