use quote::quote;
use syn::{
parse::{Parse, ParseStream, Result},
Ident, Token,
};
use crate::rich::{common::Visibility, Field, FieldType};
#[derive(Debug)]
pub(crate) struct ShortFn {
attributes: Vec<syn::Attribute>,
name: Ident,
expr: syn::Expr,
}
impl Parse for ShortFn {
fn parse(input: ParseStream) -> Result<Self> {
let attributes = input.call(syn::Attribute::parse_outer)?;
let name = input.parse::<Ident>()?;
let _ = input.parse::<Token![=]>()?;
let expr = input.parse::<syn::Expr>()?;
Ok(Self {
attributes,
name,
expr,
})
}
}
#[derive(Debug)]
pub(crate) struct ValueFns {
take: bool,
visibility: Option<Visibility>,
name_exprs: Vec<ShortFn>,
}
impl ValueFns {
pub fn get_fns(&self, field: &Field) -> proc_macro2::TokenStream {
let field_name = &field.name;
let visibility = self
.visibility
.as_ref()
.map(|vis| quote! { #vis })
.unwrap_or(quote! { pub });
let fns = self
.name_exprs
.iter()
.map(|short_fn| {
let fn_name = &short_fn.name;
let expr = &short_fn.expr;
let expr = match field.ty {
FieldType::Normal(_) => quote! { (#expr).into() },
FieldType::Option(_) => quote! { Some((#expr).into()) },
};
let attributes = &short_fn.attributes;
let (self_arg, ret_arg) = if self.take {
(quote! { mut self }, quote! { Self })
} else {
(quote! { &mut self }, quote! { &mut Self })
};
quote! {
#( #attributes )*
#visibility fn #fn_name(#self_arg) -> #ret_arg {
self.#field_name = #expr;
self
}
}
})
.collect::<Vec<proc_macro2::TokenStream>>();
quote! {
#( #fns )*
}
}
}
impl Parse for ValueFns {
fn parse(input: ParseStream) -> Result<Self> {
let attr_name = "value_fns";
if input.parse::<Ident>()? != attr_name {
return Err(input.error(format!("Expected `{}`", attr_name)));
}
let mut take = false;
let mut visibility = None;
if input.peek(syn::token::Paren) {
let inner;
syn::parenthesized!(inner in input);
while !inner.is_empty() {
match inner.parse::<Ident>()?.to_string().as_ref() {
"take" => {
if take {
return Err(
inner.error("`value_fns` have received `take` flag more than once")
);
} else {
take = true;
}
}
"vis" => {
if let Some(_) = visibility {
return Err(inner.error(
"`value_fns` attribute have been received visibility value more than once",
));
}
let _ = inner.parse::<Token![=]>()?;
let vis = inner.parse::<Visibility>()?;
visibility = Some(vis);
}
_ => {
return Err(inner.error(
"`value_fns` attribute only accept `take`, `vis` and `doc` attribute",
))
}
}
}
}
let _ = input.parse::<Token![=]>()?;
let inner_content;
let _ = syn::braced!(inner_content in input);
let name_exprs = inner_content
.parse_terminated::<_, Token![,]>(ShortFn::parse)?
.into_iter()
.collect::<Vec<ShortFn>>();
let mut fn_names: Vec<&Ident> = vec![];
for sf in name_exprs.iter() {
if fn_names.contains(&&sf.name) {
return Err(
inner_content.error(format!("the function name `{}` is duplicated.", sf.name))
);
} else {
fn_names.push(&sf.name)
}
}
Ok(Self {
take,
visibility,
name_exprs,
})
}
}