1use sha2::{Digest, Sha256};
2use std::collections::HashMap;
3
4pub fn compute_hash(content: &str) -> String {
7 let mut hasher = Sha256::new();
8 hasher.update(content.as_bytes());
9 let result = hasher.finalize();
10 format!("{:x}", result)[..16].to_string()
12}
13
14pub fn compute_ir_hash(ir_content: &str) -> String {
17 compute_hash(ir_content)
18}
19
20pub fn compute_file_hash(file_content: &str) -> String {
22 compute_hash(file_content)
23}
24
25pub fn compute_plugin_hash(files: &HashMap<String, String>) -> String {
28 let mut content = String::new();
29
30 let mut file_names: Vec<_> = files.keys().collect();
32 file_names.sort();
33
34 for name in file_names {
36 let file_hash = compute_file_hash(&files[name]);
37 content.push_str(&format!("{}:{}\n", name, file_hash));
38 }
39
40 compute_hash(&content)
41}
42
43pub fn compute_file_hashes(files: &HashMap<String, String>) -> HashMap<String, String> {
46 files
47 .iter()
48 .map(|(name, content)| (name.clone(), compute_file_hash(content)))
49 .collect()
50}
51
52#[cfg(test)]
53mod tests {
54 use super::*;
55
56 #[test]
57 fn test_compute_hash_deterministic() {
58 let content = "hello world";
59 let hash1 = compute_hash(content);
60 let hash2 = compute_hash(content);
61 assert_eq!(hash1, hash2, "Hash should be deterministic");
62 }
63
64 #[test]
65 fn test_compute_hash_length() {
66 let hash = compute_hash("test");
67 assert_eq!(hash.len(), 16, "Hash should be 16 characters");
68 }
69
70 #[test]
71 fn test_compute_hash_different_inputs() {
72 let hash1 = compute_hash("foo");
73 let hash2 = compute_hash("bar");
74 assert_ne!(hash1, hash2, "Different inputs should produce different hashes");
75 }
76
77 #[test]
78 fn test_compute_plugin_hash_sorted() {
79 let mut files1 = HashMap::new();
80 files1.insert("a.ts".to_string(), "content a".to_string());
81 files1.insert("b.ts".to_string(), "content b".to_string());
82
83 let mut files2 = HashMap::new();
84 files2.insert("b.ts".to_string(), "content b".to_string());
85 files2.insert("a.ts".to_string(), "content a".to_string());
86
87 let hash1 = compute_plugin_hash(&files1);
88 let hash2 = compute_plugin_hash(&files2);
89
90 assert_eq!(
91 hash1, hash2,
92 "Plugin hash should be same regardless of insertion order"
93 );
94 }
95
96 #[test]
97 fn test_compute_file_hashes() {
98 let mut files = HashMap::new();
99 files.insert("types.ts".to_string(), "type Foo = string;".to_string());
100 files.insert("methods.ts".to_string(), "export function bar() {}".to_string());
101
102 let hashes = compute_file_hashes(&files);
103
104 assert_eq!(hashes.len(), 2);
105 assert!(hashes.contains_key("types.ts"));
106 assert!(hashes.contains_key("methods.ts"));
107 assert_eq!(hashes["types.ts"].len(), 16);
108 assert_eq!(hashes["methods.ts"].len(), 16);
109 }
110}