use syn::{FnArg, GenericArgument, PathArguments, Signature, Type, TypePath, spanned::Spanned};
pub(super) fn input(sig: &Signature) -> Vec<Type> {
sig.inputs
.iter()
.filter_map(|arg| match arg {
FnArg::Typed(p) => Some(*p.ty.clone()),
FnArg::Receiver(_) => None,
})
.collect::<Vec<_>>()
}
pub(super) fn first_generic(ty: &Type) -> Type {
let e = syn::Error::new(
ty.span(),
"Expected something like `Result<T, E>`, got {ty:?}",
);
let Type::Path(TypePath { path, .. }) = ty else {
panic!("{ty:?}");
};
let Some(seg) = path.segments.last() else {
panic!("{e}");
};
let PathArguments::AngleBracketed(ref args) = seg.arguments else {
panic!("{e}");
};
let GenericArgument::Type(inner_ty) = &args.args[0] else {
panic!("{e}");
};
inner_ty.clone()
}
#[cfg(test)]
mod tests {
use quote::ToTokens;
use syn::{ItemFn, parse_quote};
use super::*;
fn make_fn() -> ItemFn {
parse_quote! {
async fn exec(_ctx: (), x: i32, y: i32) -> Result<i32, ordr::Error> {
Ok(x + y)
}
}
}
#[test]
fn parse_input() {
let f = make_fn();
let types = input(&f.sig);
assert_eq!(types.len(), 3);
assert_eq!(types[0].to_token_stream().to_string(), "()");
assert_eq!(types[1].to_token_stream().to_string(), "i32");
assert_eq!(types[2].to_token_stream().to_string(), "i32");
}
#[test]
fn parse_no_name_for_ctx() {
let f: ItemFn = parse_quote! {
async fn exec(_: (), x: i32, y: i32) -> Result<i32, ordr::Error> {
Ok(x + y)
}
};
let fn_args = input(&f.sig);
assert_eq!(fn_args.len(), 3);
}
}