use proc_macro2::{Ident, TokenStream};
use quote::quote;
use syn::{Attribute, Expr, Field, GenericParam, Visibility, WherePredicate};
use crate::parse::FieldsFlavor;
pub struct OutputItemBase {
pub attrs: Vec<Attribute>,
pub vis: Visibility,
pub ident: Ident,
pub generic_params: Option<Vec<GenericParam>>,
pub where_clause: Vec<WherePredicate>,
}
pub struct OutputFields {
pub fields: Vec<Field>,
pub flavor_bias: FieldsFlavor,
}
pub struct OutputVariant {
pub attrs: Vec<Attribute>,
pub ident: Ident,
pub fields: OutputFields,
pub discriminant: Option<Expr>,
}
pub struct OutputStructOrUnion<const IS_UNION: bool> {
pub base: OutputItemBase,
pub fields: OutputFields,
}
pub type OutputStruct = OutputStructOrUnion<false>;
pub type OutputUnion = OutputStructOrUnion<true>;
pub struct OutputEnum {
pub base: OutputItemBase,
pub variants: Vec<OutputVariant>,
}
pub enum OutputItems {
Structs(Vec<OutputStruct>),
Enums(Vec<OutputEnum>),
Unions(Vec<OutputUnion>),
}
impl OutputFields {
fn is_unit(&self) -> bool {
self.fields.is_empty() && self.flavor_bias == FieldsFlavor::Unit
}
pub fn print(self) -> TokenStream {
let Self {
fields,
flavor_bias,
} = self;
let flavor = match fields.first() {
Some(f) => {
if f.ident.is_some() {
FieldsFlavor::Struct
} else {
FieldsFlavor::Tuple
}
}
None => flavor_bias,
};
let fields_inner = quote! { #(#fields),* };
match flavor {
FieldsFlavor::Unit => TokenStream::new(),
FieldsFlavor::Tuple => quote! { (#fields_inner) },
FieldsFlavor::Struct => quote! { {#fields_inner} },
}
}
}
impl OutputVariant {
pub fn print(self) -> TokenStream {
let OutputVariant {
attrs,
ident,
fields,
discriminant,
} = self;
let fields = fields.print();
let discriminant = discriminant.map(|d| quote! { = #d });
quote! {
#(#attrs)*
#ident #fields #discriminant
}
}
}
impl<const IS_UNION: bool> OutputStructOrUnion<IS_UNION> {
pub fn print(self) -> TokenStream {
let Self {
base:
OutputItemBase {
attrs,
vis,
ident,
generic_params,
where_clause,
},
fields,
} = self;
let generic_params = generic_params.map(|param| {
quote! { <#(#param),*> }
});
let where_clause = (!where_clause.is_empty()).then(|| quote! { where #(#where_clause),* });
let fields = if fields.is_unit() {
quote! { ; }
} else {
fields.print()
};
let kw = if IS_UNION {
quote!(union)
} else {
quote!(struct)
};
quote! {
#(#attrs)*
#vis #kw #ident #generic_params #where_clause #fields
}
}
}
impl OutputEnum {
pub fn print(self) -> TokenStream {
let Self {
base:
OutputItemBase {
attrs,
vis,
ident,
generic_params,
where_clause,
},
variants,
} = self;
let generic_params = generic_params.map(|param| {
quote! { <#(#param),*> }
});
let where_clause = (!where_clause.is_empty()).then(|| quote! { where #(#where_clause),* });
let variants = variants.into_iter().map(|v| v.print());
quote! {
#(#attrs)*
#vis enum #ident #generic_params #where_clause {
#(#variants),*
}
}
}
}
impl OutputItems {
pub fn print(self) -> TokenStream {
match self {
Self::Structs(v) => v.into_iter().map(|s| s.print()).collect(),
Self::Enums(v) => v.into_iter().map(|s| s.print()).collect(),
Self::Unions(v) => v.into_iter().map(|s| s.print()).collect(),
}
}
}