1#![warn(
14 anonymous_parameters,
15 bare_trait_objects,
16 elided_lifetimes_in_paths,
17 missing_copy_implementations,
18 rust_2018_idioms,
19 trivial_casts,
20 trivial_numeric_casts,
21 unreachable_pub,
22 unsafe_code,
23 unused_extern_crates,
24 unused_import_braces
25)]
26#![warn(
28 clippy::all,
29 clippy::cargo,
30 clippy::dbg_macro,
31 clippy::float_cmp_const,
32 clippy::get_unwrap,
33 clippy::mem_forget,
34 clippy::nursery,
35 clippy::pedantic,
36 clippy::todo,
37 clippy::unwrap_used,
38 clippy::uninlined_format_args
39)]
40#![allow(
42 clippy::default_trait_access,
43 clippy::doc_markdown,
44 clippy::if_not_else,
45 clippy::module_name_repetitions,
46 clippy::multiple_crate_versions,
47 clippy::must_use_candidate,
48 clippy::needless_pass_by_value,
49 clippy::needless_ifs,
50 clippy::use_self,
51 clippy::cargo_common_metadata,
52 clippy::missing_errors_doc,
53 clippy::enum_glob_use,
54 clippy::struct_excessive_bools,
55 clippy::missing_const_for_fn,
56 clippy::redundant_pub_crate,
57 clippy::result_large_err,
58 clippy::future_not_send,
59 clippy::option_if_let_else,
60 clippy::from_over_into,
61 clippy::manual_inspect
62)]
63#![cfg_attr(test, allow(clippy::non_ascii_literal, clippy::unwrap_used))]
65
66#[allow(unused_extern_crates)]
67extern crate proc_macro;
68
69use proc_macro_crate::{FoundCrate, crate_name};
70use proc_macro2::Span;
71use proc_macro2::{Ident, TokenStream};
72use quote::{format_ident, quote};
73use syn::{
74 Data, DataEnum, DataStruct, DeriveInput, Fields, Result, parse_macro_input, parse_quote,
75 spanned::Spanned,
76};
77
78mod container;
79mod derive_enum;
80mod derive_struct;
81mod generics;
82mod policy_guard;
83mod redacted_display;
84mod strategy;
85mod transform;
86mod types;
87use container::{ContainerOptions, parse_container_options};
88use derive_enum::derive_enum;
89use derive_struct::derive_struct;
90use generics::{add_predicates, push_debug_predicate};
91use redacted_display::derive_redacted_display;
92
93#[proc_macro_derive(Sensitive, attributes(sensitive, not_sensitive))]
137pub fn derive_sensitive_container(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
138 let input = parse_macro_input!(input as DeriveInput);
139 match expand(input, DeriveKind::Sensitive) {
140 Ok(tokens) => tokens.into(),
141 Err(err) => err.into_compile_error().into(),
142 }
143}
144
145#[proc_macro_derive(NotSensitive, attributes(not_sensitive))]
173pub fn derive_not_sensitive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
174 let input = parse_macro_input!(input as DeriveInput);
175 match expand_not_sensitive(input) {
176 Ok(tokens) => tokens.into(),
177 Err(err) => err.into_compile_error().into(),
178 }
179}
180
181fn reject_sensitivity_attrs(attrs: &[syn::Attribute], data: &Data, macro_name: &str) -> Result<()> {
187 let check_attr = |attr: &syn::Attribute| -> Result<()> {
188 if attr.path().is_ident("sensitive") {
189 return Err(syn::Error::new(
190 attr.span(),
191 format!("`#[sensitive]` attributes are not allowed on `{macro_name}` types"),
192 ));
193 }
194 if attr.path().is_ident("not_sensitive") {
195 return Err(syn::Error::new(
196 attr.span(),
197 format!(
198 "`#[not_sensitive]` attributes are not needed on `{macro_name}` types (the entire type is already non-sensitive)"
199 ),
200 ));
201 }
202 Ok(())
203 };
204
205 for attr in attrs {
206 check_attr(attr)?;
207 }
208
209 match data {
210 Data::Struct(data) => {
211 for field in &data.fields {
212 for attr in &field.attrs {
213 check_attr(attr)?;
214 }
215 }
216 }
217 Data::Enum(data) => {
218 for variant in &data.variants {
219 for attr in &variant.attrs {
220 check_attr(attr)?;
221 }
222 for field in &variant.fields {
223 for attr in &field.attrs {
224 check_attr(attr)?;
225 }
226 }
227 }
228 }
229 Data::Union(_) => {}
230 }
231
232 Ok(())
233}
234
235fn expand_not_sensitive(input: DeriveInput) -> Result<TokenStream> {
236 let DeriveInput {
237 ident,
238 generics,
239 data,
240 attrs,
241 ..
242 } = input;
243
244 if let Data::Union(u) = &data {
246 return Err(syn::Error::new(
247 u.union_token.span(),
248 "`NotSensitive` cannot be derived for unions",
249 ));
250 }
251
252 reject_sensitivity_attrs(&attrs, &data, "NotSensitive")?;
253
254 let crate_root = crate_root();
255 let mapper_type = internal_ident("__RedactableMapper");
256 let mapper = internal_ident("__redactable_mapper");
257
258 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
261 let container_impl = quote! {
262 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
263 fn redact_with<#mapper_type: #crate_root::RedactableMapper>(self, #mapper: &#mapper_type) -> Self {
264 self
265 }
266 }
267
268 impl #impl_generics #crate_root::Redactable for #ident #ty_generics #where_clause {}
269 };
270
271 #[cfg(feature = "slog")]
273 let slog_impl = {
274 let slog_crate = quote! { #crate_root::__private::slog };
275 let mut slog_generics = generics.clone();
276 let (_, ty_generics, _) = slog_generics.split_for_impl();
277 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
278 slog_generics
279 .make_where_clause()
280 .predicates
281 .push(parse_quote!(#self_ty: #crate_root::__private::serde::Serialize));
282 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
283 slog_generics.split_for_impl();
284 quote! {
285 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
286 fn serialize(
287 &self,
288 _record: &#slog_crate::Record<'_>,
289 key: #slog_crate::Key,
290 serializer: &mut dyn #slog_crate::Serializer,
291 ) -> #slog_crate::Result {
292 #crate_root::slog::__slog_serialize_not_sensitive(self, _record, key, serializer)
293 }
294 }
295
296 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
297 }
298 };
299
300 #[cfg(not(feature = "slog"))]
301 let slog_impl = quote! {};
302
303 #[cfg(feature = "tracing")]
305 let tracing_impl = {
306 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
307 generics.split_for_impl();
308 quote! {
309 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
310 }
311 };
312
313 #[cfg(not(feature = "tracing"))]
314 let tracing_impl = quote! {};
315
316 Ok(quote! {
317 #container_impl
318 #slog_impl
319 #tracing_impl
320 })
321}
322
323#[proc_macro_derive(NotSensitiveDisplay)]
371pub fn derive_not_sensitive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
372 let input = parse_macro_input!(input as DeriveInput);
373 match expand_not_sensitive_display(input) {
374 Ok(tokens) => tokens.into(),
375 Err(err) => err.into_compile_error().into(),
376 }
377}
378
379fn expand_not_sensitive_display(input: DeriveInput) -> Result<TokenStream> {
380 let DeriveInput {
381 ident,
382 generics,
383 data,
384 attrs,
385 ..
386 } = input;
387
388 if let Data::Union(u) = &data {
390 return Err(syn::Error::new(
391 u.union_token.span(),
392 "`NotSensitiveDisplay` cannot be derived for unions",
393 ));
394 }
395
396 reject_sensitivity_attrs(&attrs, &data, "NotSensitiveDisplay")?;
397
398 let crate_root = crate_root();
399 let mapper_type = internal_ident("__RedactableMapper");
400 let mapper = internal_ident("__redactable_mapper");
401
402 let (container_impl_generics, container_ty_generics, container_where_clause) =
407 generics.split_for_impl();
408 let container_impl = quote! {
409 impl #container_impl_generics #crate_root::RedactableWithMapper for #ident #container_ty_generics #container_where_clause {
410 fn redact_with<#mapper_type: #crate_root::RedactableMapper>(self, #mapper: &#mapper_type) -> Self {
411 self
412 }
413 }
414
415 impl #container_impl_generics #crate_root::Redactable for #ident #container_ty_generics #container_where_clause {}
416 };
417
418 let mut display_generics = generics.clone();
421 let (_, display_ty, _) = generics.split_for_impl();
422 let display_self_ty: syn::Type = parse_quote!(#ident #display_ty);
423 let display_where_clause = display_generics.make_where_clause();
424 display_where_clause
425 .predicates
426 .push(parse_quote!(#display_self_ty: ::core::fmt::Display));
427
428 let (display_impl_generics, display_ty_generics, display_where_clause) =
429 display_generics.split_for_impl();
430
431 let redacted_display_impl = quote! {
435 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
436 fn fmt_redacted(&self, __redactable_f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
437 ::core::fmt::Display::fmt(self, __redactable_f)
438 }
439 }
440
441 impl #display_impl_generics #crate_root::ToRedactedOutput for #ident #display_ty_generics #display_where_clause {
442 fn to_redacted_output(&self) -> #crate_root::RedactedOutput {
443 #crate_root::RedactedOutput::Text(
444 #crate_root::RedactableWithFormatter::redacted_display(self).to_string(),
445 )
446 }
447 }
448 };
449
450 #[cfg(feature = "slog")]
452 let slog_impl = {
453 let slog_crate = quote! { #crate_root::__private::slog };
454 let mut slog_generics = generics.clone();
455 let (_, ty_generics, _) = slog_generics.split_for_impl();
456 let self_ty: syn::Type = syn::parse_quote!(#ident #ty_generics);
457 slog_generics
458 .make_where_clause()
459 .predicates
460 .push(syn::parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
461 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
462 slog_generics.split_for_impl();
463 quote! {
464 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
465 fn serialize(
466 &self,
467 _record: &#slog_crate::Record<'_>,
468 key: #slog_crate::Key,
469 serializer: &mut dyn #slog_crate::Serializer,
470 ) -> #slog_crate::Result {
471 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
472 serializer.emit_arguments(key, &format_args!("{}", redacted))
473 }
474 }
475
476 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
477 }
478 };
479
480 #[cfg(not(feature = "slog"))]
481 let slog_impl = quote! {};
482
483 #[cfg(feature = "tracing")]
485 let tracing_impl = {
486 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
487 generics.split_for_impl();
488 quote! {
489 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
490 }
491 };
492
493 #[cfg(not(feature = "tracing"))]
494 let tracing_impl = quote! {};
495
496 Ok(quote! {
497 #container_impl
498 #redacted_display_impl
499 #slog_impl
500 #tracing_impl
501 })
502}
503
504#[proc_macro_derive(SensitiveDisplay, attributes(sensitive, not_sensitive, error))]
537pub fn derive_sensitive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
538 let input = parse_macro_input!(input as DeriveInput);
539 match expand(input, DeriveKind::SensitiveDisplay) {
540 Ok(tokens) => tokens.into(),
541 Err(err) => err.into_compile_error().into(),
542 }
543}
544
545fn crate_root() -> proc_macro2::TokenStream {
550 match crate_name("redactable") {
551 Ok(FoundCrate::Itself) => quote! { crate },
552 Ok(FoundCrate::Name(name)) => {
553 let ident = format_ident!("{}", name);
554 quote! { ::#ident }
555 }
556 Err(_) => quote! { ::redactable },
557 }
558}
559
560fn crate_path(item: &str) -> proc_macro2::TokenStream {
561 let root = crate_root();
562 let item_ident = syn::parse_str::<syn::Path>(item).expect("redactable crate path should parse");
563 quote! { #root::#item_ident }
564}
565
566pub(crate) fn internal_ident(name: &str) -> Ident {
568 Ident::new(name, Span::mixed_site())
569}
570
571pub(crate) struct DeriveOutput {
575 pub(crate) redaction_body: TokenStream,
576 pub(crate) used_generics: Vec<syn::WherePredicate>,
577 pub(crate) policy_applicable_generics: Vec<syn::WherePredicate>,
578 pub(crate) debug_redacted_body: TokenStream,
579 pub(crate) debug_unredacted_body: TokenStream,
580 pub(crate) debug_unredacted_generics: Vec<syn::WherePredicate>,
581}
582
583struct DebugOutput {
584 body: TokenStream,
585 generics: Vec<syn::WherePredicate>,
586}
587
588enum DeriveKind {
593 Sensitive,
595 SensitiveDisplay,
597}
598
599#[allow(clippy::too_many_lines)]
600fn expand(input: DeriveInput, kind: DeriveKind) -> Result<TokenStream> {
601 let DeriveInput {
602 ident,
603 generics,
604 data,
605 attrs,
606 ..
607 } = input;
608
609 let ContainerOptions { dual } = parse_container_options(&attrs)?;
610
611 let crate_root = crate_root();
612 let policy_guards = policy_guard::generate_policy_guards(&ident, &data, &generics)?;
613 let formatter = internal_ident("__redactable_f");
614 let mapper = internal_ident("__redactable_mapper");
615 let mapper_type = internal_ident("__RedactableMapper");
616
617 let dual_pairing_assert = if dual && generics.params.is_empty() {
625 match kind {
626 DeriveKind::Sensitive => quote! {
627 const _: fn() = || {
628 fn dual_requires_sensitive_display<T: #crate_root::RedactableWithFormatter>() {}
631 dual_requires_sensitive_display::<#ident>();
632 };
633 },
634 DeriveKind::SensitiveDisplay => quote! {
635 const _: fn() = || {
636 fn dual_requires_sensitive<T: #crate_root::RedactableWithMapper>() {}
639 dual_requires_sensitive::<#ident>();
640 };
641 },
642 }
643 } else {
644 quote! {}
645 };
646
647 if matches!(kind, DeriveKind::SensitiveDisplay) {
648 let redacted_display_output = derive_redacted_display(&ident, &data, &attrs, &generics)?;
649 let redacted_display_generics =
650 add_predicates(generics.clone(), &redacted_display_output.display_generics);
651 let redacted_display_generics = add_predicates(
652 redacted_display_generics,
653 &redacted_display_output.debug_generics,
654 );
655 let redacted_display_generics = add_predicates(
656 redacted_display_generics,
657 &redacted_display_output.policy_ref_generics,
658 );
659 let redacted_display_generics = add_predicates(
660 redacted_display_generics,
661 &redacted_display_output.nested_generics,
662 );
663 let (display_impl_generics, display_ty_generics, display_where_clause) =
664 redacted_display_generics.split_for_impl();
665 let redacted_display_body = redacted_display_output.body;
666 let redacted_display_impl = quote! {
667 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
668 fn fmt_redacted(&self, #formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
669 #redacted_display_body
670 }
671 }
672 };
673 let to_redacted_output_impl = quote! {
674 impl #display_impl_generics #crate_root::ToRedactedOutput for #ident #display_ty_generics #display_where_clause {
675 fn to_redacted_output(&self) -> #crate_root::RedactedOutput {
676 #crate_root::RedactedOutput::Text(
677 #crate_root::RedactableWithFormatter::redacted_display(self).to_string(),
678 )
679 }
680 }
681 };
682
683 let debug_output = derive_unredacted_debug(&ident, &data, &generics)?;
684 let debug_generics =
691 add_predicates(redacted_display_generics.clone(), &debug_output.generics);
692 let (debug_impl_generics, debug_ty_generics, debug_where_clause) =
693 debug_generics.split_for_impl();
694 let debug_unredacted_body = debug_output.body;
695 let debug_impl = quote! {
696 impl #debug_impl_generics ::core::fmt::Debug for #ident #debug_ty_generics #debug_where_clause {
697 fn fmt(&self, #formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
698 if ::core::cfg!(test) || #crate_root::__TESTING {
699 #debug_unredacted_body
700 } else {
701 #crate_root::RedactableWithFormatter::fmt_redacted(self, #formatter)
702 }
703 }
704 }
705 };
706
707 let slog_impl = if dual {
709 quote! {}
710 } else {
711 #[cfg(feature = "slog")]
712 {
713 let slog_crate = quote! { #crate_root::__private::slog };
714 let mut slog_generics = generics;
715 let (_, ty_generics, _) = slog_generics.split_for_impl();
716 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
717 slog_generics
718 .make_where_clause()
719 .predicates
720 .push(parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
721 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
722 slog_generics.split_for_impl();
723 quote! {
724 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
725 fn serialize(
726 &self,
727 _record: &#slog_crate::Record<'_>,
728 key: #slog_crate::Key,
729 serializer: &mut dyn #slog_crate::Serializer,
730 ) -> #slog_crate::Result {
731 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
732 serializer.emit_arguments(key, &format_args!("{}", redacted))
733 }
734 }
735
736 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
737 }
738 }
739
740 #[cfg(not(feature = "slog"))]
741 {
742 quote! {}
743 }
744 };
745
746 let tracing_impl = if dual {
747 quote! {}
748 } else {
749 #[cfg(feature = "tracing")]
750 {
751 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
752 redacted_display_generics.split_for_impl();
753 quote! {
754 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
755 }
756 }
757
758 #[cfg(not(feature = "tracing"))]
759 {
760 quote! {}
761 }
762 };
763
764 return Ok(quote! {
765 #policy_guards
766 #redacted_display_impl
767 #to_redacted_output_impl
768 #debug_impl
769 #slog_impl
770 #tracing_impl
771 #dual_pairing_assert
772 });
773 }
774
775 let derive_output = match data {
778 Data::Struct(data) => derive_struct(&ident, data, &generics)?,
779 Data::Enum(data) => derive_enum(&ident, data, &generics)?,
780 Data::Union(u) => {
781 return Err(syn::Error::new(
782 u.union_token.span(),
783 "`Sensitive` cannot be derived for unions",
784 ));
785 }
786 };
787
788 let policy_generics = add_predicates(generics.clone(), &derive_output.used_generics);
789 let policy_generics =
790 add_predicates(policy_generics, &derive_output.policy_applicable_generics);
791 let (impl_generics, ty_generics, where_clause) = policy_generics.split_for_impl();
792 #[cfg(feature = "slog")]
793 let slog_base_generics = generics.clone();
794 let debug_unredacted_generics =
797 add_predicates(generics, &derive_output.debug_unredacted_generics);
798 let (
799 debug_unredacted_impl_generics,
800 debug_unredacted_ty_generics,
801 debug_unredacted_where_clause,
802 ) = debug_unredacted_generics.split_for_impl();
803 let redaction_body = &derive_output.redaction_body;
804 let debug_redacted_body = &derive_output.debug_redacted_body;
805 let debug_unredacted_body = &derive_output.debug_unredacted_body;
806 let debug_impl = if dual {
815 quote! {}
816 } else {
817 quote! {
818 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
819 fn fmt(&self, #formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
820 if ::core::cfg!(test) || #crate_root::__TESTING {
821 #debug_unredacted_body
822 } else {
823 #debug_redacted_body
824 }
825 }
826 }
827 }
828 };
829
830 #[cfg(feature = "slog")]
831 let slog_impl = {
832 let slog_crate = quote! { #crate_root::__private::slog };
833 let mut slog_generics = slog_base_generics;
834 let slog_where_clause = slog_generics.make_where_clause();
835 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
836 slog_where_clause
837 .predicates
838 .push(parse_quote!(#self_ty: ::core::clone::Clone));
839 slog_where_clause
842 .predicates
843 .push(parse_quote!(#self_ty: #crate_root::__private::serde::Serialize));
844 slog_where_clause
845 .predicates
846 .push(parse_quote!(#self_ty: #crate_root::slog::SlogRedactedExt));
847 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
848 slog_generics.split_for_impl();
849 quote! {
850 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
851 fn serialize(
852 &self,
853 _record: &#slog_crate::Record<'_>,
854 key: #slog_crate::Key,
855 serializer: &mut dyn #slog_crate::Serializer,
856 ) -> #slog_crate::Result {
857 let redacted = #crate_root::slog::SlogRedactedExt::slog_redacted_json(self.clone());
858 #slog_crate::Value::serialize(&redacted, _record, key, serializer)
859 }
860 }
861
862 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
863 }
864 };
865
866 #[cfg(not(feature = "slog"))]
867 let slog_impl = quote! {};
868
869 #[cfg(feature = "tracing")]
870 let tracing_impl = quote! {
871 impl #impl_generics #crate_root::tracing::TracingRedacted for #ident #ty_generics #where_clause {}
872 };
873
874 #[cfg(not(feature = "tracing"))]
875 let tracing_impl = quote! {};
876
877 let trait_impl = quote! {
878 #policy_guards
879 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
880 fn redact_with<#mapper_type: #crate_root::RedactableMapper>(self, #mapper: &#mapper_type) -> Self {
881 use #crate_root::RedactableWithMapper as _;
882 #redaction_body
883 }
884 }
885
886 impl #impl_generics #crate_root::Redactable for #ident #ty_generics #where_clause {}
887
888 #debug_impl
889
890 #slog_impl
891
892 #tracing_impl
893
894 #dual_pairing_assert
895 };
896 Ok(trait_impl)
897}
898
899fn derive_unredacted_debug(
900 name: &Ident,
901 data: &Data,
902 _generics: &syn::Generics,
903) -> Result<DebugOutput> {
904 match data {
905 Data::Struct(data) => Ok(derive_unredacted_debug_struct(name, data)),
906 Data::Enum(data) => Ok(derive_unredacted_debug_enum(name, data)),
907 Data::Union(u) => Err(syn::Error::new(
908 u.union_token.span(),
909 "`SensitiveDisplay` cannot be derived for unions",
910 )),
911 }
912}
913
914fn derive_unredacted_debug_struct(name: &Ident, data: &DataStruct) -> DebugOutput {
915 let formatter = internal_ident("__redactable_f");
916 let debug = internal_ident("__redactable_debug");
917 let mut debug_generics = Vec::new();
918 match &data.fields {
919 Fields::Named(fields) => {
920 let mut bindings = Vec::new();
921 let mut debug_fields = Vec::new();
922 for field in &fields.named {
923 let ident = field
924 .ident
925 .clone()
926 .expect("named field should have identifier");
927 bindings.push(ident.clone());
928 push_debug_predicate(&mut debug_generics, &field.ty);
929 debug_fields.push(quote! {
930 #debug.field(stringify!(#ident), #ident);
931 });
932 }
933 DebugOutput {
934 body: quote! {
935 match self {
936 Self { #(#bindings),* } => {
937 let mut #debug = #formatter.debug_struct(stringify!(#name));
938 #(#debug_fields)*
939 #debug.finish()
940 }
941 }
942 },
943 generics: debug_generics,
944 }
945 }
946 Fields::Unnamed(fields) => {
947 let mut bindings = Vec::new();
948 let mut debug_fields = Vec::new();
949 for (index, field) in fields.unnamed.iter().enumerate() {
950 let ident = format_ident!("field_{index}", span = Span::mixed_site());
951 bindings.push(ident.clone());
952 push_debug_predicate(&mut debug_generics, &field.ty);
953 debug_fields.push(quote! {
954 #debug.field(#ident);
955 });
956 }
957 DebugOutput {
958 body: quote! {
959 match self {
960 Self ( #(#bindings),* ) => {
961 let mut #debug = #formatter.debug_tuple(stringify!(#name));
962 #(#debug_fields)*
963 #debug.finish()
964 }
965 }
966 },
967 generics: debug_generics,
968 }
969 }
970 Fields::Unit => DebugOutput {
971 body: quote! {
972 #formatter.write_str(stringify!(#name))
973 },
974 generics: debug_generics,
975 },
976 }
977}
978
979fn derive_unredacted_debug_enum(name: &Ident, data: &DataEnum) -> DebugOutput {
980 let formatter = internal_ident("__redactable_f");
981 let debug = internal_ident("__redactable_debug");
982 let mut debug_generics = Vec::new();
983 let mut debug_arms = Vec::new();
984 for variant in &data.variants {
985 let variant_ident = &variant.ident;
986 let debug_name = quote! { concat!(stringify!(#name), "::", stringify!(#variant_ident)) };
987 match &variant.fields {
988 Fields::Unit => {
989 debug_arms.push(quote! {
990 #name::#variant_ident => #formatter.write_str(#debug_name)
991 });
992 }
993 Fields::Named(fields) => {
994 let mut bindings = Vec::new();
995 let mut debug_fields = Vec::new();
996 for field in &fields.named {
997 let ident = field
998 .ident
999 .clone()
1000 .expect("named field should have identifier");
1001 bindings.push(ident.clone());
1002 push_debug_predicate(&mut debug_generics, &field.ty);
1003 debug_fields.push(quote! {
1004 #debug.field(stringify!(#ident), #ident);
1005 });
1006 }
1007 debug_arms.push(quote! {
1008 #name::#variant_ident { #(#bindings),* } => {
1009 let mut #debug = #formatter.debug_struct(#debug_name);
1010 #(#debug_fields)*
1011 #debug.finish()
1012 }
1013 });
1014 }
1015 Fields::Unnamed(fields) => {
1016 let mut bindings = Vec::new();
1017 let mut debug_fields = Vec::new();
1018 for (index, field) in fields.unnamed.iter().enumerate() {
1019 let ident = format_ident!("field_{index}", span = Span::mixed_site());
1020 bindings.push(ident.clone());
1021 push_debug_predicate(&mut debug_generics, &field.ty);
1022 debug_fields.push(quote! {
1023 #debug.field(#ident);
1024 });
1025 }
1026 debug_arms.push(quote! {
1027 #name::#variant_ident ( #(#bindings),* ) => {
1028 let mut #debug = #formatter.debug_tuple(#debug_name);
1029 #(#debug_fields)*
1030 #debug.finish()
1031 }
1032 });
1033 }
1034 }
1035 }
1036 let body = if debug_arms.is_empty() {
1037 quote! { match *self {} }
1038 } else {
1039 quote! {
1040 match self {
1041 #(#debug_arms),*
1042 }
1043 }
1044 };
1045 DebugOutput {
1046 body,
1047 generics: debug_generics,
1048 }
1049}
1050
1051#[cfg(all(test, feature = "slog"))]
1052mod generated_dependency_tests;
1053
1054#[cfg(all(test, feature = "slog"))]
1055#[test]
1056fn structural_generated_dependency_roots() {
1057 generated_dependency_tests::run_structural_generated_dependency_roots();
1058}