use proc_macro::TokenStream;
use quote::quote;
use syn::{Data, DeriveInput, Fields, PathArguments, Type, parse_macro_input};
#[proc_macro_derive(SilentDebug)]
pub fn silent_debug(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics ::core::fmt::Debug for #name #ty_generics #where_clause {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "<elided secret for {}>", stringify!(#name))
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(SilentDisplay)]
pub fn silent_display(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let name = &ast.ident;
let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
let expanded = quote! {
impl #impl_generics ::core::fmt::Display for #name #ty_generics #where_clause {
fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
write!(f, "<elided secret for {}>", stringify!(#name))
}
}
};
TokenStream::from(expanded)
}
#[proc_macro_derive(WordWrapper)]
pub fn word_wrapper_derive(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let name = &input.ident;
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let field_type = match &input.data {
Data::Struct(data_struct) => match &data_struct.fields {
Fields::Unnamed(fields) if fields.unnamed.len() == 1 => match fields.unnamed.first() {
Some(field) => &field.ty,
None => {
return syn::Error::new_spanned(
&input,
"WordWrapper requires exactly one field",
)
.to_compile_error()
.into();
},
},
_ => {
return syn::Error::new_spanned(
&input,
"WordWrapper can only be derived for tuple structs with exactly one field",
)
.to_compile_error()
.into();
},
},
_ => {
return syn::Error::new_spanned(&input, "WordWrapper can only be derived for structs")
.to_compile_error()
.into();
},
};
let word_type = if let Type::Path(type_path) = field_type {
let Some(segment) = type_path.path.segments.last() else {
return syn::Error::new_spanned(
field_type,
"WordWrapper can only be derived for types wrapping a 'Word' field",
)
.to_compile_error()
.into();
};
if segment.ident != "Word" {
return syn::Error::new_spanned(
field_type,
"WordWrapper can only be derived for types wrapping a 'Word' field",
)
.to_compile_error()
.into();
}
if !matches!(segment.arguments, PathArguments::None) {
return syn::Error::new_spanned(
field_type,
"WordWrapper can only be derived for types wrapping a 'Word' field",
)
.to_compile_error()
.into();
}
field_type
} else {
return syn::Error::new_spanned(
field_type,
"WordWrapper can only be derived for types wrapping a 'Word' field",
)
.to_compile_error()
.into();
};
let expanded = quote! {
impl #impl_generics #name #ty_generics #where_clause {
pub fn from_raw(word: #word_type) -> Self {
Self(word)
}
pub fn as_elements(&self) -> &[<#word_type as ::core::ops::Index<usize>>::Output] {
self.0.as_elements()
}
pub fn as_bytes(&self) -> [u8; 32] {
self.0.as_bytes()
}
pub fn to_hex(&self) -> String {
self.0.to_hex()
}
pub fn as_word(&self) -> #word_type {
self.0
}
}
};
TokenStream::from(expanded)
}