use quote::quote_spanned;
use syn::parse_quote;
use syn::punctuated::Punctuated;
use syn::spanned::Spanned;
pub fn signature_to_function_call(sig: &syn::Signature) -> syn::Result<syn::ExprCall> {
let funcexpr = syn::ExprPath {
attrs: Vec::new(),
qself: None,
path: sig.ident.clone().into(),
};
let mut funcargs = Punctuated::new();
for item in &sig.inputs {
match item {
syn::FnArg::Receiver(recv) => {
let span = recv.self_token.span;
funcargs.push(syn::parse2(quote_spanned!(span=> self))?);
}
syn::FnArg::Typed(argty) => {
if let syn::Pat::Ident(ref id) = *argty.pat {
let argpath = syn::ExprPath {
attrs: Vec::new(),
qself: None,
path: id.ident.clone().into(),
};
funcargs.push(syn::Expr::Path(argpath));
} else {
return Err(syn::Error::new(argty.span(), "expected identifier"));
}
}
}
}
Ok(syn::ExprCall {
attrs: Vec::new(),
paren_token: syn::token::Paren {
span: funcexpr.span(),
},
func: Box::new(funcexpr.into()),
args: funcargs,
})
}
pub fn signature_to_method_call(sig: &syn::Signature) -> syn::Result<syn::ExprMethodCall> {
let receiver = sig.receiver().unwrap();
let span = receiver.span();
let mut funcargs = Punctuated::new();
for item in &sig.inputs {
match item {
syn::FnArg::Receiver(_) => {}
syn::FnArg::Typed(argty) => {
if let syn::Pat::Ident(ref id) = *argty.pat {
let argpath = syn::ExprPath {
attrs: Vec::new(),
qself: None,
path: id.ident.clone().into(),
};
funcargs.push(syn::Expr::Path(argpath));
} else {
return Err(syn::Error::new(argty.span(), "expected identifier"));
}
}
}
}
Ok(syn::ExprMethodCall {
attrs: Vec::new(),
receiver: Box::new(syn::parse2(quote_spanned!(span=> self))?),
dot_token: syn::token::Dot {
spans: [sig.span()],
},
method: sig.ident.clone(),
turbofish: None,
paren_token: syn::token::Paren { span: sig.span() },
args: funcargs,
})
}
pub fn prepend_function_path(call: &mut syn::ExprCall, module: syn::Path) -> syn::Result<()> {
if let syn::Expr::Path(ref mut path) = *call.func {
for (i, segment) in module.segments.into_iter().enumerate() {
path.path.segments.insert(i, segment);
}
Ok(())
} else {
Err(syn::Error::new(call.func.span(), "expected path"))
}
}
pub fn deref_expr(expr: syn::Expr) -> syn::Expr {
syn::Expr::Paren(syn::ExprParen {
attrs: Vec::new(),
paren_token: syn::token::Paren { span: expr.span() },
expr: Box::new(syn::Expr::Unary(syn::ExprUnary {
attrs: Vec::new(),
op: syn::UnOp::Deref(parse_quote!(*)),
expr: Box::new(expr),
})),
})
}
pub fn trait_to_generic_ident(trait_: &syn::ItemTrait) -> syn::Ident {
let mut raw = trait_
.ident
.to_string()
.chars()
.filter(|c| c.is_uppercase())
.collect::<String>();
loop {
if !trait_.generics.params.iter().any(|g| match g {
syn::GenericParam::Type(param) if param.ident == raw => true,
syn::GenericParam::Const(param) if param.ident == raw => true,
_ => false,
}) {
break;
} else {
raw.push('_');
}
}
syn::Ident::new(&raw, trait_.ident.span())
}
#[cfg(test)]
mod tests {
use syn::parse_quote;
#[test]
fn prepend_function_path() {
let path = parse_quote!(crate::qualified::path);
let mut call = parse_quote!(myfunction(arg1, arg2));
super::prepend_function_path(&mut call, path).unwrap();
assert_eq!(
call,
parse_quote!(crate::qualified::path::myfunction(arg1, arg2))
);
}
#[test]
fn deref_expr() {
let expr = parse_quote!(self);
let dereffed = super::deref_expr(expr);
assert_eq!(dereffed, parse_quote!((*self)));
}
#[test]
fn trait_to_generic_ident() {
let trait_ = syn::parse_quote!(
trait Trait {}
);
let expected: syn::Ident = syn::parse_quote!(T);
assert_eq!(super::trait_to_generic_ident(&trait_), expected);
let trait_ = syn::parse_quote!(
trait SomeTrait {}
);
let expected: syn::Ident = syn::parse_quote!(ST);
assert_eq!(super::trait_to_generic_ident(&trait_), expected);
let trait_ = syn::parse_quote!(
trait Trait<T> {}
);
let expected: syn::Ident = syn::parse_quote!(T_);
assert_eq!(super::trait_to_generic_ident(&trait_), expected);
}
}