none-guard/src/lib.rs

66 lines
2 KiB
Rust

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, DeriveInput};
#[proc_macro_derive(NoneGuard)]
pub fn none_guard(input: TokenStream) -> TokenStream {
let ast = parse_macro_input!(input as DeriveInput);
let struct_name = &ast.ident;
if let syn::Data::Struct(s) = &ast.data {
let field_checks = s.fields.iter().map(|field| {
let name = &field.ident;
if let syn::Type::Path(type_path) = &field.ty {
if type_path.path.segments.len() == 1 {
let segment = &type_path.path.segments.iter().next().unwrap();
if segment.ident != "Option" {
return quote! {
compile_error!("All fields must be of type Option!");
};
}
} else {
return quote! {
compile_error!("All fields must be of type Option!");
};
}
} else {
return quote! {
compile_error!("All fields must be of type Option!");
};
}
quote! { self.#name.is_some() }
});
let is_some_checks = field_checks
.clone()
.reduce(|acc, e| quote! { #acc && #e })
.unwrap();
let is_none_checks = field_checks
.map(|tok| quote! { !(#tok) })
.reduce(|acc, e| quote! { #acc && #e })
.unwrap();
quote! {
impl #struct_name {
pub fn is_all_some(&self) -> bool {
#is_some_checks
}
pub fn is_all_none(&self) -> bool {
#is_none_checks
}
}
}
.into()
} else {
quote! {
compile_error!("CheckAllNone macro can only be used with structs!");
}
.into()
}
}