1use std::path::Path;
7use sha1::{Sha1, Digest};
8use std::io::Read;
9use tokio::fs;
10use thiserror::Error;
11
12#[derive(Debug, Error)]
14pub enum HashError {
15 #[error("IO error: {0}")]
16 Io(#[from] std::io::Error),
17
18 #[error("SHA1 mismatch: expected {expected}, got {actual}")]
19 Mismatch { expected: String, actual: String },
20}
21
22pub type HashResult<T> = Result<T, HashError>;
23
24pub async fn verify_file_sha1(path: &Path, expected_sha1: &str) -> HashResult<bool> {
26 let content = fs::read(path).await?;
27
28 let mut hasher = Sha1::new();
29 hasher.update(&content);
30 let calculated_sha1 = hex::encode(hasher.finalize());
31
32 Ok(calculated_sha1.eq_ignore_ascii_case(expected_sha1))
33}
34
35pub async fn verify_file_sha1_streaming(path: &Path, expected_sha1: &str) -> HashResult<bool> {
37 use tokio::io::AsyncReadExt;
38
39 let mut file = fs::File::open(path).await?;
40 let mut hasher = Sha1::new();
41 let mut buffer = [0u8; 8192];
42
43 loop {
44 let n = file.read(&mut buffer).await?;
45 if n == 0 {
46 break;
47 }
48 hasher.update(&buffer[..n]);
49 }
50
51 let calculated_sha1 = hex::encode(hasher.finalize());
52 Ok(calculated_sha1.eq_ignore_ascii_case(expected_sha1))
53}
54
55pub fn calculate_file_sha1_sync(path: &Path) -> HashResult<String> {
57 let mut file = std::fs::File::open(path)?;
58 let mut hasher = Sha1::new();
59 let mut buffer = [0u8; 8192];
60
61 loop {
62 let n = file.read(&mut buffer)?;
63 if n == 0 {
64 break;
65 }
66 hasher.update(&buffer[..n]);
67 }
68
69 Ok(hex::encode(hasher.finalize()))
70}
71
72pub fn verify_file_sha1_sync(path: &Path, expected_sha1: &str) -> HashResult<bool> {
74 let calculated_sha1 = calculate_file_sha1_sync(path)?;
75 Ok(calculated_sha1.eq_ignore_ascii_case(expected_sha1))
76}
77
78pub fn calculate_sha1_bytes(data: &[u8]) -> String {
80 let mut hasher = Sha1::new();
81 hasher.update(data);
82 hex::encode(hasher.finalize())
83}
84
85pub fn calculate_sha1_bytes_raw(data: &[u8]) -> [u8; 20] {
87 let mut hasher = Sha1::new();
88 hasher.update(data);
89 hasher.finalize().into()
90}