use crate::quote;
use std::collections::HashMap;
use syn::{
punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute, FnArg, Pat, PatType, Type,
TypePath,
};
use crate::attr;
#[derive(Debug)]
pub struct Arg<'a> {
pub pos: usize,
pub arg: &'a PatType,
pub help: Option<String>,
}
pub fn parse_args<'a>(
args: &'a Punctuated<FnArg, Comma>,
attr: &Vec<Attribute>,
) -> Result<Vec<Arg<'a>>, proc_macro2::TokenStream> {
let mut res: HashMap<String, Arg<'a>> = HashMap::new();
for (i, arg) in args.iter().enumerate() {
if i == 0 {
continue;
}
if let syn::FnArg::Typed(arg) = arg {
let name = match arg.pat.as_ref() {
Pat::Ident(i) => i.ident.to_string(),
_ => {
return Err(
syn::Error::new(arg.pat.span(), "Expected ident").into_compile_error()
)
}
};
res.insert(
name,
Arg {
pos: res.len(),
arg: arg,
help: None,
},
);
}
}
for attr in attr {
if attr.path().is_ident("clik_arg") {
attr::parse_attr(&attr, &mut res)?
}
}
let mut vec: Vec<Arg<'a>> = res.into_iter().map(|f| f.1).collect();
vec.sort_by(|a, b| a.pos.cmp(&b.pos));
Ok(vec)
}
pub fn get_state_var<'a>(
args: &'a Punctuated<FnArg, Comma>,
) -> Result<TypePath, proc_macro2::TokenStream> {
Err(match args.first() {
Some(first) => match first {
FnArg::Typed(typed) => match typed.ty.as_ref() {
Type::Reference(reference) => match reference.elem.as_ref() {
Type::Path(p) => return Ok(p.clone()),
_ => syn::Error::new(typed.span(), "State variable must be a type"),
},
_ => syn::Error::new(typed.span(), "State variable must be a reference"),
},
FnArg::Receiver(r) => syn::Error::new(r.span(), "State variable cannot be 'self'"),
},
None => syn::Error::new(
args.span(),
"Expected at least one argument: state variable",
),
}
.into_compile_error())
}
pub fn create_parse_blocks<'a>(args: Vec<Arg<'a>>) -> Vec<proc_macro2::TokenStream> {
let mut res: Vec<proc_macro2::TokenStream> = Vec::new();
for arg in args {
let ty = &arg.arg.ty;
let ident = &arg.arg.pat;
let i = res.len();
let block = quote! {
let #ident: #ty = {
match match args.get(#i) {
None => return Err(
clik::error::MissingArgumentError {
name: stringify!(#ident).to_string(),
position: #i,
ty: stringify!(#ty).to_string()
}.into()),
Some(v) => v
}.parse::<#ty>() {
Ok(v) => v,
Err(e) => return Err(
clik::error::WrongArgumentError {
name: stringify!(#ident).to_string(),
position: #i,
ty: stringify!(#ty).to_string(),
inner: e.into()
}.into())
}
};
};
res.push(block);
}
res
}