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
7use proc_macro::TokenStream;
8use quote::quote;
9
10#[proc_macro_derive(Hex)]
11pub fn derive_hex(item: TokenStream) -> TokenStream {
12    let input = syn::parse_macro_input!(item as syn::DeriveInput);
13    let ident = &input.ident;
14
15    (quote! {
16        impl core::fmt::LowerHex for #ident {
17            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
18                let bytes = self.to_bytes();
19
20                if f.alternate() {
21                    write!(f, "0x")?
22                }
23
24                for byte in &bytes[..] {
25                    write!(f, "{:02x}", &byte)?
26                }
27
28                Ok(())
29            }
30        }
31
32        impl core::fmt::UpperHex for #ident {
33            fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
34                let bytes = self.to_bytes();
35
36                if f.alternate() {
37                    write!(f, "0x")?
38                }
39
40                for byte in &bytes[..] {
41                    write!(f, "{:02X}", &byte)?
42                }
43
44                Ok(())
45            }
46        }
47    })
48    .into()
49}
50
51#[proc_macro_derive(HexDebug)]
52pub fn derive_hex_debug(item: TokenStream) -> TokenStream {
53    let mut hex: TokenStream = derive_hex(item.clone());
54    let input = syn::parse_macro_input!(item as syn::DeriveInput);
55    let ident = &input.ident;
56
57    let dbg: TokenStream = (quote! {
58    impl core::fmt::Debug for #ident {
59        fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
60            // Once we format an object using the debug notation (e.g. `{:x?}`)
61            // there is absolutely NO WAY to detect the flag for the lowerhex
62            // or upperhex, and therefore forwarding to the relevant formatter.
63            // Two methods for this purpose exists, but they're not exposed
64            // because they didn't agree on a name yet, see:
65            // <https://github.com/rust-lang/rust/blob/90442458ac46b1d5eed752c316da25450f67285b/library/core/src/fmt/mod.rs#L1817-L1825>
66            //
67            // Therefore the only way is using the deprecated method `flags`,
68            // implementing the same logic of the forementioned methods.
69
70            // We also do not have access to the `FlagV1` enum since it's
71            // private.
72            let FlagV1_DebugUpperHex = 5_u32;
73
74            #[allow(deprecated)]
75            if f.flags() & (1 << FlagV1_DebugUpperHex) !=0 {
76                core::fmt::UpperHex::fmt(self, f)
77            } else { // LowerHex is always the default for debug
78                core::fmt::LowerHex::fmt(self, f)
79            }
80        }
81    }})
82    .into();
83
84    hex.extend(dbg);
85    hex
86}