Skip to main content

derive_hex/
lib.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at http://mozilla.org/MPL/2.0/.
4//
5// Copyright (c) DUSK NETWORK. All rights reserved.
6
7#![forbid(unsafe_code)]
8
9use proc_macro::TokenStream;
10use quote::quote;
11
12#[proc_macro_derive(Hex)]
13/// Derives lower- and upper-hexadecimal formatting for a serializable type.
14///
15/// # Security
16///
17/// Do not derive `Hex` for types containing secrets or other confidential
18/// material. Hexadecimal formatting exposes the complete serialized
19/// representation.
20pub fn derive_hex(item: TokenStream) -> TokenStream {
21    let input = syn::parse_macro_input!(item as syn::DeriveInput);
22    let ident = &input.ident;
23    let (impl_generics, ty_generics, where_clause) =
24        input.generics.split_for_impl();
25
26    (quote! {
27        impl #impl_generics ::core::fmt::LowerHex for #ident #ty_generics #where_clause {
28            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
29                if f.alternate() {
30                    ::core::write!(f, "0x")?
31                }
32
33                for byte in self.to_bytes() {
34                    ::core::write!(f, "{byte:02x}")?
35                }
36
37                ::core::result::Result::Ok(())
38            }
39        }
40
41        impl #impl_generics ::core::fmt::UpperHex for #ident #ty_generics #where_clause {
42            fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
43                if f.alternate() {
44                    ::core::write!(f, "0x")?
45                }
46
47                for byte in self.to_bytes() {
48                    ::core::write!(f, "{byte:02X}")?
49                }
50
51                ::core::result::Result::Ok(())
52            }
53        }
54    })
55    .into()
56}
57
58#[proc_macro_derive(HexDebug)]
59/// Derives hexadecimal formatting and renders the serialized value for
60/// [`Debug`](::core::fmt::Debug).
61///
62/// # Security
63///
64/// Do not derive `HexDebug` for types containing secrets or other confidential
65/// material. It exposes the complete serialized representation through both
66/// hexadecimal and ordinary debug formatting.
67pub fn derive_hex_debug(item: TokenStream) -> TokenStream {
68    let mut hex: TokenStream = derive_hex(item.clone());
69    let input = syn::parse_macro_input!(item as syn::DeriveInput);
70    let ident = &input.ident;
71    let (impl_generics, ty_generics, where_clause) =
72        input.generics.split_for_impl();
73
74    let dbg: TokenStream = (quote! {
75    impl #impl_generics ::core::fmt::Debug for #ident #ty_generics #where_clause {
76        fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
77            // `Formatter` does not publicly expose the debug-hex case. Bit 5 is
78            // `FlagV1::DebugUpperHex` in `core`:
79            // <https://github.com/rust-lang/rust/blob/90442458ac46b1d5eed752c316da25450f67285b/library/core/src/fmt/mod.rs#L1817-L1825>
80            const DEBUG_UPPER_HEX: u32 = 1 << 5;
81
82            #[allow(deprecated)]
83            if f.flags() & DEBUG_UPPER_HEX != 0 {
84                ::core::fmt::UpperHex::fmt(self, f)
85            } else { // LowerHex is always the default for debug
86                ::core::fmt::LowerHex::fmt(self, f)
87            }
88        }
89    }})
90    .into();
91
92    hex.extend(dbg);
93    hex
94}