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))]
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))]
171pub fn derive_not_sensitive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
172 let input = parse_macro_input!(input as DeriveInput);
173 match expand_not_sensitive(input) {
174 Ok(tokens) => tokens.into(),
175 Err(err) => err.into_compile_error().into(),
176 }
177}
178
179fn reject_sensitivity_attrs(attrs: &[syn::Attribute], data: &Data, macro_name: &str) -> Result<()> {
185 let check_attr = |attr: &syn::Attribute| -> Result<()> {
186 if attr.path().is_ident("sensitive") {
187 return Err(syn::Error::new(
188 attr.span(),
189 format!("`#[sensitive]` attributes are not allowed on `{macro_name}` types"),
190 ));
191 }
192 if attr.path().is_ident("not_sensitive") {
193 return Err(syn::Error::new(
194 attr.span(),
195 format!(
196 "`#[not_sensitive]` attributes are not needed on `{macro_name}` types (the entire type is already non-sensitive)"
197 ),
198 ));
199 }
200 Ok(())
201 };
202
203 for attr in attrs {
204 check_attr(attr)?;
205 }
206
207 match data {
208 Data::Struct(data) => {
209 for field in &data.fields {
210 for attr in &field.attrs {
211 check_attr(attr)?;
212 }
213 }
214 }
215 Data::Enum(data) => {
216 for variant in &data.variants {
217 for field in &variant.fields {
218 for attr in &field.attrs {
219 check_attr(attr)?;
220 }
221 }
222 }
223 }
224 Data::Union(_) => {}
225 }
226
227 Ok(())
228}
229
230fn expand_not_sensitive(input: DeriveInput) -> Result<TokenStream> {
231 let DeriveInput {
232 ident,
233 generics,
234 data,
235 attrs,
236 ..
237 } = input;
238
239 if let Data::Union(u) = &data {
241 return Err(syn::Error::new(
242 u.union_token.span(),
243 "`NotSensitive` cannot be derived for unions",
244 ));
245 }
246
247 reject_sensitivity_attrs(&attrs, &data, "NotSensitive")?;
248
249 let crate_root = crate_root();
250
251 let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
253 let container_impl = quote! {
254 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
255 fn redact_with<M: #crate_root::RedactableMapper>(self, _mapper: &M) -> Self {
256 self
257 }
258 }
259 };
260
261 #[cfg(feature = "slog")]
263 let slog_impl = {
264 let slog_crate = slog_crate()?;
265 let mut slog_generics = generics.clone();
266 let (_, ty_generics, _) = slog_generics.split_for_impl();
267 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
268 slog_generics
269 .make_where_clause()
270 .predicates
271 .push(parse_quote!(#self_ty: ::serde::Serialize));
272 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
273 slog_generics.split_for_impl();
274 quote! {
275 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
276 fn serialize(
277 &self,
278 _record: &#slog_crate::Record<'_>,
279 key: #slog_crate::Key,
280 serializer: &mut dyn #slog_crate::Serializer,
281 ) -> #slog_crate::Result {
282 #crate_root::slog::__slog_serialize_not_sensitive(self, _record, key, serializer)
283 }
284 }
285
286 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
287 }
288 };
289
290 #[cfg(not(feature = "slog"))]
291 let slog_impl = quote! {};
292
293 #[cfg(feature = "tracing")]
295 let tracing_impl = {
296 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
297 generics.split_for_impl();
298 quote! {
299 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
300 }
301 };
302
303 #[cfg(not(feature = "tracing"))]
304 let tracing_impl = quote! {};
305
306 Ok(quote! {
307 #container_impl
308 #slog_impl
309 #tracing_impl
310 })
311}
312
313#[proc_macro_derive(NotSensitiveDisplay)]
357pub fn derive_not_sensitive_display(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
358 let input = parse_macro_input!(input as DeriveInput);
359 match expand_not_sensitive_display(input) {
360 Ok(tokens) => tokens.into(),
361 Err(err) => err.into_compile_error().into(),
362 }
363}
364
365fn expand_not_sensitive_display(input: DeriveInput) -> Result<TokenStream> {
366 let DeriveInput {
367 ident,
368 generics,
369 data,
370 attrs,
371 ..
372 } = input;
373
374 if let Data::Union(u) = &data {
376 return Err(syn::Error::new(
377 u.union_token.span(),
378 "`NotSensitiveDisplay` cannot be derived for unions",
379 ));
380 }
381
382 reject_sensitivity_attrs(&attrs, &data, "NotSensitiveDisplay")?;
383
384 let crate_root = crate_root();
385
386 let (container_impl_generics, container_ty_generics, container_where_clause) =
389 generics.split_for_impl();
390 let container_impl = quote! {
391 impl #container_impl_generics #crate_root::RedactableWithMapper for #ident #container_ty_generics #container_where_clause {
392 fn redact_with<M: #crate_root::RedactableMapper>(self, _mapper: &M) -> Self {
393 self
394 }
395 }
396 };
397
398 let mut display_generics = generics.clone();
401 let display_where_clause = display_generics.make_where_clause();
402 for param in generics.type_params() {
404 let ident = ¶m.ident;
405 display_where_clause
406 .predicates
407 .push(syn::parse_quote!(#ident: ::core::fmt::Display));
408 }
409
410 let (display_impl_generics, display_ty_generics, display_where_clause) =
411 display_generics.split_for_impl();
412
413 let redacted_display_impl = quote! {
415 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
416 fn fmt_redacted(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
417 ::core::fmt::Display::fmt(self, f)
418 }
419 }
420 };
421
422 #[cfg(feature = "slog")]
424 let slog_impl = {
425 let slog_crate = slog_crate()?;
426 let mut slog_generics = generics.clone();
427 let (_, ty_generics, _) = slog_generics.split_for_impl();
428 let self_ty: syn::Type = syn::parse_quote!(#ident #ty_generics);
429 slog_generics
430 .make_where_clause()
431 .predicates
432 .push(syn::parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
433 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
434 slog_generics.split_for_impl();
435 quote! {
436 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
437 fn serialize(
438 &self,
439 _record: &#slog_crate::Record<'_>,
440 key: #slog_crate::Key,
441 serializer: &mut dyn #slog_crate::Serializer,
442 ) -> #slog_crate::Result {
443 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
444 serializer.emit_arguments(key, &format_args!("{}", redacted))
445 }
446 }
447
448 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
449 }
450 };
451
452 #[cfg(not(feature = "slog"))]
453 let slog_impl = quote! {};
454
455 #[cfg(feature = "tracing")]
457 let tracing_impl = {
458 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
459 generics.split_for_impl();
460 quote! {
461 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
462 }
463 };
464
465 #[cfg(not(feature = "tracing"))]
466 let tracing_impl = quote! {};
467
468 Ok(quote! {
469 #container_impl
470 #redacted_display_impl
471 #slog_impl
472 #tracing_impl
473 })
474}
475
476#[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, DeriveKind::SensitiveDisplay) {
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 DeriveKind {
587 Sensitive,
589 SensitiveDisplay,
591}
592
593#[allow(clippy::too_many_lines)]
594fn expand(input: DeriveInput, kind: DeriveKind) -> Result<TokenStream> {
595 let DeriveInput {
596 ident,
597 generics,
598 data,
599 attrs,
600 ..
601 } = input;
602
603 let ContainerOptions { dual } = parse_container_options(&attrs)?;
604
605 let crate_root = crate_root();
606
607 if matches!(kind, DeriveKind::SensitiveDisplay) {
608 let redacted_display_output = derive_redacted_display(&ident, &data, &attrs, &generics)?;
609 let redacted_display_generics =
610 add_display_bounds(generics.clone(), &redacted_display_output.display_generics);
611 let redacted_display_generics = add_debug_bounds(
612 redacted_display_generics,
613 &redacted_display_output.debug_generics,
614 );
615 let redacted_display_generics = add_policy_applicable_ref_bounds(
616 redacted_display_generics,
617 &redacted_display_output.policy_ref_generics,
618 );
619 let redacted_display_generics = add_redacted_display_bounds(
620 redacted_display_generics,
621 &redacted_display_output.nested_generics,
622 );
623 let (display_impl_generics, display_ty_generics, display_where_clause) =
624 redacted_display_generics.split_for_impl();
625 let redacted_display_body = redacted_display_output.body;
626 let redacted_display_impl = quote! {
627 impl #display_impl_generics #crate_root::RedactableWithFormatter for #ident #display_ty_generics #display_where_clause {
628 fn fmt_redacted(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
629 #redacted_display_body
630 }
631 }
632 };
633
634 let debug_output = derive_unredacted_debug(&ident, &data, &generics)?;
635 let debug_unredacted_generics = add_debug_bounds(generics.clone(), &debug_output.generics);
636 let (
637 debug_unredacted_impl_generics,
638 debug_unredacted_ty_generics,
639 debug_unredacted_where_clause,
640 ) = debug_unredacted_generics.split_for_impl();
641 let (debug_redacted_impl_generics, debug_redacted_ty_generics, debug_redacted_where_clause) =
642 redacted_display_generics.split_for_impl();
643 let debug_unredacted_body = debug_output.body;
644 let debug_impl = quote! {
645 #[cfg(any(test, feature = "testing"))]
646 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
647 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
648 #debug_unredacted_body
649 }
650 }
651
652 #[cfg(not(any(test, feature = "testing")))]
653 impl #debug_redacted_impl_generics ::core::fmt::Debug for #ident #debug_redacted_ty_generics #debug_redacted_where_clause {
654 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
655 #crate_root::RedactableWithFormatter::fmt_redacted(self, f)
656 }
657 }
658 };
659
660 let slog_impl = if dual {
662 quote! {}
663 } else {
664 #[cfg(feature = "slog")]
665 {
666 let slog_crate = slog_crate()?;
667 let mut slog_generics = generics;
668 let (_, ty_generics, _) = slog_generics.split_for_impl();
669 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
670 slog_generics
671 .make_where_clause()
672 .predicates
673 .push(parse_quote!(#self_ty: #crate_root::RedactableWithFormatter));
674 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
675 slog_generics.split_for_impl();
676 quote! {
677 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
678 fn serialize(
679 &self,
680 _record: &#slog_crate::Record<'_>,
681 key: #slog_crate::Key,
682 serializer: &mut dyn #slog_crate::Serializer,
683 ) -> #slog_crate::Result {
684 let redacted = #crate_root::RedactableWithFormatter::redacted_display(self);
685 serializer.emit_arguments(key, &format_args!("{}", redacted))
686 }
687 }
688
689 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
690 }
691 }
692
693 #[cfg(not(feature = "slog"))]
694 {
695 quote! {}
696 }
697 };
698
699 let tracing_impl = if dual {
700 quote! {}
701 } else {
702 #[cfg(feature = "tracing")]
703 {
704 let (tracing_impl_generics, tracing_ty_generics, tracing_where_clause) =
705 redacted_display_generics.split_for_impl();
706 quote! {
707 impl #tracing_impl_generics #crate_root::tracing::TracingRedacted for #ident #tracing_ty_generics #tracing_where_clause {}
708 }
709 }
710
711 #[cfg(not(feature = "tracing"))]
712 {
713 quote! {}
714 }
715 };
716
717 return Ok(quote! {
718 #redacted_display_impl
719 #debug_impl
720 #slog_impl
721 #tracing_impl
722 });
723 }
724
725 let derive_output = match data {
728 Data::Struct(data) => derive_struct(&ident, data, &generics)?,
729 Data::Enum(data) => derive_enum(&ident, data, &generics)?,
730 Data::Union(u) => {
731 return Err(syn::Error::new(
732 u.union_token.span(),
733 "`Sensitive` cannot be derived for unions",
734 ));
735 }
736 };
737
738 let policy_generics = add_container_bounds(generics.clone(), &derive_output.used_generics);
739 let policy_generics =
740 add_policy_applicable_bounds(policy_generics, &derive_output.policy_applicable_generics);
741 let (impl_generics, ty_generics, where_clause) = policy_generics.split_for_impl();
742 let debug_redacted_generics =
743 add_debug_bounds(generics.clone(), &derive_output.debug_redacted_generics);
744 let (debug_redacted_impl_generics, debug_redacted_ty_generics, debug_redacted_where_clause) =
745 debug_redacted_generics.split_for_impl();
746 let debug_unredacted_generics =
747 add_debug_bounds(generics.clone(), &derive_output.debug_unredacted_generics);
748 let (
749 debug_unredacted_impl_generics,
750 debug_unredacted_ty_generics,
751 debug_unredacted_where_clause,
752 ) = debug_unredacted_generics.split_for_impl();
753 let redaction_body = &derive_output.redaction_body;
754 let debug_redacted_body = &derive_output.debug_redacted_body;
755 let debug_unredacted_body = &derive_output.debug_unredacted_body;
756 let debug_impl = if dual {
758 quote! {}
759 } else {
760 quote! {
761 #[cfg(any(test, feature = "testing"))]
762 impl #debug_unredacted_impl_generics ::core::fmt::Debug for #ident #debug_unredacted_ty_generics #debug_unredacted_where_clause {
763 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
764 #debug_unredacted_body
765 }
766 }
767
768 #[cfg(not(any(test, feature = "testing")))]
769 impl #debug_redacted_impl_generics ::core::fmt::Debug for #ident #debug_redacted_ty_generics #debug_redacted_where_clause {
770 fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
771 #debug_redacted_body
772 }
773 }
774 }
775 };
776
777 #[cfg(feature = "slog")]
778 let slog_impl = {
779 let slog_crate = slog_crate()?;
780 let mut slog_generics = generics;
781 let slog_where_clause = slog_generics.make_where_clause();
782 let self_ty: syn::Type = parse_quote!(#ident #ty_generics);
783 slog_where_clause
784 .predicates
785 .push(parse_quote!(#self_ty: ::core::clone::Clone));
786 slog_where_clause
789 .predicates
790 .push(parse_quote!(#self_ty: ::serde::Serialize));
791 slog_where_clause
792 .predicates
793 .push(parse_quote!(#self_ty: #crate_root::slog::SlogRedactedExt));
794 let (slog_impl_generics, slog_ty_generics, slog_where_clause) =
795 slog_generics.split_for_impl();
796 quote! {
797 impl #slog_impl_generics #slog_crate::Value for #ident #slog_ty_generics #slog_where_clause {
798 fn serialize(
799 &self,
800 _record: &#slog_crate::Record<'_>,
801 key: #slog_crate::Key,
802 serializer: &mut dyn #slog_crate::Serializer,
803 ) -> #slog_crate::Result {
804 let redacted = #crate_root::slog::SlogRedactedExt::slog_redacted_json(self.clone());
805 #slog_crate::Value::serialize(&redacted, _record, key, serializer)
806 }
807 }
808
809 impl #slog_impl_generics #crate_root::slog::SlogRedacted for #ident #slog_ty_generics #slog_where_clause {}
810 }
811 };
812
813 #[cfg(not(feature = "slog"))]
814 let slog_impl = quote! {};
815
816 #[cfg(feature = "tracing")]
817 let tracing_impl = quote! {
818 impl #impl_generics #crate_root::tracing::TracingRedacted for #ident #ty_generics #where_clause {}
819 };
820
821 #[cfg(not(feature = "tracing"))]
822 let tracing_impl = quote! {};
823
824 let trait_impl = quote! {
825 impl #impl_generics #crate_root::RedactableWithMapper for #ident #ty_generics #where_clause {
826 fn redact_with<M: #crate_root::RedactableMapper>(self, mapper: &M) -> Self {
827 use #crate_root::RedactableWithMapper as _;
828 #redaction_body
829 }
830 }
831
832 #debug_impl
833
834 #slog_impl
835
836 #tracing_impl
837 };
838 Ok(trait_impl)
839}
840
841fn derive_unredacted_debug(
842 name: &Ident,
843 data: &Data,
844 generics: &syn::Generics,
845) -> Result<DebugOutput> {
846 match data {
847 Data::Struct(data) => Ok(derive_unredacted_debug_struct(name, data, generics)),
848 Data::Enum(data) => Ok(derive_unredacted_debug_enum(name, data, generics)),
849 Data::Union(u) => Err(syn::Error::new(
850 u.union_token.span(),
851 "`SensitiveDisplay` cannot be derived for unions",
852 )),
853 }
854}
855
856fn derive_unredacted_debug_struct(
857 name: &Ident,
858 data: &DataStruct,
859 generics: &syn::Generics,
860) -> DebugOutput {
861 let mut debug_generics = Vec::new();
862 match &data.fields {
863 Fields::Named(fields) => {
864 let mut bindings = Vec::new();
865 let mut debug_fields = Vec::new();
866 for field in &fields.named {
867 let ident = field
868 .ident
869 .clone()
870 .expect("named field should have identifier");
871 bindings.push(ident.clone());
872 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
873 debug_fields.push(quote! {
874 debug.field(stringify!(#ident), #ident);
875 });
876 }
877 DebugOutput {
878 body: quote! {
879 match self {
880 Self { #(#bindings),* } => {
881 let mut debug = f.debug_struct(stringify!(#name));
882 #(#debug_fields)*
883 debug.finish()
884 }
885 }
886 },
887 generics: debug_generics,
888 }
889 }
890 Fields::Unnamed(fields) => {
891 let mut bindings = Vec::new();
892 let mut debug_fields = Vec::new();
893 for (index, field) in fields.unnamed.iter().enumerate() {
894 let ident = format_ident!("field_{index}");
895 bindings.push(ident.clone());
896 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
897 debug_fields.push(quote! {
898 debug.field(#ident);
899 });
900 }
901 DebugOutput {
902 body: quote! {
903 match self {
904 Self ( #(#bindings),* ) => {
905 let mut debug = f.debug_tuple(stringify!(#name));
906 #(#debug_fields)*
907 debug.finish()
908 }
909 }
910 },
911 generics: debug_generics,
912 }
913 }
914 Fields::Unit => DebugOutput {
915 body: quote! {
916 f.write_str(stringify!(#name))
917 },
918 generics: debug_generics,
919 },
920 }
921}
922
923fn derive_unredacted_debug_enum(
924 name: &Ident,
925 data: &DataEnum,
926 generics: &syn::Generics,
927) -> DebugOutput {
928 let mut debug_generics = Vec::new();
929 let mut debug_arms = Vec::new();
930 for variant in &data.variants {
931 let variant_ident = &variant.ident;
932 match &variant.fields {
933 Fields::Unit => {
934 debug_arms.push(quote! {
935 #name::#variant_ident => f.write_str(stringify!(#name::#variant_ident))
936 });
937 }
938 Fields::Named(fields) => {
939 let mut bindings = Vec::new();
940 let mut debug_fields = Vec::new();
941 for field in &fields.named {
942 let ident = field
943 .ident
944 .clone()
945 .expect("named field should have identifier");
946 bindings.push(ident.clone());
947 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
948 debug_fields.push(quote! {
949 debug.field(stringify!(#ident), #ident);
950 });
951 }
952 debug_arms.push(quote! {
953 #name::#variant_ident { #(#bindings),* } => {
954 let mut debug = f.debug_struct(stringify!(#name::#variant_ident));
955 #(#debug_fields)*
956 debug.finish()
957 }
958 });
959 }
960 Fields::Unnamed(fields) => {
961 let mut bindings = Vec::new();
962 let mut debug_fields = Vec::new();
963 for (index, field) in fields.unnamed.iter().enumerate() {
964 let ident = format_ident!("field_{index}");
965 bindings.push(ident.clone());
966 collect_generics_from_type(&field.ty, generics, &mut debug_generics);
967 debug_fields.push(quote! {
968 debug.field(#ident);
969 });
970 }
971 debug_arms.push(quote! {
972 #name::#variant_ident ( #(#bindings),* ) => {
973 let mut debug = f.debug_tuple(stringify!(#name::#variant_ident));
974 #(#debug_fields)*
975 debug.finish()
976 }
977 });
978 }
979 }
980 }
981 DebugOutput {
982 body: quote! {
983 match self {
984 #(#debug_arms),*
985 }
986 },
987 generics: debug_generics,
988 }
989}