sylvia_derive/parser/
check_generics.rs1use proc_macro2::Ident;
2use syn::visit::Visit;
3use syn::{parse_quote, GenericArgument, GenericParam, Path, TraitItemType, Type};
4
5pub trait GetPath {
8 fn get_path(&self) -> Option<Path>;
9}
10
11impl GetPath for GenericParam {
12 fn get_path(&self) -> Option<Path> {
13 match self {
14 GenericParam::Type(ty) => {
15 let ident = &ty.ident;
16 Some(parse_quote! { #ident })
17 }
18 _ => None,
19 }
20 }
21}
22
23impl GetPath for GenericArgument {
24 fn get_path(&self) -> Option<Path> {
25 match self {
26 GenericArgument::Type(Type::Path(path)) => {
27 let path = &path.path;
28 Some(parse_quote! { #path })
29 }
30 _ => None,
31 }
32 }
33}
34
35impl GetPath for TraitItemType {
36 fn get_path(&self) -> Option<Path> {
37 let ident = &self.ident;
38 Some(parse_quote!(#ident))
39 }
40}
41
42impl GetPath for Ident {
43 fn get_path(&self) -> Option<Path> {
44 Some(parse_quote! { #self })
45 }
46}
47
48impl GetPath for Type {
49 fn get_path(&self) -> Option<Path> {
50 match self {
51 Type::Path(path) => Some(path.path.clone()),
52 _ => None,
53 }
54 }
55}
56
57#[derive(Debug)]
59pub struct CheckGenerics<'g, Generic> {
60 generics: &'g [&'g Generic],
61 used: Vec<&'g Generic>,
62}
63
64impl<'g, Generic> CheckGenerics<'g, Generic>
65where
66 Generic: GetPath + PartialEq,
67{
68 pub fn new(generics: &'g [&'g Generic]) -> Self {
69 Self {
70 generics,
71 used: vec![],
72 }
73 }
74
75 pub fn used(self) -> Vec<&'g Generic> {
76 self.used
77 }
78
79 pub fn used_unused(self) -> (Vec<&'g Generic>, Vec<&'g Generic>) {
81 let unused = self
82 .generics
83 .iter()
84 .filter(|gen| !self.used.contains(*gen))
85 .copied()
86 .collect();
87
88 (self.used, unused)
89 }
90}
91
92impl<'ast, Generic> Visit<'ast> for CheckGenerics<'_, Generic>
93where
94 Generic: GetPath + PartialEq,
95{
96 fn visit_path(&mut self, p: &'ast syn::Path) {
97 if let Some(gen) = self
98 .generics
99 .iter()
100 .find(|gen| gen.get_path().as_ref() == Some(p))
101 {
102 if !self.used.contains(gen) {
103 self.used.push(gen);
104 }
105 }
106
107 for el in &p.segments {
109 self.visit_path_segment(el);
110 }
111 }
112}