1#![warn(
12 anonymous_parameters,
13 bare_trait_objects,
14 elided_lifetimes_in_paths,
15 missing_copy_implementations,
16 rust_2018_idioms,
17 trivial_casts,
18 trivial_numeric_casts,
19 unreachable_pub,
20 unsafe_code,
21 unused_extern_crates,
22 unused_import_braces
23)]
24#![warn(
26 clippy::all,
27 clippy::cargo,
28 clippy::dbg_macro,
29 clippy::float_cmp_const,
30 clippy::get_unwrap,
31 clippy::mem_forget,
32 clippy::nursery,
33 clippy::pedantic,
34 clippy::todo,
35 clippy::unwrap_used,
36 clippy::uninlined_format_args
37)]
38#![allow(
40 clippy::default_trait_access,
41 clippy::doc_markdown,
42 clippy::if_not_else,
43 clippy::module_name_repetitions,
44 clippy::multiple_crate_versions,
45 clippy::must_use_candidate,
46 clippy::needless_pass_by_value,
47 clippy::needless_ifs,
48 clippy::use_self,
49 clippy::cargo_common_metadata,
50 clippy::missing_errors_doc,
51 clippy::enum_glob_use,
52 clippy::struct_excessive_bools,
53 clippy::missing_const_for_fn,
54 clippy::redundant_pub_crate,
55 clippy::result_large_err,
56 clippy::future_not_send,
57 clippy::option_if_let_else,
58 clippy::from_over_into,
59 clippy::manual_inspect
60)]
61#![cfg_attr(test, allow(clippy::non_ascii_literal, clippy::unwrap_used))]
63
64#[allow(unused_extern_crates)]
65extern crate proc_macro;
66
67use proc_macro2::{Ident, TokenStream};
68use proc_macro_crate::{crate_name, FoundCrate};
69use quote::{format_ident, quote};
70use syn::{parse_macro_input, parse_quote, spanned::Spanned, Data, DeriveInput, Result};
71
72mod derive_enum;
73mod derive_struct;
74mod generics;
75mod strategy;
76mod transform;
77mod types;
78use derive_enum::derive_enum;
79use derive_struct::derive_struct;
80use generics::{add_classified_value_bounds, add_debug_bounds, add_walker_bounds};
81
82#[proc_macro_derive(Sensitive, attributes(sensitive))]
103pub fn derive_sensitive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
104 let input = parse_macro_input!(input as DeriveInput);
105 match expand(input) {
106 Ok(tokens) => tokens.into(),
107 Err(err) => err.into_compile_error().into(),
108 }
109}
110
111fn crate_root() -> proc_macro2::TokenStream {
116 match crate_name("redaction") {
117 Ok(FoundCrate::Itself) => quote! { crate },
118 Ok(FoundCrate::Name(name)) => {
119 let ident = format_ident!("{}", name);
120 quote! { ::#ident }
121 }
122 Err(_) => quote! { ::redaction },
123 }
124}
125
126fn crate_path(item: &str) -> proc_macro2::TokenStream {
127 let root = crate_root();
128 let item_ident = syn::parse_str::<syn::Path>(item).expect("redaction crate path should parse");
129 quote! { #root::#item_ident }
130}
131
132struct DeriveOutput {
133 redaction_body: TokenStream,
134 used_generics: Vec<Ident>,
135 classified_generics: Vec<Ident>,
136 debug_redacted_body: TokenStream,
137 debug_redacted_generics: Vec<Ident>,
138 debug_unredacted_body: TokenStream,
139 debug_unredacted_generics: Vec<Ident>,
140}
141
142#[allow(clippy::too_many_lines)]
143fn expand(input: DeriveInput) -> Result<TokenStream> {
144 let DeriveInput {
145 ident,
146 generics,
147 data,
148 ..
149 } = input;
150
151 let crate_root = crate_root();
152
153 let derive_output = match &data {
154 Data::Struct(data) => {
155 let output = derive_struct(&ident, data.clone(), &generics)?;
156 DeriveOutput {
157 redaction_body: output.redaction_body,
158 used_generics: output.used_generics,
159 classified_generics: output.classified_generics,
160 debug_redacted_body: output.debug_redacted_body,
161 debug_redacted_generics: output.debug_redacted_generics,
162 debug_unredacted_body: output.debug_unredacted_body,
163 debug_unredacted_generics: output.debug_unredacted_generics,
164 }
165 }
166 Data::Enum(data) => {
167 let output = derive_enum(&ident, data.clone(), &generics)?;
168 DeriveOutput {
169 redaction_body: output.redaction_body,
170 used_generics: output.used_generics,
171 classified_generics: output.classified_generics,
172 debug_redacted_body: output.debug_redacted_body,
173 debug_redacted_generics: output.debug_redacted_generics,
174 debug_unredacted_body: output.debug_unredacted_body,
175 debug_unredacted_generics: output.debug_unredacted_generics,
176 }
177 }
178 Data::Union(u) => {
179 return Err(syn::Error::new(
180 u.union_token.span(),
181 "`Sensitive` cannot be derived for unions",
182 ));
183 }
184 };
185
186 let classify_generics = add_walker_bounds(generics.clone(), &derive_output.used_generics);
187 let classify_generics =
188 add_classified_value_bounds(classify_generics, &derive_output.classified_generics);
189 let (impl_generics, ty_generics, where_clause) = classify_generics.split_for_impl();
190 let debug_redacted_generics =
191 add_debug_bounds(generics.clone(), &derive_output.debug_redacted_generics);
192 let (debug_redacted_impl_generics, debug_redacted_ty_generics, debug_redacted_where_clause) =
193 debug_redacted_generics.split_for_impl();
194 let debug_unredacted_generics =
195 add_debug_bounds(generics.clone(), &derive_output.debug_unredacted_generics);
196 let (
197 debug_unredacted_impl_generics,
198 debug_unredacted_ty_generics,
199 debug_unredacted_where_clause,
200 ) = debug_unredacted_generics.split_for_impl();
201 let redaction_body = &derive_output.redaction_body;
202 let debug_redacted_body = &derive_output.debug_redacted_body;
203 let debug_unredacted_body = &derive_output.debug_unredacted_body;
204 let mut slog_generics = generics;
205 let slog_where_clause = slog_generics.make_where_clause();
206 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
207 slog_where_clause
208 .predicates
209 .push(parse_quote!(#self_ty: ::core::clone::Clone));
210 slog_where_clause
211 .predicates
212 .push(parse_quote!(#self_ty: #crate_root::slog::IntoRedactedJson));
213 let (slog_impl_generics, slog_ty_generics, slog_where_clause) = slog_generics.split_for_impl();
214 let trait_impl = quote! {
215 impl #impl_generics #crate_root::RedactionWalker for #ident #ty_generics #where_clause {
216 type Output = Self;
217
218 fn redact_with<M: #crate_root::RedactionMap>(self, mapper: &M) -> Self::Output {
219 use #crate_root::RedactionWalker as _;
220 #redaction_body
221 }
222 }
223
224 #[cfg(any(test, feature = "testing"))]
225 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
226 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
227 #debug_unredacted_body
228 }
229 }
230
231 #[cfg(not(any(test, feature = "testing")))]
232 #[allow(unused_variables)]
233 impl #debug_redacted_impl_generics ::core::fmt::Debug for #ident #debug_redacted_ty_generics #debug_redacted_where_clause {
234 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
235 #debug_redacted_body
236 }
237 }
238
239 #[cfg(feature = "slog")]
240 impl #slog_impl_generics ::slog::Value for #ident #slog_ty_generics #slog_where_clause {
241 fn serialize(
242 &self,
243 _record: &::slog::Record<'_>,
244 key: ::slog::Key,
245 serializer: &mut dyn ::slog::Serializer,
246 ) -> ::slog::Result {
247 let redacted = #crate_root::slog::IntoRedactedJson::into_redacted_json(self.clone());
248 ::slog::Value::serialize(&redacted, _record, key, serializer)
249 }
250 }
251
252 };
255 Ok(trait_impl)
256}