mod appstate; mod cachefile; mod cli; mod file; mod impl_ureq; mod output; mod sharry; use std::{ process, sync::{ Arc, atomic::{AtomicBool, Ordering}, }, }; use clap::Parser; use console::style; use log::{info, trace}; use appstate::AppState; use cli::Cli; use output::{Log, SHRUPL}; use sharry::{ClientError, Parameter}; fn main() { let check_ctrlc = { let stop = Arc::new(AtomicBool::new(false)); let stop_ctrlc = stop.clone(); ctrlc::set_handler(move || { stop_ctrlc.store(true, Ordering::SeqCst); info!("stopping as soon as possible ..."); }) .expect("Error setting Ctrl-C handler"); move || { if stop.load(Ordering::SeqCst) { process::exit(255); } } }; let args = Cli::parse(); env_logger::Builder::new() .filter_module("shrupl", args.get_level_filter()) .parse_default_env() .init(); info!("args: {args:#?}"); println!("{} to {}!", style("Welcome").magenta().bold(), *SHRUPL); let mut state = AppState::try_resume(&args) .and_then(|state| output::prompt_continue().then_some(state)) .unwrap_or_else(|| { check_ctrlc(); match AppState::from_args(&args) { Ok(state) => { state.save().unwrap_or_else(|e| { Log::warning(format_args!("Failed to save state: {e}")); }); state } Err(e) => { Log::handle(&e); Log::error(format_args!("Failed to create state: {e}")); } } }); info!("continuing with state: {state:#?}"); let fns_magenta = output::style_all(&args.file_names(), |s| style(s).magenta()).join(", "); println!("{} is uploading: {fns_magenta}", *SHRUPL); let mut buffer = vec![0; args.chunk_size * 1024 * 1024]; let mut tries = 0; loop { if !args.may_retry(tries) { Log::error("Retry limit reached!"); } match state.upload_chunk(&mut buffer) { Err(e) => { Log::handle(&e); tries += 1; if let ClientError::InvalidParameter(p) = e { match p { // TODO Error 404: File not found Parameter::FileID(fid) => { // requeue file let Some(s) = state.requeue_file() else { Log::error("Failed to requeue file!"); }; trace!("File {fid:?} requeued (tried: {tries})"); state = s; } // TODO Error 404: Share might have been deleted Parameter::ShareID(sid) => { Log::error(format_args!("404 sid: {sid}")); } p => Log::error(format_args!("Unexpected {p}!")), } } else { // retry chunk let Some(s) = state.rewind() else { Log::error("Failed to retry chunk!"); }; trace!("State rewound, retrying last chunk (tried: {tries})"); state = s; } } Ok(false) => { trace!("chunk uploaded"); tries = 0; } Ok(true) => { info!("all uploads done"); break; } } state.save().unwrap_or_else(|e| { Log::warning(format_args!("Failed to save state: {e}")); }); check_ctrlc(); } state.clear().unwrap_or_else(|e| { Log::warning(format_args!("Failed to remove state: {e}")); }); println!("{} finished {}", *SHRUPL, style("successfully!").green()); }