1use crate::error::{Result, TensogramError};
10use crate::types::HashDescriptor;
11
12#[derive(Debug, Clone, Copy, PartialEq, Eq)]
13pub enum HashAlgorithm {
14 Xxh3,
15}
16
17impl HashAlgorithm {
18 pub fn as_str(&self) -> &'static str {
19 match self {
20 HashAlgorithm::Xxh3 => "xxh3",
21 }
22 }
23
24 pub fn parse(s: &str) -> Result<Self> {
25 match s {
26 "xxh3" => Ok(HashAlgorithm::Xxh3),
27 _ => Err(TensogramError::Metadata(format!("unknown hash type: {s}"))),
28 }
29 }
30}
31
32pub fn compute_hash(data: &[u8], algorithm: HashAlgorithm) -> String {
34 match algorithm {
35 HashAlgorithm::Xxh3 => {
36 let hash = xxhash_rust::xxh3::xxh3_64(data);
37 format!("{hash:016x}")
38 }
39 }
40}
41
42pub fn verify_hash(data: &[u8], descriptor: &HashDescriptor) -> Result<()> {
48 let algorithm = match HashAlgorithm::parse(&descriptor.hash_type) {
49 Ok(algo) => algo,
50 Err(_) => {
51 tracing::warn!(
52 hash_type = %descriptor.hash_type,
53 "unknown hash algorithm, skipping verification"
54 );
55 return Ok(());
56 }
57 };
58 let actual = compute_hash(data, algorithm);
59 if actual != descriptor.value {
60 return Err(TensogramError::HashMismatch {
61 expected: descriptor.value.clone(),
62 actual,
63 });
64 }
65 Ok(())
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn test_xxh3() {
74 let data = b"hello world";
75 let hash = compute_hash(data, HashAlgorithm::Xxh3);
76 assert_eq!(hash.len(), 16); assert_eq!(hash, compute_hash(data, HashAlgorithm::Xxh3));
79 }
80
81 #[test]
82 fn test_verify_hash() {
83 let data = b"test data";
84 let hash = compute_hash(data, HashAlgorithm::Xxh3);
85 let descriptor = HashDescriptor {
86 hash_type: "xxh3".to_string(),
87 value: hash,
88 };
89 assert!(verify_hash(data, &descriptor).is_ok());
90 }
91
92 #[test]
93 fn test_verify_hash_mismatch() {
94 let data = b"test data";
95 let descriptor = HashDescriptor {
96 hash_type: "xxh3".to_string(),
97 value: "0000000000000000".to_string(),
98 };
99 assert!(verify_hash(data, &descriptor).is_err());
100 }
101
102 #[test]
103 fn test_unknown_hash_type_skips_verification() {
104 let data = b"test data";
105 let descriptor = HashDescriptor {
106 hash_type: "sha256".to_string(),
107 value: "abc123".to_string(),
108 };
109 assert!(verify_hash(data, &descriptor).is_ok());
111 }
112}