1use serde::{Deserialize, Serialize};
4use sha2::{Digest, Sha256};
5use std::fmt;
6
7#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Default)]
9pub struct Hash(pub [u8; 32]);
10
11impl Hash {
12 pub const fn new(bytes: [u8; 32]) -> Self {
14 Hash(bytes)
15 }
16
17 #[allow(clippy::self_named_constructors)]
19 pub fn hash(data: &[u8]) -> Self {
20 let mut hasher = Sha256::new();
21 hasher.update(data);
22 let result = hasher.finalize();
23 let mut hash = [0u8; 32];
24 hash.copy_from_slice(&result);
25 Hash(hash)
26 }
27
28 pub fn hash_two_parts(part_a: &[u8], part_b: &[u8]) -> Self {
34 let mut hasher = Sha256::new();
35 hasher.update(part_a);
36 hasher.update(part_b);
37 let result = hasher.finalize();
38 let mut hash = [0u8; 32];
39 hash.copy_from_slice(&result);
40 Hash(hash)
41 }
42
43 pub fn to_hex(&self) -> String {
45 hex::encode(self.0)
46 }
47
48 pub fn from_hex(s: &str) -> Result<Self, String> {
50 let bytes = hex::decode(s).map_err(|e| format!("Invalid hex: {}", e))?;
51 if bytes.len() != 32 {
52 return Err(format!("Invalid length: {} (expected 32)", bytes.len()));
53 }
54 let mut hash = [0u8; 32];
55 hash.copy_from_slice(&bytes);
56 Ok(Hash(hash))
57 }
58}
59
60impl fmt::Display for Hash {
61 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
62 write!(f, "{}", self.to_hex())
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69
70 #[test]
71 fn test_hash() {
72 let data = b"The moss is ready!";
73 let hash = Hash::hash(data);
74 println!("Hash: {}", hash);
75 assert_ne!(hash.0, [0u8; 32]);
76 }
77
78 #[test]
79 fn test_hex_roundtrip() {
80 let original = Hash::hash(b"Lichen");
81 let hex = original.to_hex();
82 let parsed = Hash::from_hex(&hex).unwrap();
83 assert_eq!(original, parsed);
84 }
85}