use proc_macro2::TokenStream;
use quote::{quote, quote_spanned};
use syn::{
punctuated::Iter, spanned::Spanned, DataEnum, Field, Fields, GenericParam, Generics, Ident,
Type, TypeParamBound, Variant,
};
pub trait FieldExt {
fn get_name(&self) -> Option<&Ident>;
fn get_type(&self) -> &Type;
}
impl FieldExt for Field {
#[inline]
fn get_name(&self) -> Option<&Ident> {
self.ident.as_ref()
}
#[inline]
fn get_type(&self) -> &Type {
&self.ty
}
}
#[derive(Debug, Clone, Copy)]
pub enum FieldsType {
Named,
Unnamed,
Unit,
}
pub trait FieldsExt {
fn get_type(&self) -> FieldsType;
fn iter_fields(&self) -> Iter<Field>;
}
impl FieldsExt for Fields {
fn get_type(&self) -> FieldsType {
match self {
Fields::Named(_) => FieldsType::Named,
Fields::Unnamed(_) => FieldsType::Unnamed,
Fields::Unit => FieldsType::Unit,
}
}
#[inline]
fn iter_fields(&self) -> Iter<Field> {
self.iter()
}
}
pub trait VariantExt {
fn get_name(&self) -> &Ident;
}
impl VariantExt for Variant {
#[inline]
fn get_name(&self) -> &Ident {
&self.ident
}
}
pub trait DataEnumExt {
fn iter_variants(&self) -> Iter<Variant>;
fn name_unnamed(&mut self);
}
impl DataEnumExt for DataEnum {
#[inline]
fn iter_variants(&self) -> Iter<Variant> {
self.variants.iter()
}
fn name_unnamed(&mut self) {
self.variants.iter_mut().for_each(|variant| {
variant
.fields
.iter_mut()
.enumerate()
.for_each(|(i, field)| {
let span = field.span();
if field.ident.is_none() {
field.ident = Some(Ident::new(&format!("field_{}", i), span));
}
})
});
}
}
pub fn add_trait_bounds(mut generics: Generics, bound: TypeParamBound) -> Generics {
for param in &mut generics.params {
if let GenericParam::Type(ref mut type_param) = *param {
type_param.bounds.push(bound.clone());
}
}
generics
}
pub fn get_variant_pattern_match_expr(
fields: Iter<Field>,
fields_type: FieldsType,
add_ref: bool,
) -> TokenStream {
let fields = fields.map(|f| {
let field_name = f
.get_name()
.expect("Fields should have a name when writing pattern matching expression");
if add_ref {
quote_spanned! {f.span()=>
ref #field_name
}
} else {
quote_spanned! {f.span()=>
#field_name
}
}
});
match fields_type {
FieldsType::Named => {
quote! {
{
#(#fields,)*
}
}
}
FieldsType::Unnamed => {
quote! {
(
#(#fields,)*
)
}
}
FieldsType::Unit => quote!(),
}
}