use alloc::{format, vec::Vec};
use proc_macro2::TokenStream;
use syn::{FieldsNamed, FieldsUnnamed, Generics, Ident, Type};
use super::common::{FieldRef, Format, ImportRoot, InlineOptions, Transparent};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct Structure {
pub inline_opts: Option<InlineOptions>,
pub root_import: Option<ImportRoot>,
pub name_ident: Ident,
pub generics: Generics,
pub field_list: FieldList,
pub options: StructureOptions,
}
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[allow(clippy::large_enum_variant)]
pub enum StructureOptions {
Standalone {
source_field: Option<FieldRef>,
format_args: Format,
},
Transparent(Transparent),
Forward {
source_field: Option<FieldRef>,
field_ref: FieldRef,
field_type: Type,
format_args: Format,
},
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum FieldList {
Named(Vec<NamedField>),
Unnamed(Vec<UnnamedField>),
Unit,
}
impl FieldList {
#[inline]
pub fn raw(fields: &syn::Fields) -> syn::Result<Self> {
match fields {
syn::Fields::Named(FieldsNamed { named, .. }) => {
let field_list = named
.iter()
.map(|syn::Field { ident, ty, .. }| {
let name = ident
.clone()
.ok_or_else(|| syn::Error::new_spanned(ident, "expected a named field"))?;
let ty = ty.clone();
Ok(NamedField { name, ty })
})
.collect::<syn::Result<Vec<_>>>()?;
Ok(FieldList::Named(field_list))
}
syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
let field_list = unnamed
.iter()
.map(|syn::Field { ty, .. }| {
let ty = ty.clone();
Ok(UnnamedField { ty })
})
.collect::<syn::Result<Vec<_>>>()?;
Ok(FieldList::Unnamed(field_list))
}
syn::Fields::Unit => Ok(FieldList::Unit),
}
}
pub fn exactly_one(fields: &syn::Fields) -> syn::Result<(FieldRef, Type)> {
const DEFAULT_FIELD: usize = 0;
match fields {
syn::Fields::Named(FieldsNamed { named, .. }) => {
if named.len() != 1 {
return Err(syn::Error::new_spanned(fields, "expected exactly one field"));
}
let field = &named[DEFAULT_FIELD];
let name = field.ident.clone();
let ty = field.ty.clone();
Ok((name.map_or(FieldRef::Indexed(DEFAULT_FIELD), FieldRef::Named), ty))
}
syn::Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => {
if unnamed.len() != 1 {
return Err(syn::Error::new_spanned(fields, "expected exactly one field"));
}
let field = &unnamed[DEFAULT_FIELD];
let ty = field.ty.clone();
Ok((FieldRef::Indexed(DEFAULT_FIELD), ty))
}
syn::Fields::Unit => Err(syn::Error::new_spanned(fields, "expected at least one field")),
}
}
pub fn pattern(&self) -> syn::Result<TokenStream> {
match self {
FieldList::Named(named) => {
let pattern = named.iter().map(|field| {
let name = &field.name;
quote::quote! { ref #name }
});
Ok(quote::quote! { { #(#pattern),* } })
}
FieldList::Unnamed(unnamed) => {
let pattern = unnamed.iter().enumerate().map(|(index, _)| {
let ident = Ident::new(&format!("_{index}"), proc_macro2::Span::call_site());
quote::quote! { ref #ident }
});
Ok(quote::quote! { ( #(#pattern),* ) })
}
FieldList::Unit => Ok(quote::quote! {}),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct NamedField {
name: Ident,
ty: Type,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct UnnamedField {
ty: Type,
}