use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use syn::{parse_macro_input, Data, DeriveInput, Error, Fields, Ident, Type};
enum StructType {
Named,
Unnamed,
}
#[proc_macro_derive(New)]
pub fn new_macro(_input: TokenStream) -> TokenStream {
let input = parse_macro_input!(_input as DeriveInput);
let name = &input.ident;
let struct_data = match &input.data {
Data::Struct(data) => {
match &data.fields {
Fields::Named(named) => {
let mut struct_fields: (StructType, Vec<(Ident, &Type)>) =
(StructType::Named, Vec::new());
let mut iter = named.named.iter();
while let Some(field) = iter.next() {
let field_name = field.ident.clone().unwrap();
struct_fields.1.push((field_name, &field.ty))
}
struct_fields
}
Fields::Unnamed(unnamed) => {
let mut struct_fields: (StructType, Vec<(Ident, &Type)>) =
(StructType::Unnamed, Vec::new());
let mut iter = unnamed.unnamed.iter().enumerate();
while let Some((i, field)) = iter.next() {
struct_fields.1.push((
Ident::new(&format!("_{}", &i), Span::call_site()).clone(),
&field.ty,
));
}
struct_fields
}
_ => {
return TokenStream::from(
Error::new_spanned(
input,
"This macro only supports Tuple and Braced Structs.",
)
.to_compile_error(),
);
}
}
}
_ => {
return TokenStream::from(
Error::new_spanned(input, "This macro can only be used on Structs.")
.to_compile_error(),
);
}
};
let mut arguements = Vec::new();
let mut names = Vec::new();
for field in struct_data.1 {
let name = field.0;
let ptype = field.1;
arguements.push(quote! { #name: #ptype });
names.push(name)
}
let expr = match struct_data.0 {
StructType::Named => {
quote! {
Self {
#(#names),*
}
}
}
StructType::Unnamed => {
quote! {
Self (
#(#names),*
)
}
}
};
let expanded = quote! {
impl #name {
fn new(#(#arguements),*) -> Self {
#expr
}
}
};
TokenStream::from(expanded)
}