syn_prelude/
path_helpers.rs

1use proc_macro2::Span;
2use syn::{
3    punctuated::Punctuated, spanned::Spanned, Ident, PathArguments, PathSegment, Token, TypePath,
4};
5use syn_prelude_macros::gen_tuples_for_impl_into_idents;
6
7pub trait PathHelpers {
8    fn new() -> Self;
9    fn to_type(self) -> syn::Type;
10    fn from_ident<I: IntoIdent>(origin: I) -> Self;
11    fn from_idents<I: IntoIdents>(origin: I) -> Self;
12    fn push_segment<S: IntoSegment>(&mut self, segment: S) -> &mut Self;
13    fn modify_segment_at<F: FnOnce(&mut PathSegment)>(
14        &mut self,
15        index: usize,
16        modify: F,
17    ) -> &mut Self;
18    fn push_arg(&mut self, index: usize, typ: syn::Type) -> &mut Self {
19        self.modify_segment_at(index, |segment| {
20            let span = typ.span();
21            let arg = syn::GenericArgument::Type(typ);
22            if let syn::PathArguments::AngleBracketed(args) = &mut segment.arguments {
23                args.args.push(arg)
24            } else {
25                segment.arguments =
26                    syn::PathArguments::AngleBracketed(syn::AngleBracketedGenericArguments {
27                        colon2_token: None,
28                        lt_token: Token![<](span),
29                        args: {
30                            let mut args = Punctuated::new();
31                            args.push(arg);
32                            args
33                        },
34                        gt_token: Token![>](span),
35                    })
36            }
37        })
38    }
39    fn push_ident_arg(&mut self, index: usize, ident: Ident) -> &mut Self {
40        self.push_arg(
41            index,
42            syn::Type::Path(syn::TypePath {
43                qself: None,
44                path: syn::Path::from_ident(ident),
45            }),
46        )
47    }
48}
49
50pub trait IntoSegment {
51    fn into_segment(self) -> PathSegment;
52}
53
54impl IntoSegment for PathSegment {
55    fn into_segment(self) -> PathSegment {
56        self
57    }
58}
59
60impl IntoSegment for &PathSegment {
61    fn into_segment(self) -> PathSegment {
62        self.clone()
63    }
64}
65
66impl IntoSegment for &Ident {
67    fn into_segment(self) -> PathSegment {
68        PathSegment {
69            ident: self.clone(),
70            arguments: PathArguments::None,
71        }
72    }
73}
74
75impl IntoSegment for Ident {
76    fn into_segment(self) -> PathSegment {
77        PathSegment {
78            ident: self,
79            arguments: PathArguments::None,
80        }
81    }
82}
83
84impl IntoSegment for (&str, Span) {
85    fn into_segment(self) -> PathSegment {
86        PathSegment {
87            ident: Ident::new(self.0, self.1),
88            arguments: PathArguments::None,
89        }
90    }
91}
92
93pub trait IntoIdent {
94    fn into_ident(self) -> syn::Ident;
95}
96
97impl IntoIdent for Ident {
98    fn into_ident(self) -> syn::Ident {
99        self
100    }
101}
102
103impl IntoIdent for &Ident {
104    fn into_ident(self) -> syn::Ident {
105        self.clone()
106    }
107}
108
109impl IntoIdent for (&str, Span) {
110    fn into_ident(self) -> syn::Ident {
111        syn::Ident::new(self.0, self.1)
112    }
113}
114
115pub trait IntoIdents {
116    fn into_idents(self) -> impl Iterator<Item = Ident>;
117}
118
119gen_tuples_for_impl_into_idents!(2..=10);
120
121impl PathHelpers for syn::Path {
122    fn new() -> Self {
123        Self {
124            leading_colon: None,
125            segments: Punctuated::new(),
126        }
127    }
128
129    fn to_type(self) -> syn::Type {
130        syn::Type::Path(TypePath {
131            qself: None,
132            path: self,
133        })
134    }
135
136    fn from_ident<I: IntoIdent>(origin: I) -> Self {
137        let mut path = Self::new();
138        path.push_segment(origin.into_ident());
139        path
140    }
141
142    fn from_idents<I: IntoIdents>(origin: I) -> Self {
143        let mut path = Self::new();
144        for ident in origin.into_idents() {
145            path.push_segment(ident);
146        }
147        path
148    }
149
150    fn push_segment<S: IntoSegment>(&mut self, segment: S) -> &mut Self {
151        self.segments.push(segment.into_segment());
152        self
153    }
154
155    fn modify_segment_at<F: FnOnce(&mut PathSegment)>(
156        &mut self,
157        index: usize,
158        modify: F,
159    ) -> &mut Self {
160        if let Some((_, segment)) = self
161            .segments
162            .iter_mut()
163            .enumerate()
164            .find(|(i, _)| *i == index)
165        {
166            modify(segment);
167        }
168        self
169    }
170}