Compare commits

...

2 commits

Author SHA1 Message Date
dc7eeaddfe CLI using indicatif 2025-06-05 17:37:35 +00:00
3bf5d96e04 earlier check for stop signal 2025-06-05 12:58:09 +00:00
7 changed files with 218 additions and 30 deletions

102
Cargo.lock generated
View file

@ -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"

View file

@ -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"

View file

@ -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<ProgressBar>,
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))

View file

@ -20,12 +20,18 @@ 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();
ctrlc::set_handler(move || {
stop_ctrlc.store(true, Ordering::SeqCst);
info!("stopping after chunk ...");
info!("stopping as soon as possible ...");
})
.expect("Error setting Ctrl-C handler");
@ -43,37 +49,48 @@ 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))
})
.unwrap_or_else(|| match AppState::from_args(&args, &agent) {
Ok(state) => {
state.save().unwrap();
state
}
Err(e) => {
if let Some(cause) = match e {
ureq::Error::StatusCode(403) => Some("Alias ID"),
ureq::Error::Io(_) => Some("URL"),
_ => None,
} {
println!(
"{} probably wrong: {} {:?}",
style("Error!").red(),
style(cause).cyan(),
style(e.to_string()).yellow()
);
} else {
error!("unknown error: {e}");
println!("{}", style("Unknown Error!").red());
}
.unwrap_or_else(|| {
stop.load(Ordering::SeqCst).then(|| exit(0));
exit(1);
match AppState::from_args(&args, &agent) {
Ok(state) => {
state.save().unwrap();
state
}
Err(e) => {
if let Some(cause) = match e {
ureq::Error::StatusCode(403) => Some("Alias ID"),
ureq::Error::Io(_) => Some("URL"),
_ => None,
} {
info!("handling error: {e:?}");
println!(
"{} probably wrong: {} {:?}",
style("Error!").red().bold(),
style(cause).cyan().italic(),
style(e.to_string()).yellow()
);
} else {
error!("unknown error: {e} {e:?}");
println!("{}", style("Unknown Error!").red().bold());
}
exit(1);
}
}
});
println!(
"{} uploading: {}",
style("ShrUpl").yellow().bold(),
style(state.file_names().join(", ")).magenta(),
);
info!("continuing with state: {state:?}");
loop {

View file

@ -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,

View file

@ -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<T>(&self) -> T
where
T: TryFrom<usize>,
<T as TryFrom<usize>>::Error: Debug,
{
self.offset.try_into().unwrap()
}
pub fn size<T>(&self) -> T
where
T: TryFrom<usize>,
<T as TryFrom<usize>>::Error: Debug,
{
self.size.try_into().unwrap()
}
pub fn upload_chunk(
mut self,
http: &ureq::Agent,

View file

@ -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;