create Error::Mismatch variant

This commit is contained in:
Jörn-Michael Miehe 2025-06-26 09:56:29 +00:00
parent 9de8f948dc
commit b17f239801
8 changed files with 43 additions and 44 deletions

View file

@ -63,15 +63,7 @@ impl CacheFile {
bar: &ProgressBar, bar: &ProgressBar,
) -> error::Result<()> { ) -> error::Result<()> {
bar.set_message(format!("checking {:?}", file.get_name())); bar.set_message(format!("checking {:?}", file.get_name()));
file.check_hash(|bytes| bar.inc(bytes))
match file.check_hash(|bytes| bar.inc(bytes)) {
Ok(true) => Ok(()),
Ok(false) => Err(error::Error::unknown(format!(
"Hash mismatch for file {:?}!",
file.get_name()
))),
Err(e) => Err(e.into()),
}
} }
info!("checking files in {state:?}"); info!("checking files in {state:?}");

View file

@ -26,12 +26,15 @@ pub enum Error {
#[error(transparent)] #[error(transparent)]
StdIo(#[from] std::io::Error), StdIo(#[from] std::io::Error),
#[error("response error: {0}")] #[error("Response error: {0}")]
Response(String), Response(String),
#[error("Invalid {0}")] #[error("Invalid {0}")]
InvalidParameter(Parameter), InvalidParameter(Parameter),
#[error("Mismatch, expected {expected:?} but got {actual:?}")]
Mismatch { expected: String, actual: String },
#[error("Unknown error: {0}")] #[error("Unknown error: {0}")]
Unknown(String), Unknown(String),
} }
@ -59,14 +62,18 @@ impl Error {
Self::Response(into_string(e)) Self::Response(into_string(e))
} }
pub fn unknown(e: impl ToString) -> Self { pub fn mismatch(expected: impl ToString, actual: impl ToString) -> Self {
Self::Unknown(into_string(e)) Self::Mismatch {
expected: into_string(expected),
actual: into_string(actual),
}
} }
#[must_use] #[must_use]
pub fn is_fatal(&self) -> bool { pub fn is_fatal(&self) -> bool {
match self { match self {
Self::InvalidParameter(p) => p.is_fatal(), Self::InvalidParameter(p) => p.is_fatal(),
Self::Mismatch { .. } => true,
Self::Unknown(_) => true, Self::Unknown(_) => true,
_ => false, _ => false,
} }

View file

@ -53,12 +53,9 @@ impl Checked {
} }
} }
pub fn hash(&mut self, f: impl Fn(u64)) -> io::Result<()> { pub fn hash(&mut self, f: impl Fn(u64)) -> error::Result<()> {
if self.hash.is_some() { if self.hash.is_some() {
return Err(io::Error::other(format!( return Err(error::Error::mismatch("unhashed file", self.path.display()));
"file {:?} is already hashed!",
self.path.display()
)));
} }
self.hash = Some(super::compute_file_hash(&self.path, self.size, f)?); self.hash = Some(super::compute_file_hash(&self.path, self.size, f)?);
@ -101,7 +98,7 @@ impl<'t> FileTrait<'t> for Checked {
self.size self.size
} }
fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result<bool> { fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()> {
super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress) super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress)
} }
} }

View file

@ -2,22 +2,19 @@ mod checked;
mod chunk; mod chunk;
mod uploading; mod uploading;
use std::{ use std::{ffi::OsStr, fs, io::Read, path::Path};
ffi::OsStr,
fs,
io::{self, Read},
path::Path,
};
use base64ct::{Base64, Encoding}; use base64ct::{Base64, Encoding};
use blake2b_simd::Params as Blake2b; use blake2b_simd::Params as Blake2b;
pub use checked::Checked; pub use checked::Checked;
pub use chunk::Chunk; pub use chunk::Chunk;
use log::debug; use log::{debug, warn};
pub use uploading::Uploading; pub use uploading::Uploading;
fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> io::Result<String> { use crate::error;
fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> error::Result<String> {
let mut file = fs::File::open(path)?; let mut file = fs::File::open(path)?;
let mut hasher = Blake2b::new().hash_length(64).to_state(); let mut hasher = Blake2b::new().hash_length(64).to_state();
@ -36,9 +33,7 @@ fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> io::R
} }
if bytes_read != size { if bytes_read != size {
return Err(io::Error::other(format!( return Err(error::Error::mismatch(size, bytes_read));
"Hashed {bytes_read:?} bytes, known file size {size:?}!"
)));
} }
let result = Base64::encode_string(hasher.finalize().as_bytes()); let result = Base64::encode_string(hasher.finalize().as_bytes());
@ -51,15 +46,20 @@ fn check_file_hash(
size: u64, size: u64,
hash: Option<&String>, hash: Option<&String>,
on_progress: impl Fn(u64), on_progress: impl Fn(u64),
) -> io::Result<bool> { ) -> error::Result<()> {
let Some(hash) = hash else { let Some(expected) = hash else {
debug!("no hash to check for {:?}!", path.display()); return Err(error::Error::mismatch("hash", path.display()));
return Ok(false);
}; };
let result = *hash == compute_file_hash(path, size, on_progress)?; let actual = &compute_file_hash(path, size, on_progress)?;
debug!("matches {:?}: {result:?}", *hash);
Ok(result) if expected == actual {
debug!("hash matches {expected:?}");
Ok(())
} else {
warn!("hash mismatch for file {:?}", path.display());
Err(error::Error::mismatch(expected, actual))
}
} }
pub trait FileTrait<'t> { pub trait FileTrait<'t> {
@ -80,5 +80,5 @@ pub trait FileTrait<'t> {
/// get the file's size /// get the file's size
fn get_size(&self) -> u64; fn get_size(&self) -> u64;
fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result<bool>; fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()>;
} }

View file

@ -7,6 +7,8 @@ use std::{
use log::warn; use log::warn;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::error;
use super::{Checked, Chunk, FileTrait}; use super::{Checked, Chunk, FileTrait};
#[derive(Serialize, Deserialize, Debug)] #[derive(Serialize, Deserialize, Debug)]
@ -101,7 +103,7 @@ impl<'t> FileTrait<'t> for Uploading {
self.size self.size
} }
fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result<bool> { fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()> {
super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress) super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress)
} }
} }

View file

@ -42,7 +42,7 @@ fn find_cause(
error.into() error.into()
} }
} }
error => error::Error::unknown(error), error => error::Error::Unknown(error.to_string()),
} }
} }

View file

@ -3,7 +3,7 @@ use std::{fmt, process, sync::LazyLock};
use console::{StyledObject, style}; use console::{StyledObject, style};
use dialoguer::{Confirm, Select, theme::ColorfulTheme}; use dialoguer::{Confirm, Select, theme::ColorfulTheme};
use indicatif::{ProgressBar, ProgressStyle}; use indicatif::{ProgressBar, ProgressStyle};
use log::{error, info}; use log::{info, warn};
type StaticStyled<'t> = LazyLock<StyledObject<&'t str>>; type StaticStyled<'t> = LazyLock<StyledObject<&'t str>>;
@ -95,7 +95,7 @@ impl Log {
pub fn handle(e: &crate::error::Error) { pub fn handle(e: &crate::error::Error) {
if e.is_fatal() { if e.is_fatal() {
// react to fatal error // react to fatal error
error!("fatal error: {e:?}"); warn!("fatal error: {e:?}");
Self::error(e); Self::error(e);
} }

View file

@ -32,9 +32,10 @@ pub trait Client {
{ {
Ok(fid) Ok(fid)
} else { } else {
Err(error::Error::unknown(format!( Err(error::Error::mismatch(
"Could not extract File ID from {uri:?}" "<proto>://<base>/api/v2/alias/upload/<share>/files/tus/<file>",
))) uri,
))
} }
} }