use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput, Data, Fields, Error};
#[allow(unused_imports)]
use struct_validation_core::ValidationError;
#[proc_macro_derive(Validate)]
pub fn derive_validate(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
let struct_name = &input.ident;
let fields = if let Data::Struct(data) = &input.data {
match &data.fields {
Fields::Named(fields) => &fields.named,
_ => {
return Error::new_spanned(
struct_name,
"Validate can only be derived for structs with named fields",
)
.to_compile_error()
.into();
}
}
} else {
return Error::new_spanned(
struct_name,
"Validate can only be derived for structs",
)
.to_compile_error()
.into();
};
let validator_iters = fields.iter().map(|field| {
let field_name = &field.ident;
let field_name_str = field_name.as_ref().unwrap().to_string();
quote! {
self.#field_name.validate()
.into_iter()
.map(|mut e| { e.add_prefix(#field_name_str); e })
}
});
let stream = validator_iters.reduce(|acc, stream| {
quote! {
#acc.chain(#stream)
}
}).unwrap_or_else(|| quote! { std::iter::empty() });
let expanded = quote! {
impl Validate for #struct_name {
fn validate(&self) -> Vec<struct_validation::ValidationError> {
#stream.collect()
}
}
};
TokenStream::from(expanded)
}