ambient_ci/
checksummer.rs1use std::{
4 fs::File,
5 io::Read,
6 path::{Path, PathBuf},
7};
8
9use indicatif::{ProgressBar, ProgressStyle};
10use sha2::{Digest, Sha256};
11
12#[derive(Debug, Eq, PartialEq)]
14pub enum Checksum {
15 Sha256(String),
17}
18
19impl std::fmt::Display for Checksum {
20 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
21 let s = match self {
22 Self::Sha256(s) => s,
23 };
24 write!(f, "{s}")
25 }
26}
27
28pub struct Checksummer {
30 filename: PathBuf,
31}
32
33const BUF_SIZE: usize = 1025 * 1024;
34
35impl Checksummer {
36 pub fn new(filename: &Path) -> Self {
38 Self {
39 filename: filename.into(),
40 }
41 }
42
43 pub fn sha256(&self) -> Result<Checksum, ChecksummerError> {
45 let metadata = self
46 .filename
47 .metadata()
48 .map_err(|err| ChecksummerError::GetMetadata(self.filename.clone(), err))?;
49 let bar = ProgressBar::new(metadata.len()).with_style(
50 ProgressStyle::with_template(
51 "read {binary_bytes}/{binary_total_bytes} speed {binary_bytes_per_sec} {wide_bar} ETA {eta}",
52 )
53 .map_err(ChecksummerError::ProgressStyle)?,
54 );
55
56 let mut sha256 = Sha256::new();
57 let mut file = File::open(&self.filename)
58 .map_err(|err| ChecksummerError::Open(self.filename.clone(), err))?;
59
60 let mut buf = vec![0; BUF_SIZE];
61 loop {
62 let mut n = file
63 .read(&mut buf)
64 .map_err(|err| ChecksummerError::Read(self.filename.clone(), err))?;
65
66 if n == 0 {
67 break;
68 }
69
70 sha256.update(&buf[..n]);
71
72 while n >= u64::MAX as usize {
73 bar.inc(u64::MAX);
74 n -= u64::MAX as usize;
75 }
76 bar.inc(n as u64); }
78
79 let checksum = Checksum::Sha256(hex::encode(sha256.finalize()));
80 Ok(checksum)
81 }
82}
83
84#[derive(Debug, thiserror::Error)]
86pub enum ChecksummerError {
87 #[error("failed to get file metadata for {0}")]
89 GetMetadata(PathBuf, #[source] std::io::Error),
90
91 #[error("failed to create a progress bar")]
93 ProgressStyle(#[source] indicatif::style::TemplateError),
94
95 #[error("failed to open file for reading: {0}")]
97 Open(PathBuf, #[source] std::io::Error),
98
99 #[error("failed to read from file: {0}")]
101 Read(PathBuf, #[source] std::io::Error),
102}