ckb_fixed_hash_macros/lib.rs
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);