extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro2::{Literal, Span};
use quote::quote;
use syn::{parse, parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Ident};
pub fn debug_gen(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let mut generics = input.generics;
for param in &mut generics.params {
if let GenericParam::Type(type_param) = param {
type_param.bounds.push(parse_quote!(tfmt::uDebug));
}
}
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let ident = &input.ident;
let ts = match input.data {
Data::Struct(data) => {
let ident_s = ident.to_string();
let body = match data.fields {
Fields::Named(fields) => {
let fields = fields
.named
.iter()
.map(|field| {
let ident = field.ident.as_ref().expect("UNREACHABLE");
let name = ident.to_string();
quote!(field(#name, &self.#ident)?)
})
.collect::<Vec<_>>();
quote!(f.debug_struct(#ident_s)?#(.#fields)*.finish())
}
Fields::Unnamed(fields) => {
let fields = (0..fields.unnamed.len())
.map(|i| {
let i = Literal::u64_unsuffixed(i as u64);
quote!(field(&self.#i)?)
})
.collect::<Vec<_>>();
quote!(f.debug_tuple(#ident_s)?#(.#fields)*.finish())
}
Fields::Unit => quote!(f.write_str(#ident_s)),
};
quote!(
impl #impl_generics tfmt::uDebug for #ident #ty_generics #where_clause {
fn fmt<W>(&self, f: &mut tfmt::Formatter<'_, W>) -> core::result::Result<(), W::Error>
where
W: tfmt::uWrite + ?Sized,
{
#body
}
}
)
}
Data::Enum(data) => {
let arms = data
.variants
.iter()
.map(|var| {
let variant = &var.ident;
let variant_s = variant.to_string();
match &var.fields {
Fields::Named(fields) => {
let mut pats = Vec::with_capacity(fields.named.len());
let mut methods = Vec::with_capacity(fields.named.len());
for field in &fields.named {
let ident = field.ident.as_ref().unwrap();
let ident_s = ident.to_string();
pats.push(quote!(#ident));
methods.push(quote!(field(#ident_s, #ident)?));
}
quote!(
#ident::#variant { #(#pats),* } => {
f.debug_struct(#variant_s)?#(.#methods)*.finish()
}
)
}
Fields::Unnamed(fields) => {
let pats = &(0..fields.unnamed.len())
.map(|i| Ident::new(&format!("_{}", i), Span::call_site()))
.collect::<Vec<_>>();
quote!(
#ident::#variant(#(#pats),*) => {
f.debug_tuple(#variant_s)?#(.field(#pats)?)*.finish()
}
)
}
Fields::Unit => quote!(
#ident::#variant => {
f.write_str(#variant_s)
}
),
}
})
.collect::<Vec<_>>();
let body = if arms.is_empty() {
quote!(unsafe { core::unreachable!() })
} else {
quote!(
match self {
#(#arms),*
}
)
};
quote!(
impl #impl_generics tfmt::uDebug for #ident #ty_generics #where_clause {
fn fmt<W>(&self, f: &mut tfmt::Formatter<'_, W>) -> core::result::Result<(), W::Error>
where
W: tfmt::uWrite + ?Sized,
{
#body
}
}
)
}
Data::Union(..) => {
return parse::Error::new(Span::call_site(), "this trait cannot be derived for unions")
.to_compile_error()
.into();
}
};
ts.into()
}