use proc_macro2::{Ident, Span};
use quote::format_ident;
use syn::{Fields, GenericArgument, Index, Member, PathArguments, Type};
#[derive(Debug)]
pub struct FieldView<'a> {
pub binding: Ident,
pub member: Member,
pub ty: &'a Type,
pub is_option: bool,
}
pub trait FieldsExt {
fn views(&self) -> Vec<FieldView<'_>>;
}
impl FieldsExt for Fields {
fn views(&self) -> Vec<FieldView<'_>> {
self.iter()
.enumerate()
.map(|(i, field)| {
let binding = field.ident.clone().unwrap_or_else(|| format_ident!("v{}", i));
let member: Member = match &field.ident {
Some(name) => Member::Named(name.clone()),
None => Member::Unnamed(Index { index: i as u32, span: Span::call_site() }),
};
let ty: &Type = match &field.ty {
Type::Reference(r) => r.elem.as_ref(),
t => t,
};
let is_option = option_inner(ty).is_some();
FieldView { binding, member, ty, is_option }
})
.collect()
}
}
pub(crate) fn option_inner(ty: &Type) -> Option<&Type> {
if let Type::Path(path) = ty
&& let Some(seg) = path.path.segments.last()
&& seg.ident == "Option"
&& let PathArguments::AngleBracketed(args) = &seg.arguments
&& let Some(GenericArgument::Type(inner)) = args.args.first()
{
return Some(inner);
}
None
}