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| { dbg!(&acc, &e); quote! { #acc && #e } }) .unwrap(); let is_none_checks = field_checks .map(|tok| quote! { !(#tok) }) .reduce(|acc, e| { dbg!(&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() } }