Skip to main content

parsentry_cache/
key.rs

1//! Cache key generation using SHA256 hashing
2
3use sha2::{Digest, Sha256};
4
5/// Current cache version - increment to invalidate all entries
6pub const CACHE_VERSION: &str = "1.0.0";
7
8/// Generate a deterministic cache key from arbitrary string parts.
9///
10/// Returns a SHA256 hex digest of `CACHE_VERSION` joined with `parts` by `"|"`.
11pub fn hash_key(parts: &[&str]) -> String {
12    let mut hasher = Sha256::new();
13    hasher.update(CACHE_VERSION.as_bytes());
14    for part in parts {
15        hasher.update(b"|");
16        hasher.update(part.as_bytes());
17    }
18    format!("{:x}", hasher.finalize())
19}
20
21#[cfg(test)]
22mod tests {
23    use super::*;
24
25    #[test]
26    fn test_deterministic() {
27        let k1 = hash_key(&["a", "b", "c"]);
28        let k2 = hash_key(&["a", "b", "c"]);
29        assert_eq!(k1, k2);
30    }
31
32    #[test]
33    fn test_different_parts_differ() {
34        let k1 = hash_key(&["a", "b"]);
35        let k2 = hash_key(&["a", "c"]);
36        assert_ne!(k1, k2);
37    }
38
39    #[test]
40    fn test_different_length_parts_differ() {
41        let k1 = hash_key(&["a"]);
42        let k2 = hash_key(&["a", "b"]);
43        assert_ne!(k1, k2);
44    }
45
46    #[test]
47    fn test_empty_parts() {
48        let k = hash_key(&[]);
49        assert_eq!(k.len(), 64);
50    }
51
52    #[test]
53    fn test_key_is_64_hex_chars() {
54        let k = hash_key(&["test", "data"]);
55        assert_eq!(k.len(), 64);
56        assert!(k.chars().all(|c| c.is_ascii_hexdigit()));
57    }
58
59    #[test]
60    fn test_version_is_embedded() {
61        // Changing the version constant would change output; we verify by
62        // checking the hash includes CACHE_VERSION (indirectly: same parts
63        // produce the same hash only if version is constant).
64        let k1 = hash_key(&["x"]);
65        let k2 = hash_key(&["x"]);
66        assert_eq!(k1, k2);
67    }
68}