use quote::quote;
use crate::core::Attrs;
#[derive(Clone)]
pub struct Field {
raw_attrs: Vec<syn::Attribute>,
attrs: Attrs,
vis: syn::Visibility,
name: FieldName,
ty: syn::Type,
}
impl Field {
pub fn parse(i: usize, field: &syn::Field) -> syn::Result<Self> {
Ok(Self {
raw_attrs: field.attrs.clone(),
attrs: Attrs::parse(&field.attrs)?,
vis: field.vis.clone(),
name: match &field.ident {
None => syn::Index::from(i).into(),
Some(v) => v.clone().into(),
},
ty: field.ty.clone(),
})
}
#[allow(unused)]
pub fn raw_attrs(&self) -> &[syn::Attribute] {
&self.raw_attrs
}
pub fn attrs(&self) -> &Attrs {
&self.attrs
}
pub fn vis(&self) -> &syn::Visibility {
&self.vis
}
pub fn name(&self) -> &FieldName {
&self.name
}
pub fn ty(&self) -> &syn::Type {
&self.ty
}
pub fn display_name(&self) -> syn::Result<String> {
let display = self.attrs.get("display")?;
Ok(display
.iter()
.find_map(|a| a.as_attr())
.and_then(|attr| attr.get_string("alias"))
.unwrap_or_else(|| self.name.to_string()))
}
pub fn is_option(&self) -> bool {
self.as_inner().is_some()
}
pub fn as_inner(&self) -> Option<&syn::Type> {
let syn::Type::Path(type_path) = &self.ty else {
return None;
};
let segment = type_path.path.segments.last()?;
if segment.ident != "Option" {
return None;
}
let syn::PathArguments::AngleBracketed(args) = &segment.arguments else {
return None;
};
let syn::GenericArgument::Type(inner) = args.args.first()? else {
return None;
};
Some(inner)
}
pub fn is_bool(&self) -> bool {
matches!(&self.ty, syn::Type::Path(p) if p.path.is_ident("bool"))
}
pub fn docs(&self) -> Vec<&syn::Attribute> {
self.raw_attrs
.iter()
.filter(|a| a.path().is_ident("doc"))
.collect()
}
pub fn default_value(&self) -> Option<proc_macro2::TokenStream> {
self.attrs.iter().find_map(|attr| {
attr.args()
.iter()
.find(|arg| arg.path().is_ident("default"))
.and_then(|arg| arg.as_value_tokens())
})
}
}
impl quote::ToTokens for Field {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
let attrs = self.attrs();
let vis = self.vis();
let name = self.name();
let ty = self.ty();
tokens.extend(quote! {
#[#attrs] #vis #name : #ty,
});
}
}
#[derive(Clone, PartialEq, Eq)]
pub enum FieldName {
Index(syn::Index),
Ident(syn::Ident),
}
impl From<syn::Index> for FieldName {
fn from(value: syn::Index) -> Self {
Self::Index(value)
}
}
impl From<syn::Ident> for FieldName {
fn from(value: syn::Ident) -> Self {
Self::Ident(value)
}
}
impl quote::ToTokens for FieldName {
fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
tokens.extend(match self {
Self::Index(v) => quote!(#v),
Self::Ident(v) => quote!(#v),
});
}
}
impl std::fmt::Display for FieldName {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Ident(v) => write!(f, "{}", v),
Self::Index(v) => write!(f, "{}", v.index),
}
}
}