use quote::{format_ident, quote};
use syn::{
parse::{Parse, ParseStream, Result},
Ident, Token,
};
use crate::rich::{common::Visibility, Field, FieldType};
#[derive(Debug, Default)]
pub(crate) struct ReadFn {
attributes: Vec<syn::Attribute>,
rename: Option<Ident>,
copy: bool,
visibility: Option<Visibility>,
}
impl ReadFn {
pub fn get_fn(&self, field: &Field) -> proc_macro2::TokenStream {
let field_name = &field.name;
let read_fn_name = format_ident!("get_{}", &field.name);
let read_fn_name = self.rename.as_ref().unwrap_or(&read_fn_name);
let visibility = self
.visibility
.as_ref()
.map(|vis| quote! { #vis })
.unwrap_or(quote! { pub });
let attributes = &self.attributes;
match (&field.ty, self.copy) {
(FieldType::Normal(ty), true) => quote! {
#( #attributes )*
#visibility fn #read_fn_name(&self) -> #ty {
self.#field_name
}
},
(FieldType::Normal(ty), false) => quote! {
#( #attributes )*
#visibility fn #read_fn_name(&self) -> &#ty {
&self.#field_name
}
},
(FieldType::Option(ty), true) => quote! {
#( #attributes )*
#visibility fn #read_fn_name(&self) -> Option<#ty> {
self.#field_name.clone()
}
},
(FieldType::Option(ty), false) => quote! {
#( #attributes )*
#visibility fn #read_fn_name(&self) -> Option<&#ty> {
self.#field_name.as_ref()
}
},
}
}
}
impl Parse for ReadFn {
fn parse(input: ParseStream) -> Result<Self> {
let attr_name = "read";
if input.parse::<Ident>()? != attr_name {
return Err(input.error(format!("Expected `{}`", attr_name)));
}
if input.peek(Token![,]) || input.is_empty() {
return Ok(ReadFn::default());
}
let mut read_fn = ReadFn::default();
let inner;
syn::parenthesized!(inner in input);
read_fn.attributes = inner.call(syn::Attribute::parse_outer)?;
while !inner.is_empty() {
match inner.parse::<Ident>()?.to_string().as_ref() {
"rename" => {
let _ = inner.parse::<Token![=]>()?;
match read_fn.rename {
Some(_) => {
return Err(
inner.error("`read` attribute have been renamed more than once")
)
}
None => read_fn.rename = Some(inner.parse::<Ident>()?),
};
}
"copy" => {
if read_fn.copy {
return Err(inner.error("`read` have received `copy` flag more than once"));
}
read_fn.copy = true;
}
"vis" => {
if let Some(_) = read_fn.visibility {
return Err(inner.error(
"`read` attribute have been received visibility value more than once",
));
}
let _ = inner.parse::<Token![=]>()?;
let visibility = inner.parse::<Visibility>()?;
read_fn.visibility = Some(visibility);
}
_ => {
Err(inner.error("`read` attribute only accept `rename` and `copy` attributes"))?
}
}
if inner.peek(Token![,]) {
inner.parse::<Token![,]>()?;
}
}
Ok(read_fn)
}
}