use proc_macro::TokenStream;
use quote::quote;
use syn::{
parse_macro_input, punctuated::Punctuated, Data, DeriveInput, Fields, Meta, Token,
};
pub fn derive_newtype(input: TokenStream) -> TokenStream {
let DeriveInput { ident, data, attrs, .. } = parse_macro_input!(input);
let mut derive_deref = false;
let mut derive_deref_mut = false;
let mut derive_from = false;
for attr in attrs {
if attr.meta.path().is_ident("newtype") {
let nested = attr
.parse_args_with(Punctuated::<Meta, Token![,]>::parse_terminated)
.unwrap();
for meta in nested {
if meta.path().is_ident("Deref") {
derive_deref = true;
} else if meta.path().is_ident("DerefMut") {
derive_deref_mut = true;
} else if meta.path().is_ident("From") {
derive_from = true;
}
}
}
}
let mut impls = Vec::new();
let inner = match data {
Data::Struct(data) => match data.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => {
fields.unnamed.first().unwrap().ty.clone()
}
_ => panic!("expecting a unit newtype struct"),
},
_ => panic!("expecting a unit newtype struct"),
};
if derive_deref {
impls.push(quote! {
impl std::ops::Deref for #ident {
type Target = #inner;
fn deref(&self) -> &Self::Target {
&self.0
}
}
});
}
if derive_deref_mut {
impls.push(quote! {
impl std::ops::DerefMut for #ident {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
});
}
if derive_from {
impls.push(quote! {
impl From<#inner> for #ident {
fn from(inner: #inner) -> Self {
Self(inner)
}
}
});
}
let expanded = quote! {
#(#impls)*
};
TokenStream::from(expanded)
}