diff --git a/Cargo.lock b/Cargo.lock index 0e62816..12504e4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -79,6 +79,12 @@ version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" + [[package]] name = "bytes" version = "1.10.1" @@ -491,6 +497,18 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "indicatif" +version = "0.17.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "183b3088984b400f4cfac3620d5e076c84da5364016b4f49473de574b2586235" +dependencies = [ + "console", + "number_prefix", + "portable-atomic", + "web-time", +] + [[package]] name = "is_terminal_polyfill" version = "1.70.1" @@ -527,6 +545,16 @@ dependencies = [ "syn", ] +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.172" @@ -594,6 +622,12 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "number_prefix" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" + [[package]] name = "once_cell" version = "1.21.3" @@ -818,6 +852,7 @@ dependencies = [ "dialoguer", "dirs-next", "env_logger", + "indicatif", "log", "serde", "serde_json", @@ -1023,6 +1058,73 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "webpki-roots" version = "0.26.11" diff --git a/Cargo.toml b/Cargo.toml index 9273a19..0c53fe1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ ctrlc = { version = "3.4.7", features = ["termination"] } dialoguer = { version = "0.11.0", default-features = false } dirs-next = "2.0.0" env_logger = "0.11.8" +indicatif = { version = "0.17.11", default-features = false } log = "0.4.27" serde = { version = "1.0.219", features = ["derive"] } serde_json = "1.0.140" diff --git a/src/appstate.rs b/src/appstate.rs index fe39f80..bbe762e 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -5,6 +5,8 @@ use std::{ path::{Path, PathBuf}, }; +use console::style; +use indicatif::{ProgressBar, ProgressStyle}; use log::{debug, trace}; use serde::{Deserialize, Serialize}; @@ -17,6 +19,8 @@ use super::{ pub struct AppState { #[serde(skip)] file_name: PathBuf, + #[serde(skip)] + progress: Option, alias: Alias, share: Share, @@ -29,6 +33,15 @@ enum FileState { U(FileUploading), } +impl FileState { + fn file_name(&self) -> &str { + match self { + FileState::C(checked) => checked.file_name(), + FileState::U(uploading) => uploading.file_name(), + } + } +} + impl AppState { fn cache_dir() -> PathBuf { let dir_name = dirs_next::cache_dir() @@ -61,6 +74,7 @@ impl AppState { Self { file_name, + progress: None, alias: state.alias, share: state.share, files: state.files, @@ -79,12 +93,17 @@ impl AppState { Ok(Self { file_name, + progress: None, alias, share, files, }) } + pub fn file_names(&self) -> Vec<&str> { + self.files.iter().map(FileState::file_name).collect() + } + pub fn upload_chunk( &mut self, http: &ureq::Agent, @@ -100,8 +119,34 @@ impl AppState { debug!("{uploading} chunk {chunk_size}"); + let pb = match self.progress { + Some(ref pb) => pb, + None => { + self.progress = Some({ + let pb = { + let ps = ProgressStyle::with_template(&format!( + "{{msg:.yellow}}: {{bar:50.cyan/blue}} {{binary_bytes:.magenta}}{}{{binary_total_bytes:.magenta}} ({{elapsed}})", + style("/").magenta(), + )) + .unwrap(); + + ProgressBar::new(uploading.size()) + .with_style(ps) + .with_message(uploading.file_name().to_owned()) + .with_position(uploading.offset()) + }; + pb.tick(); + + pb + }); + self.progress.as_ref().unwrap() + } + }; + pb.tick(); + match uploading.upload_chunk(http, &self.alias, chunk_size) { ChunkState::Ok(upl) => { + pb.set_position(upl.offset()); self.files.push_front(FileState::U(upl)); Ok(Some(())) } @@ -111,6 +156,8 @@ impl AppState { } ChunkState::Finished(path) => { debug!("Finished {:?}!", path.display()); + pb.finish(); + self.progress = None; self.share.notify(http, &self.alias).unwrap(); Ok(self.files.front().map(drop)) diff --git a/src/main.rs b/src/main.rs index d8b379b..7c36d60 100644 --- a/src/main.rs +++ b/src/main.rs @@ -20,6 +20,12 @@ use appstate::AppState; use cli::Cli; fn main() { + println!( + "{} to {}!", + style("Welcome").magenta().bold(), + style("ShrUpl").yellow().bold(), + ); + let stop = Arc::new(AtomicBool::new(false)); let stop_ctrlc = stop.clone(); @@ -43,7 +49,7 @@ fn main() { let mut state = AppState::try_resume(&args) .and_then(|state| { Confirm::with_theme(&ColorfulTheme::default()) - .with_prompt("Previous operation found. Continue?") + .with_prompt("Continue previously stopped operation?") .default(true) .interact() .map_or(None, |b| b.then_some(state)) @@ -62,15 +68,16 @@ fn main() { ureq::Error::Io(_) => Some("URL"), _ => None, } { + info!("handling error: {e:?}"); println!( "{} probably wrong: {} – {:?}", - style("Error!").red(), - style(cause).cyan(), + style("Error!").red().bold(), + style(cause).cyan().italic(), style(e.to_string()).yellow() ); } else { - error!("unknown error: {e}"); - println!("{}", style("Unknown Error!").red()); + error!("unknown error: {e} – {e:?}"); + println!("{}", style("Unknown Error!").red().bold()); } exit(1); @@ -78,6 +85,12 @@ fn main() { } }); + println!( + "{} uploading: {}", + style("ShrUpl").yellow().bold(), + style(state.file_names().join(", ")).magenta(), + ); + info!("continuing with state: {state:?}"); loop { diff --git a/src/sharry/file/checked.rs b/src/sharry/file/checked.rs index 166e940..db9542c 100644 --- a/src/sharry/file/checked.rs +++ b/src/sharry/file/checked.rs @@ -31,6 +31,10 @@ impl FileChecked { } } + pub fn file_name(&self) -> &str { + self.path.file_name().unwrap().to_str().unwrap() + } + pub fn start_upload( self, http: &ureq::Agent, diff --git a/src/sharry/file/uploading.rs b/src/sharry/file/uploading.rs index eb82d2d..297196a 100644 --- a/src/sharry/file/uploading.rs +++ b/src/sharry/file/uploading.rs @@ -1,5 +1,5 @@ use std::{ - fmt::Display, + fmt::{Debug, Display}, fs::File, io::{self, Read, Seek, SeekFrom}, path::PathBuf, @@ -7,10 +7,7 @@ use std::{ use log::debug; use serde::{Deserialize, Serialize}; -use ureq::{ - Error::Other, - http::{HeaderValue, StatusCode}, -}; +use ureq::http::{HeaderValue, StatusCode}; use super::{Alias, SharryAlias}; @@ -71,6 +68,26 @@ impl FileUploading { Ok(bytes) } + pub fn file_name(&self) -> &str { + self.path.file_name().unwrap().to_str().unwrap() + } + + pub fn offset(&self) -> T + where + T: TryFrom, + >::Error: Debug, + { + self.offset.try_into().unwrap() + } + + pub fn size(&self) -> T + where + T: TryFrom, + >::Error: Debug, + { + self.size.try_into().unwrap() + } + pub fn upload_chunk( mut self, http: &ureq::Agent, diff --git a/src/sharry/mod.rs b/src/sharry/mod.rs index 98d7643..6deb57e 100644 --- a/src/sharry/mod.rs +++ b/src/sharry/mod.rs @@ -7,5 +7,5 @@ mod share; pub use alias::Alias; pub use api::{NewShareRequest, Uri}; -pub use file::{ChunkState, FileChecked, FileUploading}; +pub use file::{ChunkState, FileChecked, FileUploading, UploadError}; pub use share::Share;