2025-06-02 23:57:17 +00:00
|
|
|
mod appstate;
|
2025-06-12 16:07:54 +00:00
|
|
|
mod cachefile;
|
2025-05-28 00:07:59 +00:00
|
|
|
mod cli;
|
2025-06-10 18:20:52 +00:00
|
|
|
mod file;
|
2025-06-18 13:09:34 +00:00
|
|
|
mod impl_ureq;
|
2025-05-22 17:34:44 +00:00
|
|
|
mod sharry;
|
|
|
|
|
|
2025-06-05 01:44:39 +00:00
|
|
|
use std::{
|
2025-06-12 09:26:11 +00:00
|
|
|
process,
|
2025-06-05 01:44:39 +00:00
|
|
|
sync::{
|
|
|
|
|
Arc,
|
|
|
|
|
atomic::{AtomicBool, Ordering},
|
|
|
|
|
},
|
2025-06-05 01:34:12 +00:00
|
|
|
};
|
|
|
|
|
|
2025-05-27 14:01:09 +00:00
|
|
|
use clap::Parser;
|
2025-06-05 12:09:47 +00:00
|
|
|
use console::style;
|
2025-06-13 23:50:42 +00:00
|
|
|
use dialoguer::{Select, theme::ColorfulTheme};
|
2025-06-13 23:11:40 +00:00
|
|
|
use log::{error, info, trace};
|
2025-05-22 17:34:44 +00:00
|
|
|
|
2025-06-02 23:57:17 +00:00
|
|
|
use appstate::AppState;
|
2025-05-28 00:07:59 +00:00
|
|
|
use cli::Cli;
|
2025-06-08 21:31:50 +00:00
|
|
|
use sharry::ClientError;
|
2025-05-27 14:01:09 +00:00
|
|
|
|
2025-06-13 23:50:42 +00:00
|
|
|
fn prompt_continue() -> bool {
|
|
|
|
|
let prompt = format!(
|
|
|
|
|
"This operation has previously been stopped. {}",
|
|
|
|
|
style("How to proceed?").cyan()
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
let choices = [
|
|
|
|
|
format!("Load and {}", style("continue operation").green().bold()),
|
|
|
|
|
format!("Start a {}", style("new operation").cyan().bold()),
|
|
|
|
|
format!("Quit {}", style("ShrUpl").yellow().bold()),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
let selection = Select::with_theme(&ColorfulTheme::default())
|
|
|
|
|
.with_prompt(prompt)
|
|
|
|
|
.default(0)
|
|
|
|
|
.items(&choices)
|
|
|
|
|
.interact()
|
|
|
|
|
.unwrap_or(2);
|
|
|
|
|
|
|
|
|
|
if selection == 2 {
|
|
|
|
|
process::exit(255);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
selection == 0
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-18 13:09:34 +00:00
|
|
|
fn handle_error(e: &ClientError) {
|
|
|
|
|
if e.is_fatal() {
|
|
|
|
|
// react to fatal error
|
|
|
|
|
error!("fatal error: {e:?}");
|
|
|
|
|
eprintln!(
|
|
|
|
|
"{} {}",
|
2025-06-12 00:54:26 +00:00
|
|
|
style("Error!").red().bold(),
|
2025-06-18 13:09:34 +00:00
|
|
|
style(e.to_string()).cyan().italic(),
|
2025-06-12 00:54:26 +00:00
|
|
|
);
|
2025-06-18 13:09:34 +00:00
|
|
|
process::exit(1);
|
2025-06-12 00:54:26 +00:00
|
|
|
}
|
2025-06-18 13:09:34 +00:00
|
|
|
|
|
|
|
|
// handle recoverable error
|
|
|
|
|
info!("recoverable error: {e:?}");
|
2025-06-12 00:54:26 +00:00
|
|
|
}
|
|
|
|
|
|
2025-05-17 23:57:52 +00:00
|
|
|
fn main() {
|
2025-06-11 18:17:16 +00:00
|
|
|
env_logger::init();
|
|
|
|
|
|
|
|
|
|
let check_ctrlc = {
|
|
|
|
|
let stop = Arc::new(AtomicBool::new(false));
|
|
|
|
|
let stop_ctrlc = stop.clone();
|
2025-06-12 00:54:26 +00:00
|
|
|
|
2025-06-11 18:17:16 +00:00
|
|
|
ctrlc::set_handler(move || {
|
|
|
|
|
stop_ctrlc.store(true, Ordering::SeqCst);
|
|
|
|
|
info!("stopping as soon as possible ...");
|
|
|
|
|
})
|
|
|
|
|
.expect("Error setting Ctrl-C handler");
|
2025-06-05 01:34:12 +00:00
|
|
|
|
2025-06-11 18:17:16 +00:00
|
|
|
move || {
|
|
|
|
|
if stop.load(Ordering::SeqCst) {
|
2025-06-12 09:26:11 +00:00
|
|
|
process::exit(255);
|
2025-06-11 18:17:16 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
2025-05-22 17:34:44 +00:00
|
|
|
|
2025-05-28 00:07:59 +00:00
|
|
|
let args = Cli::parse();
|
2025-06-15 00:44:28 +00:00
|
|
|
info!("args: {args:#?}");
|
2025-05-22 17:34:44 +00:00
|
|
|
|
2025-06-15 00:56:58 +00:00
|
|
|
println!(
|
|
|
|
|
"{} to {}!",
|
|
|
|
|
style("Welcome").magenta().bold(),
|
|
|
|
|
style("ShrUpl").yellow().bold(),
|
|
|
|
|
);
|
|
|
|
|
|
2025-06-05 11:20:27 +00:00
|
|
|
let mut state = AppState::try_resume(&args)
|
2025-06-13 23:50:42 +00:00
|
|
|
.and_then(|state| prompt_continue().then_some(state))
|
2025-06-05 12:58:09 +00:00
|
|
|
.unwrap_or_else(|| {
|
2025-06-11 18:17:16 +00:00
|
|
|
check_ctrlc();
|
2025-06-05 12:58:09 +00:00
|
|
|
|
2025-06-12 23:01:16 +00:00
|
|
|
match AppState::from_args(&args) {
|
2025-06-05 12:58:09 +00:00
|
|
|
Ok(state) => {
|
2025-06-12 23:28:42 +00:00
|
|
|
state.save().unwrap_or_else(|e| {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"{} Failed to save {} state: {e}",
|
|
|
|
|
style("Warning:").red().bold(),
|
|
|
|
|
style("ShrUpl").yellow().bold(),
|
2025-06-13 16:02:37 +00:00
|
|
|
);
|
2025-06-12 23:28:42 +00:00
|
|
|
});
|
2025-06-05 12:58:09 +00:00
|
|
|
state
|
2025-06-05 12:09:47 +00:00
|
|
|
}
|
2025-06-05 12:58:09 +00:00
|
|
|
Err(e) => {
|
2025-06-18 13:09:34 +00:00
|
|
|
handle_error(&e);
|
2025-06-12 09:26:11 +00:00
|
|
|
process::exit(1);
|
2025-06-05 12:58:09 +00:00
|
|
|
}
|
2025-06-05 11:20:27 +00:00
|
|
|
}
|
|
|
|
|
});
|
2025-06-04 21:02:35 +00:00
|
|
|
|
2025-06-15 00:44:28 +00:00
|
|
|
info!("continuing with state: {state:#?}");
|
2025-06-12 00:54:26 +00:00
|
|
|
|
2025-06-13 23:50:42 +00:00
|
|
|
let fns_magenta = state
|
|
|
|
|
.file_names()
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|&n| style(n).magenta().to_string())
|
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
|
.join(", ");
|
|
|
|
|
|
2025-06-05 17:37:35 +00:00
|
|
|
println!(
|
2025-06-13 23:50:42 +00:00
|
|
|
"{} is uploading: {fns_magenta}",
|
2025-06-05 17:37:35 +00:00
|
|
|
style("ShrUpl").yellow().bold(),
|
|
|
|
|
);
|
|
|
|
|
|
2025-06-13 22:27:15 +00:00
|
|
|
let mut buffer = vec![0; args.chunk_size * 1024 * 1024];
|
|
|
|
|
|
2025-06-05 01:13:54 +00:00
|
|
|
loop {
|
2025-06-13 22:27:15 +00:00
|
|
|
match state.upload_chunk(&mut buffer) {
|
2025-06-13 23:00:36 +00:00
|
|
|
Err(e) => {
|
2025-06-13 23:50:42 +00:00
|
|
|
// TODO better error handling (this will just retry endlessly)
|
2025-06-14 00:07:43 +00:00
|
|
|
// Error 404: Share might have been deleted
|
2025-06-18 13:09:34 +00:00
|
|
|
handle_error(&e);
|
2025-06-13 23:00:36 +00:00
|
|
|
|
|
|
|
|
if let Some(s) = state.rewind() {
|
2025-06-18 13:09:34 +00:00
|
|
|
trace!("State rewound, retrying last chunk");
|
2025-06-13 23:00:36 +00:00
|
|
|
state = s;
|
|
|
|
|
} else {
|
|
|
|
|
eprintln!("{} Failed to retry chunk!", style("Error:").red().bold());
|
|
|
|
|
process::exit(1);
|
2025-06-13 23:11:40 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
Ok(false) => {
|
|
|
|
|
trace!("chunk uploaded");
|
2025-06-13 23:00:36 +00:00
|
|
|
}
|
2025-06-13 16:02:37 +00:00
|
|
|
Ok(true) => {
|
2025-06-05 11:20:27 +00:00
|
|
|
info!("all uploads done");
|
|
|
|
|
break;
|
|
|
|
|
}
|
2025-06-04 16:44:16 +00:00
|
|
|
}
|
2025-06-05 01:13:54 +00:00
|
|
|
|
2025-06-12 23:28:42 +00:00
|
|
|
state.save().unwrap_or_else(|e| {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"{} Failed to save {} state: {e}",
|
|
|
|
|
style("Warning:").red().bold(),
|
|
|
|
|
style("ShrUpl").yellow().bold(),
|
2025-06-13 16:02:37 +00:00
|
|
|
);
|
2025-06-12 23:28:42 +00:00
|
|
|
});
|
2025-06-11 18:17:16 +00:00
|
|
|
check_ctrlc();
|
2025-05-26 20:31:22 +00:00
|
|
|
}
|
2025-06-13 23:02:41 +00:00
|
|
|
|
|
|
|
|
state.clear().unwrap_or_else(|e| {
|
|
|
|
|
eprintln!(
|
|
|
|
|
"{} Failed to remove {} state: {e}",
|
|
|
|
|
style("Warning:").red().bold(),
|
|
|
|
|
style("ShrUpl").yellow().bold(),
|
|
|
|
|
);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
println!(
|
|
|
|
|
"{} finished {}",
|
|
|
|
|
style("ShrUpl").yellow().bold(),
|
|
|
|
|
style("successfully!").green()
|
|
|
|
|
);
|
2025-05-17 23:57:52 +00:00
|
|
|
}
|