use super::prelude::*;
#[derive(Debug, FromField)]
#[darling(attributes(arg))]
struct Arg {
desc: Option<LitStr>,
s: Option<Expr>,
d: Option<Expr>,
ident: Option<Ident>,
ty: Type,
}
#[derive(Debug, FromDeriveInput)]
#[darling(attributes(args), supports(struct_named))]
struct Args {
desc: LitStr,
f: Ident,
pos: Option<LitStr>,
ident: Ident,
data: ast::Data<(), Arg>,
}
fn is_arg(a: &Arg) -> bool {
!(a.desc.is_none())
}
fn a(p: Args) -> Result<TokenStream, String> {
let i = &p.ident;
let k = LitStr::new(i.to_string().to_case(Case::Kebab).as_str(), i.span());
enum F {
Args(Ident, Type),
Arg(Ident, Type, LitStr, proc_macro2::TokenStream, LitStr),
}
let f: Vec<_> = p
.data
.take_struct()
.ok_or(format!("Expected a struct."))?
.fields
.into_iter()
.map(|f| -> Result<F, String> {
let b = is_arg(&f);
let i = f.ident.unwrap();
let ty = f.ty;
Ok(if !b {
F::Args(i, ty)
} else {
let desc = f.desc.unwrap();
let k = LitStr::new(
format!("--{}", i.to_string().to_case(Case::Kebab)).as_str(),
i.span(),
);
if f.s.is_some() && f.d.is_some() {
return Err(format!(
"Only one of 's' (static) and 'd' (dynamic) should be set"
));
}
let init = if let Some(s) = f.s {
quote! { Init::Const((#s).into()) }
} else if let Some(d) = f.d {
quote! { Init::Dyn(|c| (#d)(c).into()) }
} else {
quote! { Init::None }
};
F::Arg(i, ty, desc, init, k)
})
})
.collect::<Result<Vec<_>, _>>()?;
let desc = &p.desc;
let func = &p.f;
let pos = &p.pos;
let parse = f
.iter()
.map(|a| {
match a {
F::Args(ident, _) =>
quote! { #ident: Args::new(c, args)? },
F::Arg(ident, ty, _, init, k) => quote! { #ident: <#ty>::parse2(#init, #k, c, args).map_err(|e| ArgsParseErr::Arg(#k, e, Self::usage(c)))? },
}
})
.collect::<Vec<_>>();
let descs = match pos{
Some(e) => vec![quote! { r.push([format!("positional"), format!(#e), format!(""), format!("")]) } ],
None => vec![],
};
let descs = descs.into_iter().chain( f
.iter()
.map(|a| match a {
F::Args(_, ty) => quote! {
#ty::add_usage(c, r)
},
F::Arg(_, ty, desc, init, k) => quote! { r.push(<#ty>::desc2(#init, #desc, #k, c)) },
}))
.collect::<Vec<_>>();
let defaults: Vec<_> = f
.iter()
.map(|a| match a {
F::Args(i, ty) => quote! { #i: <#ty>::default(c) },
F::Arg(i, ty, _, init, _) => quote! { #i: <#ty>::default2(c, #init) },
})
.collect();
let run = match pos {
Some(_) => quote! {
fn run(self, c:&C, a: Vec<&str>) -> Result<(), String> {
let _ = #func(c, self, a)?;
Ok(())
}
},
None => quote! {
fn run(self, c:&C, _: Vec<&str>) -> Result<(), String> {
let _ = #func(c, self)?;
Ok(())
}
},
};
Ok(quote! {
impl Args<C> for #i {
fn new<'a, 'b>(c: &C, args: &mut ParsedArgs<'b>) -> Result<Self, ArgsParseErr<'b>>{
Ok(Self{
#( #parse, )*
})
}
fn desc_act() -> &'static str { #desc }
fn add_usage(c: &C, r: &mut Vec<[String; 4]>) {
#( #descs; )*
}
fn add_paths<'a>(pfx: &Vec<Arg<'a>>, p: &mut Vec<Vec<Arg<'a>>>) {
let mut pfx = pfx.clone();
pfx.push(#k);
p.push(pfx);
}
fn default(c: &C) -> Self {
Self { #( #defaults, )* }
}
#run
}
}
.into())
}
pub fn f(i: TokenStream) -> TokenStream {
let p = match Args::from_derive_input(&parse_macro_input!(i as DeriveInput)) {
Ok(p) => p,
Err(e) => return e.write_errors().into(),
};
match a(p) {
Ok(p) => p,
Err(e) => Error::custom(e).write_errors().into(),
}
}