use proc_macro2::{Ident, Span, TokenStream};
use quote::quote_spanned;
use syn::Result;
use crate::{
crate_path,
generics::collect_generics_from_type,
strategy::Strategy,
types::{is_boxed_dyn_type, is_scalar_type},
};
pub(crate) struct DeriveContext<'a> {
pub(crate) generics: &'a syn::Generics,
pub(crate) container_path: &'a TokenStream,
pub(crate) used_generics: &'a mut Vec<Ident>,
pub(crate) classified_generics: &'a mut Vec<Ident>,
pub(crate) debug_redacted_generics: &'a mut Vec<Ident>,
pub(crate) debug_unredacted_generics: &'a mut Vec<Ident>,
}
pub(crate) fn generate_field_transform(
ctx: &mut DeriveContext<'_>,
ty: &syn::Type,
binding: &Ident,
span: Span,
strategy: &Strategy,
) -> Result<TokenStream> {
let container_path = ctx.container_path;
match strategy {
Strategy::PassThrough => {
collect_generics_from_type(ty, ctx.generics, ctx.debug_unredacted_generics);
Ok(TokenStream::new())
}
Strategy::Walk => {
if is_scalar_type(ty) {
Ok(quote_spanned! { span =>
let #binding = mapper.map_scalar(#binding);
})
} else if is_boxed_dyn_type(ty) {
let redact_boxed_path = crate_path("redact_boxed");
Ok(quote_spanned! { span =>
let #binding = #redact_boxed_path(#binding);
})
} else {
collect_generics_from_type(ty, ctx.generics, ctx.used_generics);
collect_generics_from_type(ty, ctx.generics, ctx.debug_redacted_generics);
collect_generics_from_type(ty, ctx.generics, ctx.debug_unredacted_generics);
Ok(quote_spanned! { span =>
let #binding = #container_path::redact_with(#binding, mapper);
})
}
}
Strategy::Classify(classification) => {
if is_scalar_type(ty) {
Err(syn::Error::new(
span,
"scalar fields cannot use a classification: use bare #[sensitive]. \
Scalars redact to their default value (0, false, etc.), \
except char which redacts to 'X'.",
))
} else {
collect_generics_from_type(ty, ctx.generics, ctx.classified_generics);
collect_generics_from_type(ty, ctx.generics, ctx.debug_unredacted_generics);
let classification = classification.clone();
let classifiable_path = crate_path("Classifiable");
Ok(quote_spanned! { span =>
let #binding = #classifiable_path::apply_classification::<#classification, _>(#binding, mapper);
})
}
}
}
}