use crate::Checksums;
use digest::Digest;
use std::io::{Read, Write};
use thiserror::Error;
#[derive(Error, Debug)]
pub enum ValidationError {
#[error("The checksum didn't match, got {actual}")]
ChecksumMismatch { actual: String },
#[error("The checksum was empty")]
ChecksumEmpty,
#[error("Reading failed: {0}")]
IO(#[from] std::io::Error),
}
pub trait Validate {
fn validate<R: Read>(&self, source: &mut R) -> Result<(), ValidationError>;
}
fn validate<D: Digest + Write, R: Read>(
reader: &mut R,
checksum: &[u8],
) -> Result<(), ValidationError> {
if checksum.is_empty() {
Err(ValidationError::ChecksumEmpty)
} else {
let mut digest = D::new();
std::io::copy(reader, &mut digest)?;
let hash = digest.finalize();
if hash.as_slice() == checksum {
Ok(())
} else {
Err(ValidationError::ChecksumMismatch {
actual: hex::encode(hash),
})
}
}
}
impl Validate for Checksums {
fn validate<R: Read>(&self, source: &mut R) -> Result<(), ValidationError> {
match self {
Checksums { b2: Some(b2), .. } => validate::<blake2::Blake2b, _>(source, &b2),
Checksums {
sha512: Some(sha512),
..
} => validate::<sha2::Sha512, _>(source, &sha512),
Checksums {
sha256: Some(sha256),
..
} => validate::<sha2::Sha256, _>(source, &sha256),
Checksums {
sha1: Some(sha1), ..
} => validate::<sha1::Sha1, _>(source, &sha1),
Checksums { sha1: None, .. } => Err(ValidationError::ChecksumEmpty),
}
}
}