66 lines
2 KiB
Rust
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()
|
|
}
|
|
}
|