use syn::{Data, DataStruct, DeriveInput, Fields, Type};
#[derive(Default, Debug)]
pub struct StructEntry {
pub name: String,
pub field_entries: Vec<FieldEntry>,
}
#[derive(Clone, Debug)]
pub struct FieldEntry {
pub field_name: String,
pub field_type: Type,
pub is_optional: bool,
}
impl StructEntry {
pub fn build_struct_entry(input: Box<DeriveInput>) -> syn::Result<Self> {
let struct_name = input.ident.to_string();
let fields = Self::extract_fields(&input)?;
let field_entries = fields
.iter()
.map(|field| {
let field_name = field.ident.clone().unwrap().to_string();
let is_optional = is_type_option(&field.ty);
FieldEntry {
field_name,
field_type: field.ty.clone(),
is_optional,
}
})
.collect();
Ok(Self {
name: struct_name,
field_entries,
})
}
fn extract_fields(
input: &DeriveInput,
) -> syn::Result<&syn::punctuated::Punctuated<syn::Field, syn::token::Comma>> {
match &input.data {
Data::Struct(DataStruct {
fields: Fields::Named(named_fields),
..
}) => Ok(&named_fields.named),
_ => Err(syn::Error::new_spanned(
input.ident.clone(),
"DtoMapper only supports structs with named fields",
)),
}
}
}
pub fn is_type_option(a_type: &Type) -> bool {
if let Type::Path(path_type) = a_type {
if let Some(segment) = path_type.path.segments.first() {
if segment.ident == "Option" {
return true;
}
}
}
false
}