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};
70#[cfg(feature = "slog")]
71use proc_macro2::Span;
72use proc_macro2::{Ident, TokenStream};
73use quote::{format_ident, quote};
74#[cfg(feature = "slog")]
75use syn::parse_quote;
76use syn::{
77 Data, DataEnum, DataStruct, DeriveInput, Fields, Result, parse_macro_input, spanned::Spanned,
78};
79
80mod container;
81mod derive_enum;
82mod derive_struct;
83mod generics;
84mod redacted_display;
85mod strategy;
86mod transform;
87mod types;
88use container::{ContainerOptions, parse_container_options};
89use derive_enum::derive_enum;
90use derive_struct::derive_struct;
91use generics::{
92 add_container_bounds, add_debug_bounds, add_display_bounds, add_policy_applicable_bounds,
93 add_policy_applicable_ref_bounds, add_redacted_display_bounds, collect_generics_from_type,
94};
95use redacted_display::derive_redacted_display;
96
97#[proc_macro_derive(Sensitive, attributes(sensitive, not_sensitive))]
135pub fn derive_sensitive_container(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
136 let input = parse_macro_input!(input as DeriveInput);
137 match expand(input, SlogMode::RedactedJson) {
138 Ok(tokens) => tokens.into(),
139 Err(err) => err.into_compile_error().into(),
140 }
141}
142
143#[proc_macro_derive(NotSensitive, attributes(not_sensitive))]
169pub fn derive_not_sensitive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
170 let input = parse_macro_input!(input as DeriveInput);
171 match expand_not_sensitive(input) {
172 Ok(tokens) => tokens.into(),
173 Err(err) => err.into_compile_error().into(),
174 }
175}
176
177fn reject_sensitivity_attrs(attrs: &[syn::Attribute], data: &Data, macro_name: &str) -> Result<()> {
183 let check_attr = |attr: &syn::Attribute| -> Result<()> {
184 if attr.path().is_ident("sensitive") {
185 return Err(syn::Error::new(
186 attr.span(),
187 format!("`#[sensitive]` attributes are not allowed on `{macro_name}` types"),
188 ));
189 }
190 if attr.path().is_ident("not_sensitive") {
191 return Err(syn::Error::new(
192 attr.span(),
193 format!(
194 "`#[not_sensitive]` attributes are not needed on `{macro_name}` types (the entire type is already non-sensitive)"
195 ),
196 ));
197 }
198 Ok(())
199 };
200
201 for attr in attrs {
202 check_attr(attr)?;
203 }
204
205 match data {
206 Data::Struct(data) => {
207 for field in &data.fields {
208 for attr in &field.attrs {
209 check_attr(attr)?;
210 }
211 }
212 }
213 Data::Enum(data) => {
214 for variant in &data.variants {
215 for field in &variant.fields {
216 for attr in &field.attrs {
217 check_attr(attr)?;
218 }
219 }
220 }
221 }
222 Data::Union(_) => {}
223 }
224
225 Ok(())
226}
227
228fn expand_not_sensitive(input: DeriveInput) -> Result<TokenStream> {
229 let DeriveInput {
230 ident,
231 generics,
232 data,
233 attrs,
234 ..
235 } = input;
236
237 if let Data::Union(u) = &data {
239 return Err(syn::Error::new(
240 u.union_token.span(),
241 "`NotSensitive` cannot be derived for unions",
242 ));
243 }
244
245 reject_sensitivity_attrs(&attrs, &data, "NotSensitive")?;
246
247 let crate_root = crate_root();
248
249 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
251 let container_impl = quote! {
252 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
253 fn redact_with<M: #crate_root::RedactableMapper>(self, _mapper: &M) -> Self {
254 self
255 }
256 }
257 };
258
259 #[cfg(feature = "slog")]
261 let slog_impl = {
262 let slog_crate = slog_crate()?;
263 let mut slog_generics = generics.clone();
264 let (_, ty_generics, _) = slog_generics.split_for_impl();
265 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
266 slog_generics
267 .make_where_clause()
268 .predicates
269 .push(parse_quote!(#self_ty: ::serde::Serialize));
270 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
271 slog_generics.split_for_impl();
272 quote! {
273 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
274 fn serialize(
275 &self,
276 _record: &#slog_crate::Record<'_>,
277 key: #slog_crate::Key,
278 serializer: &mut dyn #slog_crate::Serializer,
279 ) -> #slog_crate::Result {
280 #crate_root::slog::__slog_serialize_not_sensitive(self, _record, key, serializer)
281 }
282 }
283
284 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
285 }
286 };
287
288 #[cfg(not(feature = "slog"))]
289 let slog_impl = quote! {};
290
291 #[cfg(feature = "tracing")]
293 let tracing_impl = {
294 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
295 generics.split_for_impl();
296 quote! {
297 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
298 }
299 };
300
301 #[cfg(not(feature = "tracing"))]
302 let tracing_impl = quote! {};
303
304 Ok(quote! {
305 #container_impl
306 #slog_impl
307 #tracing_impl
308 })
309}
310
311#[proc_macro_derive(NotSensitiveDisplay)]
355pub fn derive_not_sensitive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
356 let input = parse_macro_input!(input as DeriveInput);
357 match expand_not_sensitive_display(input) {
358 Ok(tokens) => tokens.into(),
359 Err(err) => err.into_compile_error().into(),
360 }
361}
362
363fn expand_not_sensitive_display(input: DeriveInput) -> Result<TokenStream> {
364 let DeriveInput {
365 ident,
366 generics,
367 data,
368 attrs,
369 ..
370 } = input;
371
372 if let Data::Union(u) = &data {
374 return Err(syn::Error::new(
375 u.union_token.span(),
376 "`NotSensitiveDisplay` cannot be derived for unions",
377 ));
378 }
379
380 reject_sensitivity_attrs(&attrs, &data, "NotSensitiveDisplay")?;
381
382 let crate_root = crate_root();
383
384 let (container_impl_generics, container_ty_generics, container_where_clause) =
387 generics.split_for_impl();
388 let container_impl = quote! {
389 impl #container_impl_generics #crate_root::RedactableWithMapper for #ident #container_ty_generics #container_where_clause {
390 fn redact_with<M: #crate_root::RedactableMapper>(self, _mapper: &M) -> Self {
391 self
392 }
393 }
394 };
395
396 let mut display_generics = generics.clone();
399 let display_where_clause = display_generics.make_where_clause();
400 for param in generics.type_params() {
402 let ident = ¶m.ident;
403 display_where_clause
404 .predicates
405 .push(syn::parse_quote!(#ident: ::core::fmt::Display));
406 }
407
408 let (display_impl_generics, display_ty_generics, display_where_clause) =
409 display_generics.split_for_impl();
410
411 let redacted_display_impl = quote! {
413 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
414 fn fmt_redacted(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
415 ::core::fmt::Display::fmt(self, f)
416 }
417 }
418 };
419
420 #[cfg(feature = "slog")]
422 let slog_impl = {
423 let slog_crate = slog_crate()?;
424 let mut slog_generics = generics.clone();
425 let (_, ty_generics, _) = slog_generics.split_for_impl();
426 let self_ty: syn::Type = syn::parse_quote!(#ident #ty_generics);
427 slog_generics
428 .make_where_clause()
429 .predicates
430 .push(syn::parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
431 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
432 slog_generics.split_for_impl();
433 quote! {
434 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
435 fn serialize(
436 &self,
437 _record: &#slog_crate::Record<'_>,
438 key: #slog_crate::Key,
439 serializer: &mut dyn #slog_crate::Serializer,
440 ) -> #slog_crate::Result {
441 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
442 serializer.emit_arguments(key, &format_args!("{}", redacted))
443 }
444 }
445
446 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
447 }
448 };
449
450 #[cfg(not(feature = "slog"))]
451 let slog_impl = quote! {};
452
453 #[cfg(feature = "tracing")]
455 let tracing_impl = {
456 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
457 generics.split_for_impl();
458 quote! {
459 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
460 }
461 };
462
463 #[cfg(not(feature = "tracing"))]
464 let tracing_impl = quote! {};
465
466 Ok(quote! {
467 #container_impl
468 #redacted_display_impl
469 #slog_impl
470 #tracing_impl
471 })
472}
473
474#[proc_macro_derive(SensitiveDisplay, attributes(sensitive, not_sensitive, error))]
502pub fn derive_sensitive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
503 let input = parse_macro_input!(input as DeriveInput);
504 match expand(input, SlogMode::RedactedDisplay) {
505 Ok(tokens) => tokens.into(),
506 Err(err) => err.into_compile_error().into(),
507 }
508}
509
510fn crate_root() -> proc_macro2::TokenStream {
515 match crate_name("redactable") {
516 Ok(FoundCrate::Itself) => quote! { crate },
517 Ok(FoundCrate::Name(name)) => {
518 let ident = format_ident!("{}", name);
519 quote! { ::#ident }
520 }
521 Err(_) => quote! { ::redactable },
522 }
523}
524
525#[cfg(feature = "slog")]
531fn slog_crate() -> Result<proc_macro2::TokenStream> {
532 match crate_name("slog") {
533 Ok(FoundCrate::Itself) => Ok(quote! { crate }),
534 Ok(FoundCrate::Name(name)) => {
535 let ident = format_ident!("{}", name);
536 Ok(quote! { ::#ident })
537 }
538 Err(_) => {
539 let env_value = std::env::var("REDACTABLE_SLOG_CRATE").map_err(|_| {
540 syn::Error::new(
541 Span::call_site(),
542 "slog support is enabled, but no top-level `slog` crate was found. \
543Set the REDACTABLE_SLOG_CRATE env var to a path (e.g., `my_log::slog`) or add \
544`slog` as a direct dependency.",
545 )
546 })?;
547 let path = syn::parse_str::<syn::Path>(&env_value).map_err(|_| {
548 syn::Error::new(
549 Span::call_site(),
550 format!("REDACTABLE_SLOG_CRATE must be a valid Rust path (got `{env_value}`)"),
551 )
552 })?;
553 Ok(quote! { #path })
554 }
555 }
556}
557
558fn crate_path(item: &str) -> proc_macro2::TokenStream {
559 let root = crate_root();
560 let item_ident = syn::parse_str::<syn::Path>(item).expect("redactable crate path should parse");
561 quote! { #root::#item_ident }
562}
563
564pub(crate) struct DeriveOutput {
568 pub(crate) redaction_body: TokenStream,
569 pub(crate) used_generics: Vec<Ident>,
570 pub(crate) policy_applicable_generics: Vec<Ident>,
571 pub(crate) debug_redacted_body: TokenStream,
572 pub(crate) debug_redacted_generics: Vec<Ident>,
573 pub(crate) debug_unredacted_body: TokenStream,
574 pub(crate) debug_unredacted_generics: Vec<Ident>,
575}
576
577struct DebugOutput {
578 body: TokenStream,
579 generics: Vec<Ident>,
580}
581
582enum SlogMode {
583 RedactedJson,
584 RedactedDisplay,
585}
586
587#[allow(clippy::too_many_lines)]
588fn expand(input: DeriveInput, slog_mode: SlogMode) -> Result<TokenStream> {
589 let DeriveInput {
590 ident,
591 generics,
592 data,
593 attrs,
594 ..
595 } = input;
596
597 let ContainerOptions { skip_debug } = parse_container_options(&attrs)?;
598
599 let crate_root = crate_root();
600
601 if matches!(slog_mode, SlogMode::RedactedDisplay) {
602 let redacted_display_output = derive_redacted_display(&ident, &data, &attrs, &generics)?;
603 let redacted_display_generics =
604 add_display_bounds(generics.clone(), &redacted_display_output.display_generics);
605 let redacted_display_generics = add_debug_bounds(
606 redacted_display_generics,
607 &redacted_display_output.debug_generics,
608 );
609 let redacted_display_generics = add_policy_applicable_ref_bounds(
610 redacted_display_generics,
611 &redacted_display_output.policy_ref_generics,
612 );
613 let redacted_display_generics = add_redacted_display_bounds(
614 redacted_display_generics,
615 &redacted_display_output.nested_generics,
616 );
617 let (display_impl_generics, display_ty_generics, display_where_clause) =
618 redacted_display_generics.split_for_impl();
619 let redacted_display_body = redacted_display_output.body;
620 let redacted_display_impl = quote! {
621 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
622 fn fmt_redacted(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
623 #redacted_display_body
624 }
625 }
626 };
627
628 let debug_impl = if skip_debug {
629 quote! {}
630 } else {
631 let debug_output = derive_unredacted_debug(&ident, &data, &generics)?;
632 let debug_unredacted_generics =
633 add_debug_bounds(generics.clone(), &debug_output.generics);
634 let (
635 debug_unredacted_impl_generics,
636 debug_unredacted_ty_generics,
637 debug_unredacted_where_clause,
638 ) = debug_unredacted_generics.split_for_impl();
639 let (
640 debug_redacted_impl_generics,
641 debug_redacted_ty_generics,
642 debug_redacted_where_clause,
643 ) = redacted_display_generics.split_for_impl();
644 let debug_unredacted_body = debug_output.body;
645 quote! {
646 #[cfg(any(test, feature = "testing"))]
647 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
648 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
649 #debug_unredacted_body
650 }
651 }
652
653 #[cfg(not(any(test, feature = "testing")))]
654 impl #debug_redacted_impl_generics ::core::fmt::Debug for #ident #debug_redacted_ty_generics #debug_redacted_where_clause {
655 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
656 #crate_root::RedactableWithFormatter::fmt_redacted(self, f)
657 }
658 }
659 }
660 };
661
662 #[cfg(feature = "slog")]
665 let slog_impl = {
666 let slog_crate = slog_crate()?;
667 let mut slog_generics = generics;
668 let (_, ty_generics, _) = slog_generics.split_for_impl();
670 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
671 slog_generics
672 .make_where_clause()
673 .predicates
674 .push(parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
675 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
676 slog_generics.split_for_impl();
677 quote! {
678 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
679 fn serialize(
680 &self,
681 _record: &#slog_crate::Record<'_>,
682 key: #slog_crate::Key,
683 serializer: &mut dyn #slog_crate::Serializer,
684 ) -> #slog_crate::Result {
685 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
686 serializer.emit_arguments(key, &format_args!("{}", redacted))
687 }
688 }
689
690 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
691 }
692 };
693
694 #[cfg(not(feature = "slog"))]
695 let slog_impl = quote! {};
696
697 #[cfg(feature = "tracing")]
698 let tracing_impl = {
699 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
700 redacted_display_generics.split_for_impl();
701 quote! {
702 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
703 }
704 };
705
706 #[cfg(not(feature = "tracing"))]
707 let tracing_impl = quote! {};
708
709 return Ok(quote! {
710 #redacted_display_impl
711 #debug_impl
712 #slog_impl
713 #tracing_impl
714 });
715 }
716
717 let derive_output = match &data {
721 Data::Struct(data) => derive_struct(&ident, data.clone(), &generics)?,
722 Data::Enum(data) => derive_enum(&ident, data.clone(), &generics)?,
723 Data::Union(u) => {
724 return Err(syn::Error::new(
725 u.union_token.span(),
726 "`Sensitive` cannot be derived for unions",
727 ));
728 }
729 };
730
731 let policy_generics = add_container_bounds(generics.clone(), &derive_output.used_generics);
732 let policy_generics =
733 add_policy_applicable_bounds(policy_generics, &derive_output.policy_applicable_generics);
734 let (impl_generics, ty_generics, where_clause) = policy_generics.split_for_impl();
735 let debug_redacted_generics =
736 add_debug_bounds(generics.clone(), &derive_output.debug_redacted_generics);
737 let (debug_redacted_impl_generics, debug_redacted_ty_generics, debug_redacted_where_clause) =
738 debug_redacted_generics.split_for_impl();
739 let debug_unredacted_generics =
740 add_debug_bounds(generics.clone(), &derive_output.debug_unredacted_generics);
741 let (
742 debug_unredacted_impl_generics,
743 debug_unredacted_ty_generics,
744 debug_unredacted_where_clause,
745 ) = debug_unredacted_generics.split_for_impl();
746 let redaction_body = &derive_output.redaction_body;
747 let debug_redacted_body = &derive_output.debug_redacted_body;
748 let debug_unredacted_body = &derive_output.debug_unredacted_body;
749 let debug_impl = if skip_debug {
750 quote! {}
751 } else {
752 quote! {
753 #[cfg(any(test, feature = "testing"))]
754 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
755 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
756 #debug_unredacted_body
757 }
758 }
759
760 #[cfg(not(any(test, feature = "testing")))]
761 impl #debug_redacted_impl_generics ::core::fmt::Debug for #ident #debug_redacted_ty_generics #debug_redacted_where_clause {
762 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
763 #debug_redacted_body
764 }
765 }
766 }
767 };
768
769 #[cfg(feature = "slog")]
772 let slog_impl = {
773 let slog_crate = slog_crate()?;
774 let mut slog_generics = generics;
775 let slog_where_clause = slog_generics.make_where_clause();
776 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
777 slog_where_clause
778 .predicates
779 .push(parse_quote!(#self_ty: ::core::clone::Clone));
780 slog_where_clause
783 .predicates
784 .push(parse_quote!(#self_ty: ::serde::Serialize));
785 slog_where_clause
786 .predicates
787 .push(parse_quote!(#self_ty: #crate_root::slog::SlogRedactedExt));
788 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
789 slog_generics.split_for_impl();
790 quote! {
791 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
792 fn serialize(
793 &self,
794 _record: &#slog_crate::Record<'_>,
795 key: #slog_crate::Key,
796 serializer: &mut dyn #slog_crate::Serializer,
797 ) -> #slog_crate::Result {
798 let redacted = #crate_root::slog::SlogRedactedExt::slog_redacted_json(self.clone());
799 #slog_crate::Value::serialize(&redacted, _record, key, serializer)
800 }
801 }
802
803 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
804 }
805 };
806
807 #[cfg(not(feature = "slog"))]
808 let slog_impl = quote! {};
809
810 #[cfg(feature = "tracing")]
811 let tracing_impl = quote! {
812 impl #impl_generics #crate_root::tracing::TracingRedacted for #ident #ty_generics #where_clause {}
813 };
814
815 #[cfg(not(feature = "tracing"))]
816 let tracing_impl = quote! {};
817
818 let trait_impl = quote! {
819 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
820 fn redact_with<M: #crate_root::RedactableMapper>(self, mapper: &M) -> Self {
821 use #crate_root::RedactableWithMapper as _;
822 #redaction_body
823 }
824 }
825
826 #debug_impl
827
828 #slog_impl
829
830 #tracing_impl
831
832 };
835 Ok(trait_impl)
836}
837
838fn derive_unredacted_debug(
839 name: &Ident,
840 data: &Data,
841 generics: &syn::Generics,
842) -> Result<DebugOutput> {
843 match data {
844 Data::Struct(data) => Ok(derive_unredacted_debug_struct(name, data, generics)),
845 Data::Enum(data) => Ok(derive_unredacted_debug_enum(name, data, generics)),
846 Data::Union(u) => Err(syn::Error::new(
847 u.union_token.span(),
848 "`SensitiveDisplay` cannot be derived for unions",
849 )),
850 }
851}
852
853fn derive_unredacted_debug_struct(
854 name: &Ident,
855 data: &DataStruct,
856 generics: &syn::Generics,
857) -> DebugOutput {
858 let mut debug_generics = Vec::new();
859 match &data.fields {
860 Fields::Named(fields) => {
861 let mut bindings = Vec::new();
862 let mut debug_fields = Vec::new();
863 for field in &fields.named {
864 let ident = field
865 .ident
866 .clone()
867 .expect("named field should have identifier");
868 bindings.push(ident.clone());
869 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
870 debug_fields.push(quote! {
871 debug.field(stringify!(#ident), #ident);
872 });
873 }
874 DebugOutput {
875 body: quote! {
876 match self {
877 Self { #(#bindings),* } => {
878 let mut debug = f.debug_struct(stringify!(#name));
879 #(#debug_fields)*
880 debug.finish()
881 }
882 }
883 },
884 generics: debug_generics,
885 }
886 }
887 Fields::Unnamed(fields) => {
888 let mut bindings = Vec::new();
889 let mut debug_fields = Vec::new();
890 for (index, field) in fields.unnamed.iter().enumerate() {
891 let ident = format_ident!("field_{index}");
892 bindings.push(ident.clone());
893 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
894 debug_fields.push(quote! {
895 debug.field(#ident);
896 });
897 }
898 DebugOutput {
899 body: quote! {
900 match self {
901 Self ( #(#bindings),* ) => {
902 let mut debug = f.debug_tuple(stringify!(#name));
903 #(#debug_fields)*
904 debug.finish()
905 }
906 }
907 },
908 generics: debug_generics,
909 }
910 }
911 Fields::Unit => DebugOutput {
912 body: quote! {
913 f.write_str(stringify!(#name))
914 },
915 generics: debug_generics,
916 },
917 }
918}
919
920fn derive_unredacted_debug_enum(
921 name: &Ident,
922 data: &DataEnum,
923 generics: &syn::Generics,
924) -> DebugOutput {
925 let mut debug_generics = Vec::new();
926 let mut debug_arms = Vec::new();
927 for variant in &data.variants {
928 let variant_ident = &variant.ident;
929 match &variant.fields {
930 Fields::Unit => {
931 debug_arms.push(quote! {
932 #name::#variant_ident => f.write_str(stringify!(#name::#variant_ident))
933 });
934 }
935 Fields::Named(fields) => {
936 let mut bindings = Vec::new();
937 let mut debug_fields = Vec::new();
938 for field in &fields.named {
939 let ident = field
940 .ident
941 .clone()
942 .expect("named field should have identifier");
943 bindings.push(ident.clone());
944 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
945 debug_fields.push(quote! {
946 debug.field(stringify!(#ident), #ident);
947 });
948 }
949 debug_arms.push(quote! {
950 #name::#variant_ident { #(#bindings),* } => {
951 let mut debug = f.debug_struct(stringify!(#name::#variant_ident));
952 #(#debug_fields)*
953 debug.finish()
954 }
955 });
956 }
957 Fields::Unnamed(fields) => {
958 let mut bindings = Vec::new();
959 let mut debug_fields = Vec::new();
960 for (index, field) in fields.unnamed.iter().enumerate() {
961 let ident = format_ident!("field_{index}");
962 bindings.push(ident.clone());
963 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
964 debug_fields.push(quote! {
965 debug.field(#ident);
966 });
967 }
968 debug_arms.push(quote! {
969 #name::#variant_ident ( #(#bindings),* ) => {
970 let mut debug = f.debug_tuple(stringify!(#name::#variant_ident));
971 #(#debug_fields)*
972 debug.finish()
973 }
974 });
975 }
976 }
977 }
978 DebugOutput {
979 body: quote! {
980 match self {
981 #(#debug_arms),*
982 }
983 },
984 generics: debug_generics,
985 }
986}