1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//! Provide several proc-macros to construct const fixed-sized hashes.
//!
//! If we use an array to construct const fixed-sized hashes, it's difficult to read.
//!
//! If we use [`FromStr::from_str`] to construct fixed-sized hashes, the result is not a constant.
//! So, it will reduce runtime performance. And it could cause a runtime error if the input is malformed.
//!
//! With proc-macros, we can construct human-readable const fixed-sized hashes.
//! And it will be checked in compile time, it could never cause any runtime error.
//!
//! # Notice
//!
//! **This is an internal crate used by crate [`ckb_fixed_hash`], do not use this crate directly.**
//!
//! All proc-macros in this crate are re-exported in crate [`ckb_fixed_hash`].
//!
//! And you can found examples in crate [`ckb_fixed_hash`].
//!
//! [`FromStr::from_str`]: https://doc.rust-lang.org/std/str/trait.FromStr.html#tymethod.from_str
//! [`ckb_fixed_hash`]: ../ckb_fixed_hash/index.html

extern crate proc_macro;

use std::str::FromStr;

use quote::quote;
use syn::parse_macro_input;

macro_rules! impl_hash {
    ($name:ident, $type:ident, $type_str:expr, $link_str:expr) =>    {
        #[doc = "A proc-macro used to create a const [`"]
        #[doc = $type_str]
        #[doc = "`] from a hexadecimal string or a trimmed hexadecimal string.\n\n[`"]
        #[doc = $type_str]
        #[doc = "`]:"]
        #[doc = $link_str]
        #[proc_macro]
        pub fn $name(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
            let input = parse_macro_input!(input as syn::LitStr);
            let expanded = {
                let input = input.value().replace("_", "");
                if input.len() < 3 || &input[..2] != "0x" {
                    panic!("Input has to be a hexadecimal string with 0x-prefix.");
                };
                let input_str = &input[2..];
                let value = match &input_str[..1] {
                    "0" => {
                        if input_str.len() > 1 {
                            ckb_fixed_hash_core::$type::from_str(input_str)
                        } else {
                            ckb_fixed_hash_core::$type::from_trimmed_str(input_str)
                        }
                    },
                    _ => {
                        ckb_fixed_hash_core::$type::from_trimmed_str(input_str)
                    },
                }
                .unwrap_or_else(|err| {
                    panic!("Failed to parse the input hexadecimal string: {}", err);
                });
                let eval_str = format!("{:?}", value);
                let eval_ts: proc_macro2::TokenStream = eval_str.parse().unwrap_or_else(|_| {
                    panic!("Failed to parse the string \"{}\" to TokenStream.", eval_str);
                });
                quote!(#eval_ts)
            };
            expanded.into()
        }
    };
    ($name:ident, $type:ident) => {
        impl_hash!($name, $type, stringify!($type), concat!("../ckb_fixed_hash_core/struct.", stringify!($type), ".html"));
    }
}

impl_hash!(h160, H160);
impl_hash!(h256, H256);
impl_hash!(h512, H512);
impl_hash!(h520, H520);