#![allow(clippy::all)]
#![forbid(unsafe_code)]
use crate::codegen::enums::EnumGen;
use crate::codegen::sanitizers as sanitizer_gen;
use crate::codegen::structs::StructGen;
use crate::sanitizer::parse_sanitizers;
use crate::type_ident::TypeOrNested;
use proc_macro::TokenStream;
use proc_macro2::TokenStream as TokenStream2;
use quote::{quote, TokenStreamExt};
use syn::{parse_macro_input, DeriveInput};
mod arg;
mod codegen;
mod sanitizer;
mod sanitizers;
mod type_ident;
#[proc_macro_derive(Sanitize, attributes(sanitize))]
pub fn sanitize(input: TokenStream) -> TokenStream {
let input_parsed = parse_macro_input!(input as DeriveInput);
let name = input_parsed.ident;
let mut inner_body: TokenStream2 = Default::default();
let parsed = parse_sanitizers(input_parsed.data);
if let Ok(ref val) = parsed {
inner_body.append_all(val.get_map().iter().map(|r| {
let field = r.0;
let mut body = quote! {};
match field {
TypeOrNested::Type(field, type_ident) => {
let sanitizer_calls = sanitizer_gen::methods_layout(r.1, type_ident.clone());
let stream: TokenStream2;
if val.is_enum() {
stream = EnumGen::new(field.clone(), type_ident).body(sanitizer_calls);
} else {
stream = StructGen::new(field.clone(), type_ident).body(sanitizer_calls);
}
body.append_all(stream)
}
TypeOrNested::Nested(field, type_ident) => {
let call = quote! { <#type_ident as Sanitize>::sanitize };
if val.is_enum() {
body.append_all({
quote! {
if let Self::#field(x) = self {
#call(x);
}
}
});
} else {
body.append_all({
quote! {
#call(&mut self.#field);
}
});
}
}
}
quote! {
#body
}
}));
} else {
let err = parsed.err().unwrap().to_string();
inner_body = quote! { compile_error!(#err) };
}
let final_body = quote! {
impl sanitizer::Sanitize for #name {
fn sanitize(&mut self) {
#inner_body
}
}
};
TokenStream::from(final_body)
}