1use std::path::Path;
2
3use crate::error::{HashError, HashResult};
4
5pub fn calculate_checksum<P: AsRef<Path>>(file_path: P) -> HashResult<String> {
32 let file_path = file_path.as_ref();
33 let mut hasher = blake3::Hasher::new();
34 hasher.update_mmap(file_path).map_err(|err| {
35 HashError::ReadFailed {
36 path: file_path.to_path_buf(),
37 source: err,
38 }
39 })?;
40 Ok(hasher.finalize().to_hex().to_string())
41}
42
43pub fn verify_checksum<P: AsRef<Path>>(file_path: P, expected: &str) -> HashResult<bool> {
70 let file_path = file_path.as_ref();
71 let actual = calculate_checksum(file_path)?;
72 Ok(actual.eq_ignore_ascii_case(expected))
73}
74
75pub fn hash_string(input: &str) -> String {
92 let mut hasher = blake3::Hasher::new();
93 hasher.update(input.as_bytes());
94 hasher.finalize().to_hex().to_string()
95}
96
97#[cfg(test)]
98mod tests {
99 use std::io::Write;
100
101 use tempfile::NamedTempFile;
102
103 use super::{calculate_checksum, verify_checksum};
104
105 #[test]
106 fn test_calculate_checksum() {
107 let mut file = NamedTempFile::new().unwrap();
108 file.write_all(b"hello world\n").unwrap();
109 let path = file.path();
110
111 let checksum = calculate_checksum(path).unwrap();
112 assert_eq!(
113 checksum,
114 "dc5a4edb8240b018124052c330270696f96771a63b45250a5c17d3000e823355"
115 );
116 }
117
118 #[test]
119 fn test_verify_checksum_valid() {
120 let mut file = NamedTempFile::new().unwrap();
121 file.write_all(b"hello world\n").unwrap();
122 let path = file.path();
123
124 let result = verify_checksum(
125 path,
126 "dc5a4edb8240b018124052c330270696f96771a63b45250a5c17d3000e823355",
127 )
128 .unwrap();
129 assert!(result);
130 }
131
132 #[test]
133 fn test_verify_checksum_invalid() {
134 let mut file = NamedTempFile::new().unwrap();
135 file.write_all(b"hello world").unwrap();
136 let path = file.path();
137
138 let result = verify_checksum(path, "invalid-checksum").unwrap();
139 assert!(!result);
140 }
141
142 #[test]
143 fn test_calculate_checksum_file_not_found() {
144 let result = calculate_checksum("/path/to/nonexistent/file");
145 assert!(result.is_err());
146 }
147
148 #[test]
149 fn test_verify_checksum_file_not_found() {
150 let result = verify_checksum("/path/to/nonexistent/file", "any-checksum");
151 assert!(result.is_err());
152 }
153
154 #[test]
155 fn test_calculate_checksum_on_directory() {
156 let dir = tempfile::tempdir().unwrap();
157 let result = calculate_checksum(dir.path());
158 assert!(result.is_err());
159 }
160}