hdiff_update_core/
hash.rs1use std::{
2 fs::File,
3 io::{BufReader, Read},
4 path::{Path, PathBuf},
5};
6
7use serde::{Deserialize, Serialize};
8use sha2::{Digest, Sha256};
9
10use crate::{error::io_path, Result};
11
12#[derive(Debug, Clone, Serialize, Deserialize)]
13#[serde(rename_all = "camelCase")]
14pub struct FileDigest {
15 pub path: PathBuf,
16 pub sha256: String,
17 pub size: u64,
18}
19
20pub fn sha256_file(path: impl AsRef<Path>) -> Result<FileDigest> {
21 let path = path.as_ref();
22 let file = File::open(path).map_err(|error| io_path(path, error))?;
23 let size = file.metadata().map_err(|error| io_path(path, error))?.len();
24 let mut reader = BufReader::with_capacity(1024 * 1024, file);
25 let mut hasher = Sha256::new();
26 let mut buf = vec![0_u8; 1024 * 1024];
27
28 loop {
29 let read = reader
30 .read(&mut buf)
31 .map_err(|error| io_path(path, error))?;
32 if read == 0 {
33 break;
34 }
35 hasher.update(&buf[..read]);
36 }
37
38 Ok(FileDigest {
39 path: path.to_path_buf(),
40 sha256: hex::encode(hasher.finalize()),
41 size,
42 })
43}
44
45pub fn sha256_bytes(bytes: &[u8]) -> String {
46 let mut hasher = Sha256::new();
47 hasher.update(bytes);
48 hex::encode(hasher.finalize())
49}
50
51pub fn verify_sha256(path: impl AsRef<Path>, expected: &str) -> Result<FileDigest> {
52 let digest = sha256_file(path.as_ref())?;
53 if !digest.sha256.eq_ignore_ascii_case(expected) {
54 return Err(crate::Error::HashMismatch {
55 path: path.as_ref().to_path_buf(),
56 expected: expected.to_string(),
57 actual: digest.sha256,
58 });
59 }
60 Ok(digest)
61}