blanket/
utils.rs

1use quote::quote_spanned;
2use syn::{parse_quote, punctuated::Punctuated, spanned::Spanned, GenericParam, Token};
3
4/// Convert a function signature to a function call with the same arguments.
5pub fn signature_to_function_call(sig: &syn::Signature) -> syn::Result<syn::ExprCall> {
6    // Simply use the function ident as the function expression.
7    let funcexpr = syn::ExprPath {
8        attrs: Vec::new(),
9        qself: None,
10        path: sig.ident.clone().into(),
11    };
12
13    // Extract arguments from the method signature names
14    let mut funcargs = Punctuated::new();
15    for item in &sig.inputs {
16        match item {
17            syn::FnArg::Receiver(recv) => {
18                let span = recv.self_token.span;
19                funcargs.push(syn::parse2(quote_spanned!(span=> self))?);
20            }
21            syn::FnArg::Typed(argty) => {
22                if let syn::Pat::Ident(ref id) = *argty.pat {
23                    let argpath = syn::ExprPath {
24                        attrs: Vec::new(),
25                        qself: None,
26                        path: id.ident.clone().into(),
27                    };
28                    funcargs.push(syn::Expr::Path(argpath));
29                } else {
30                    return Err(syn::Error::new(argty.span(), "expected identifier"));
31                }
32            }
33        }
34    }
35
36    // Return the function call as an expression
37    Ok(syn::ExprCall {
38        attrs: Vec::new(),
39        paren_token: syn::token::Paren::default(),
40        func: Box::new(funcexpr.into()),
41        args: funcargs,
42    })
43}
44
45/// Convert a function signature to a method call with the same arguments.
46pub fn signature_to_method_call(sig: &syn::Signature) -> syn::Result<syn::ExprMethodCall> {
47    // Extract receiver
48    let receiver = sig.receiver().unwrap();
49    let span = receiver.span();
50
51    // Extract arguments
52    let mut funcargs = Punctuated::new();
53    for item in &sig.inputs {
54        match item {
55            syn::FnArg::Receiver(_) => {}
56            syn::FnArg::Typed(argty) => {
57                if let syn::Pat::Ident(ref id) = *argty.pat {
58                    let argpath = syn::ExprPath {
59                        attrs: Vec::new(),
60                        qself: None,
61                        path: id.ident.clone().into(),
62                    };
63                    funcargs.push(syn::Expr::Path(argpath));
64                } else {
65                    return Err(syn::Error::new(argty.span(), "expected identifier"));
66                }
67            }
68        }
69    }
70
71    // Write the method call
72    Ok(syn::ExprMethodCall {
73        attrs: Vec::new(),
74        receiver: Box::new(syn::parse2(quote_spanned!(span=> self))?),
75        dot_token: syn::token::Dot {
76            spans: [sig.span()],
77        },
78        method: sig.ident.clone(),
79        turbofish: None,
80        paren_token: syn::token::Paren::default(),
81        args: funcargs,
82    })
83}
84
85/// Prepend a module path to a function call name.
86pub fn prepend_function_path(call: &mut syn::ExprCall, module: syn::Path) -> syn::Result<()> {
87    if let syn::Expr::Path(ref mut path) = *call.func {
88        for (i, segment) in module.segments.into_iter().enumerate() {
89            path.path.segments.insert(i, segment);
90        }
91        Ok(())
92    } else {
93        Err(syn::Error::new(call.func.span(), "expected path"))
94    }
95}
96
97/// Deref an expression and wrap it in brackets to preserve operation priority.
98pub fn deref_expr(expr: syn::Expr) -> syn::Expr {
99    syn::Expr::Paren(syn::ExprParen {
100        attrs: Vec::new(),
101        paren_token: syn::token::Paren::default(),
102        expr: Box::new(syn::Expr::Unary(syn::ExprUnary {
103            attrs: Vec::new(),
104            op: syn::UnOp::Deref(parse_quote!(*)),
105            expr: Box::new(expr),
106        })),
107    })
108}
109
110/// Build a generic identifier suitable for the given trait.
111///
112/// This function extracts the initials of the trait identifier. If this results
113/// in a generic type identifier already present in the generics of that trait,
114/// as many underscores are added to the end of the identifier.
115pub fn trait_to_generic_ident(trait_: &syn::ItemTrait) -> syn::Ident {
116    let mut raw = trait_
117        .ident
118        .to_string()
119        .chars()
120        .filter(|c| c.is_uppercase())
121        .collect::<String>();
122    loop {
123        if !trait_.generics.params.iter().any(|g| match g {
124            syn::GenericParam::Type(param) if param.ident == raw => true,
125            syn::GenericParam::Const(param) if param.ident == raw => true,
126            _ => false,
127        }) {
128            break;
129        } else {
130            raw.push('_');
131        }
132    }
133
134    syn::Ident::new(&raw, trait_.ident.span())
135}
136
137/// Convert a generic type declaration to a generic with the same arguments.
138///
139/// Given a generic section `<T: 'static + Send>`, get simply `<T>`.
140pub fn generics_declaration_to_generics(
141    generics: &Punctuated<GenericParam, Token![,]>,
142) -> syn::Result<Punctuated<GenericParam, Token![,]>> {
143    generics
144        .iter()
145        .map(|gen| match gen {
146            syn::GenericParam::Type(t) => Ok(syn::GenericParam::Type(syn::TypeParam {
147                attrs: t.attrs.clone(),
148                ident: t.ident.clone(),
149                colon_token: None,
150                bounds: Punctuated::new(),
151                eq_token: None,
152                default: None,
153            })),
154            syn::GenericParam::Lifetime(l) => Ok(syn::GenericParam::Lifetime(syn::LifetimeParam {
155                attrs: l.attrs.clone(),
156                lifetime: l.lifetime.clone(),
157                colon_token: None,
158                bounds: Punctuated::new(),
159            })),
160            syn::GenericParam::Const(c) => {
161                Err(syn::Error::new(c.span(), "cannot handle const generics"))
162            }
163        })
164        .collect()
165}
166
167#[cfg(test)]
168mod tests {
169
170    use syn::parse_quote;
171
172    #[test]
173    fn prepend_function_path() {
174        let path = parse_quote!(crate::qualified::path);
175        let mut call = parse_quote!(myfunction(arg1, arg2));
176        super::prepend_function_path(&mut call, path).unwrap();
177        assert_eq!(
178            call,
179            parse_quote!(crate::qualified::path::myfunction(arg1, arg2))
180        );
181    }
182
183    #[test]
184    fn deref_expr() {
185        let expr = parse_quote!(self);
186        let dereffed = super::deref_expr(expr);
187        assert_eq!(dereffed, parse_quote!((*self)));
188    }
189
190    #[test]
191    fn trait_to_generic_ident() {
192        let trait_ = syn::parse_quote!(
193            trait Trait {}
194        );
195        let expected: syn::Ident = syn::parse_quote!(T);
196        assert_eq!(super::trait_to_generic_ident(&trait_), expected);
197
198        let trait_ = syn::parse_quote!(
199            trait SomeTrait {}
200        );
201        let expected: syn::Ident = syn::parse_quote!(ST);
202        assert_eq!(super::trait_to_generic_ident(&trait_), expected);
203
204        let trait_ = syn::parse_quote!(
205            trait Trait<T> {}
206        );
207        let expected: syn::Ident = syn::parse_quote!(T_);
208        assert_eq!(super::trait_to_generic_ident(&trait_), expected);
209    }
210}