ckb_fixed_hash_macros/
lib.rs

1//! Provide several proc-macros to construct const fixed-sized hashes.
2//!
3//! If we use an array to construct const fixed-sized hashes, it's difficult to read.
4//!
5//! If we use [`FromStr::from_str`] to construct fixed-sized hashes, the result is not a constant.
6//! So, it will reduce runtime performance. And it could cause a runtime error if the input is malformed.
7//!
8//! With proc-macros, we can construct human-readable const fixed-sized hashes.
9//! And it will be checked in compile time, it could never cause any runtime error.
10//!
11//! # Notice
12//!
13//! **This is an internal crate used by crate [`ckb_fixed_hash`], do not use this crate directly.**
14//!
15//! All proc-macros in this crate are re-exported in crate [`ckb_fixed_hash`].
16//!
17//! And you can found examples in crate [`ckb_fixed_hash`].
18//!
19//! [`FromStr::from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str
20//! [`ckb_fixed_hash`]: ../ckb_fixed_hash/index.html
21
22extern crate proc_macro;
23
24use std::str::FromStr;
25
26use quote::quote;
27use syn::parse_macro_input;
28
29macro_rules! impl_hash {
30    ($name:ident, $type:ident, $type_str:expr, $link_str:expr) =>    {
31        #[doc = "A proc-macro used to create a const [`"]
32        #[doc = $type_str]
33        #[doc = "`] from a hexadecimal string or a trimmed hexadecimal string.\n\n[`"]
34        #[doc = $type_str]
35        #[doc = "`]:"]
36        #[doc = $link_str]
37        #[proc_macro]
38        pub fn $name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
39            let input = parse_macro_input!(input as syn::LitStr);
40            let expanded = {
41                let input = input.value().replace("_", "");
42                if input.len() < 3 || &input[..2] != "0x" {
43                    panic!("Input has to be a hexadecimal string with 0x-prefix.");
44                };
45                let input_str = &input[2..];
46                let value = match &input_str[..1] {
47                    "0" => {
48                        if input_str.len() > 1 {
49                            ckb_fixed_hash_core::$type::from_str(input_str)
50                        } else {
51                            ckb_fixed_hash_core::$type::from_trimmed_str(input_str)
52                        }
53                    },
54                    _ => {
55                        ckb_fixed_hash_core::$type::from_trimmed_str(input_str)
56                    },
57                }
58                .unwrap_or_else(|err| {
59                    panic!("Failed to parse the input hexadecimal string: {}", err);
60                });
61                let eval_str = format!("{:?}", value);
62                let eval_ts: proc_macro2::TokenStream = eval_str.parse().unwrap_or_else(|_| {
63                    panic!("Failed to parse the string \"{}\" to TokenStream.", eval_str);
64                });
65                quote!(#eval_ts)
66            };
67            expanded.into()
68        }
69    };
70    ($name:ident, $type:ident) => {
71        impl_hash!($name, $type, stringify!($type), concat!("../ckb_fixed_hash_core/struct.", stringify!($type), ".html"));
72    }
73}
74
75impl_hash!(h160, H160);
76impl_hash!(h256, H256);
77impl_hash!(h512, H512);
78impl_hash!(h520, H520);