diff --git a/notes.md b/notes.md index 7fc5428..caab524 100644 --- a/notes.md +++ b/notes.md @@ -37,10 +37,6 @@ - yvk repo: https://code.yavook.de/jmm/shrupl - sharry issue: https://github.com/eikek/sharry/issues/1659 -- ureq: https://stackoverflow.com/questions/59586787/rust-how-to-do-http-put-of-large-files -- hashing: https://duckduckgo.com/?q=rust+get+file+hash&t=canonical&ia=web - - https://stackoverflow.com/q/69787906 - - https://github.com/RustCrypto/hashes # Ideas diff --git a/src/appstate.rs b/src/appstate.rs index fae1a55..db76e68 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -1,6 +1,6 @@ use std::{fmt, io, time::Duration}; -use indicatif::{ProgressBar, ProgressDrawTarget}; +use indicatif::ProgressBar; use log::{debug, warn}; use crate::{ @@ -62,7 +62,6 @@ impl AppState { if let Some(upl) = self.inner.peek_uploading() { if bar.length().is_none() { - bar.set_draw_target(ProgressDrawTarget::stderr()); bar.set_length(upl.get_size()); bar.set_message(upl.get_name().to_owned()); bar.enable_steady_tick(Duration::from_millis(100)); @@ -70,8 +69,7 @@ impl AppState { bar.set_position(upl.get_offset()); // BUG in `indicatif` crate? - // `set_position` does not force an immediate redraw, so we also call `inc_length` here - bar.inc_length(0); + // `set_position` does not force an immediate redraw like e.g. `inc_length` } f(bar); diff --git a/src/bin/shrupl.rs b/src/bin/shrupl.rs index 9ae6ede..31ac3e9 100644 --- a/src/bin/shrupl.rs +++ b/src/bin/shrupl.rs @@ -90,13 +90,13 @@ fn main() { match p { // Error 404 (File not found) error::Parameter::FileID(fid) => { - info!("requeueing file {fid:?}"); + info!("retrying file {fid:?}"); state.abort_upload(); } // Error 404 (Share not found) error::Parameter::ShareID(sid) => { - // TODO ask + output::prompt_rebuild_share(); info!("rebuilding share {sid:?}"); // rebuild share diff --git a/src/cachefile.rs b/src/cachefile.rs index d0a0592..9d0dba3 100644 --- a/src/cachefile.rs +++ b/src/cachefile.rs @@ -6,7 +6,7 @@ use std::{ time::Duration, }; -use indicatif::{ProgressBar, ProgressDrawTarget}; +use indicatif::ProgressBar; use log::{info, trace}; use serde::{Deserialize, Serialize}; @@ -63,15 +63,7 @@ impl CacheFile { bar: &ProgressBar, ) -> error::Result<()> { bar.set_message(format!("checking {:?}", file.get_name())); - - 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()), - } + file.check_hash(|bytes| bar.inc(bytes)) } info!("checking files in {state:?}"); @@ -87,7 +79,6 @@ impl CacheFile { }; let bar = new_progressbar(); - bar.set_draw_target(ProgressDrawTarget::stderr()); bar.set_length(total_size); bar.enable_steady_tick(Duration::from_millis(50)); @@ -115,7 +106,6 @@ impl CacheFile { info!("hashing files {files:?}"); let bar = new_progressbar(); - bar.set_draw_target(ProgressDrawTarget::stderr()); // BOOKMARK assumption: total file size < 2 EiB bar.set_length(files.iter().map(FileTrait::get_size).sum()); bar.enable_steady_tick(Duration::from_millis(50)); diff --git a/src/error.rs b/src/error.rs index 28cc047..ffbe246 100644 --- a/src/error.rs +++ b/src/error.rs @@ -28,12 +28,15 @@ pub enum Error { #[error(transparent)] StdIo(#[from] std::io::Error), - #[error("response error: {0}")] + #[error("Response error: {0}")] Response(String), #[error("Invalid {0}")] InvalidParameter(Parameter), + #[error("Mismatch, expected {expected:?} but got {actual:?}")] + Mismatch { expected: String, actual: String }, + #[error("Unknown error: {0}")] Unknown(String), } @@ -61,14 +64,18 @@ impl Error { Self::Response(into_string(e)) } - pub fn unknown(e: impl ToString) -> Self { - Self::Unknown(into_string(e)) + pub fn mismatch(expected: impl ToString, actual: impl ToString) -> Self { + Self::Mismatch { + expected: into_string(expected), + actual: into_string(actual), + } } #[must_use] pub fn is_fatal(&self) -> bool { match self { Self::InvalidParameter(p) => p.is_fatal(), + Self::Mismatch { .. } => true, Self::Unknown(_) => true, _ => false, } diff --git a/src/file/checked.rs b/src/file/checked.rs index 07688f7..92e9ef7 100644 --- a/src/file/checked.rs +++ b/src/file/checked.rs @@ -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() { - return Err(io::Error::other(format!( - "file {:?} is already hashed!", - self.path.display() - ))); + return Err(error::Error::mismatch("unhashed file", self.path.display())); } self.hash = Some(super::compute_file_hash(&self.path, self.size, f)?); @@ -101,7 +98,7 @@ impl<'t> FileTrait<'t> for Checked { self.size } - fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result { + 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) } } diff --git a/src/file/mod.rs b/src/file/mod.rs index f2df684..95436a8 100644 --- a/src/file/mod.rs +++ b/src/file/mod.rs @@ -2,22 +2,19 @@ mod checked; mod chunk; mod uploading; -use std::{ - ffi::OsStr, - fs, - io::{self, Read}, - path::Path, -}; +use std::{ffi::OsStr, fs, io::Read, path::Path}; use base64ct::{Base64, Encoding}; use blake2b_simd::Params as Blake2b; pub use checked::Checked; pub use chunk::Chunk; -use log::debug; +use log::{debug, warn}; pub use uploading::Uploading; -fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> io::Result { +use crate::error; + +fn compute_file_hash(path: &Path, size: u64, on_progress: impl Fn(u64)) -> error::Result { let mut file = fs::File::open(path)?; 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 { - return Err(io::Error::other(format!( - "Hashed {bytes_read:?} bytes, known file size {size:?}!" - ))); + return Err(error::Error::mismatch(size, bytes_read)); } let result = Base64::encode_string(hasher.finalize().as_bytes()); @@ -51,15 +46,20 @@ fn check_file_hash( size: u64, hash: Option<&String>, on_progress: impl Fn(u64), -) -> io::Result { - let Some(hash) = hash else { - debug!("no hash to check for {:?}!", path.display()); - return Ok(false); +) -> error::Result<()> { + let Some(expected) = hash else { + return Err(error::Error::mismatch("hash", path.display())); }; - let result = *hash == compute_file_hash(path, size, on_progress)?; - debug!("matches {:?}: {result:?}", *hash); - Ok(result) + let actual = &compute_file_hash(path, size, on_progress)?; + + 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> { @@ -80,5 +80,5 @@ pub trait FileTrait<'t> { /// get the file's size fn get_size(&self) -> u64; - fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result; + fn check_hash(&self, on_progress: impl Fn(u64)) -> error::Result<()>; } diff --git a/src/file/uploading.rs b/src/file/uploading.rs index 66f33d5..6303efa 100644 --- a/src/file/uploading.rs +++ b/src/file/uploading.rs @@ -7,7 +7,7 @@ use std::{ use log::warn; use serde::{Deserialize, Serialize}; -use crate::sharry; +use crate::{error, sharry}; use super::{Checked, Chunk, FileTrait}; @@ -108,7 +108,7 @@ impl<'t> FileTrait<'t> for Uploading { self.size } - fn check_hash(&self, on_progress: impl Fn(u64)) -> io::Result { + 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) } } diff --git a/src/impl_ureq.rs b/src/impl_ureq.rs index 8e8f87a..0a2721c 100644 --- a/src/impl_ureq.rs +++ b/src/impl_ureq.rs @@ -42,7 +42,7 @@ fn find_cause( error.into() } } - error => error::Error::unknown(error), + error => error::Error::Unknown(error.to_string()), } } diff --git a/src/output.rs b/src/output.rs index 7e52946..4d1bca9 100644 --- a/src/output.rs +++ b/src/output.rs @@ -1,9 +1,9 @@ use std::{fmt, process, sync::LazyLock}; use console::{StyledObject, style}; -use dialoguer::{Select, theme::ColorfulTheme}; +use dialoguer::{Confirm, Select, theme::ColorfulTheme}; use indicatif::{ProgressBar, ProgressStyle}; -use log::{error, info}; +use log::{info, warn}; type StaticStyled<'t> = LazyLock>; @@ -36,6 +36,23 @@ pub fn prompt_continue() -> bool { selection == 0 } +pub fn prompt_rebuild_share() { + let prompt = format!( + "Target Share cannot be accessed. {}", + style("Completely restart upload?").cyan() + ); + + let selection = Confirm::with_theme(&ColorfulTheme::default()) + .with_prompt(prompt) + .default(true) + .interact() + .unwrap_or(false); + + if selection == false { + process::exit(0); + } +} + pub fn style_all<'t, F>(strs: &[&'t str], f: F) -> Vec where F: Fn(StyledObject<&'t str>) -> StyledObject<&'t str>, @@ -46,7 +63,7 @@ where #[must_use] #[allow(clippy::missing_panics_doc)] pub fn new_progressbar() -> ProgressBar { - ProgressBar::hidden().with_style( + ProgressBar::no_length().with_style( ProgressStyle::with_template(&format!( concat!( "{{bar:50.cyan/blue}} {{msg:.magenta}}: ", @@ -78,7 +95,7 @@ impl Log { pub fn handle(e: &crate::error::Error) { if e.is_fatal() { // react to fatal error - error!("fatal error: {e:?}"); + warn!("fatal error: {e:?}"); Self::error(e); } diff --git a/src/sharry/api/id.rs b/src/sharry/api/id.rs index 71b7610..4f88db3 100644 --- a/src/sharry/api/id.rs +++ b/src/sharry/api/id.rs @@ -56,9 +56,10 @@ impl TryFrom for FileID { Ok(result) } else { - Err(error::Error::unknown(format!( - "Could not extract File ID from {value:?}" - ))) + Err(error::Error::mismatch( + ":///api/v2/alias/upload//files/tus/", + value, + )) } } }