1use std::marker::PhantomData;
5
6pub trait Hasher {
8 fn new() -> Self;
9 fn update(&mut self, input: &[u8]);
10 fn finalize(self) -> Vec<u8>;
11}
12
13pub trait OnceHasher {
15 type Hasher: Hasher;
16
17 fn hash(&self, input: &[u8]) -> Vec<u8>;
18 fn new_hasher(&self) -> Self::Hasher {
19 Self::Hasher::new()
20 }
21}
22
23pub struct OnceWrapper<H: Hasher>(PhantomData<H>);
25
26impl<H: Hasher> OnceHasher for OnceWrapper<H> {
27 type Hasher = H;
28
29 fn hash(&self, input: &[u8]) -> Vec<u8> {
30 let mut hasher = H::new();
31 hasher.update(input);
32 hasher.finalize()
33 }
34}
35
36#[macro_export]
38macro_rules! hash {
39 ($ohasher:ident, $($arg:expr),+) => {
40 {
41 use $crate::hash::{Hasher, OnceHasher};
42 let mut hasher = $crate::hash::$ohasher.new_hasher();
43 $(hasher.update($arg);)+
44 hasher.finalize()
45 }
46 };
47}
48
49#[macro_export]
51macro_rules! hash_and_encode {
52 ($ohasher:ident, $encoder:ident, $($arg:expr),+) => {
53 {
54 use $crate::hash::{Hasher, OnceHasher};
55 use $crate::encoding::{Encoder};
56 let mut hasher = $crate::hash::$ohasher.new_hasher();
57 $(hasher.update($arg);)+
58 $crate::encoding::$encoder.to_text(&hasher.finalize())
59 }
60 };
61}
62
63mod sha1;
64pub use sha1::Sha1;
65
66pub const SHA1: OnceWrapper<Sha1> = OnceWrapper(PhantomData);
68
69#[macro_export]
71macro_rules! sha1 {
72 ($($arg:expr),+) => {
73 $crate::hash!(SHA1, $($arg),+)
74 };
75}
76
77#[macro_export]
79macro_rules! sha1_hex {
80 ($($arg:expr),+) => {
81 $crate::hash_and_encode!(SHA1, HEX, $($arg),+)
82 };
83}
84
85#[macro_export]
87macro_rules! sha1_base64 {
88 ($($arg:expr),+) => {
89 $crate::hash_and_encode!(SHA1, BASE64, $($arg),+)
90 };
91}
92
93#[cfg(test)]
94mod tests {
95
96 use super::*;
97 use hex_literal::hex;
98 use rstest::*;
99
100 #[rstest]
101 #[case(Sha1::new())]
102 fn update_finalize(#[case] mut hasher: impl Hasher) {
103 let data1 = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}"#;
105 let data2 = "HyVFkGl5F5OQWJZZaNzBBg==";
106 hasher.update(data1.as_bytes());
107 hasher.update(data2.as_bytes());
108 let result = hasher.finalize();
109 assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
110 }
111
112 #[rstest]
113 #[case(SHA1)]
114 fn hash(#[case] hasher: impl OnceHasher) {
115 let data = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}HyVFkGl5F5OQWJZZaNzBBg=="#;
116 let result = hasher.hash(data.as_bytes());
117 assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
118 }
119
120 #[test]
121 fn macro_test() {
122 let data1 = r#"{"nickName":"Band","gender":1,"language":"zh_CN","city":"Guangzhou","province":"Guangdong","country":"CN","avatarUrl":"http://wx.qlogo.cn/mmopen/vi_32/1vZvI39NWFQ9XM4LtQpFrQJ1xlgZxx3w7bQxKARol6503Iuswjjn6nIGBiaycAjAtpujxyzYsrztuuICqIM5ibXQ/0"}"#;
123 let data2 = "HyVFkGl5F5OQWJZZaNzBBg==";
124 let result = crate::sha1!(data1.as_bytes(), data2.as_bytes());
125 assert_eq!(result[..], hex!("75e81ceda165f4ffa64f4068af58c64b8f54b88c"));
126 let result = crate::sha1_hex!(data1.as_bytes(), data2.as_bytes());
127 assert_eq!(result, "75e81ceda165f4ffa64f4068af58c64b8f54b88c");
128 let result = crate::sha1_base64!(data1.as_bytes(), data2.as_bytes());
129 assert_eq!(result, "degc7aFl9P+mT0Bor1jGS49UuIw=");
130 }
131}