use std::{fmt, io, time::Duration}; use console::style; use indicatif::{ProgressBar, ProgressStyle}; use log::debug; use super::{ cachefile::CacheFile, cli::Cli, file::FileTrait, sharry::{self, Client, ClientError}, }; pub struct AppState { progress: Option, buffer: Vec, inner: CacheFile, } impl fmt::Debug for AppState { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("AppState") .field("inner", &self.inner) .finish_non_exhaustive() } } impl AppState { fn new(chunk_size: usize, inner: CacheFile) -> Self { Self { progress: None, buffer: vec![0; chunk_size * 1024 * 1024], inner, } } pub fn try_resume(args: &Cli) -> Option { let inner = CacheFile::try_resume(args) .inspect_err(|e| debug!("could not resume from hash {:?}: {e}", args.get_hash())) .ok()?; Some(Self::new(args.chunk_size, inner)) } pub fn from_args(args: &Cli, http: &impl Client) -> sharry::Result { let share_id = http.share_create( &args.get_uri().endpoint("alias/upload/new"), &args.alias, args.get_share_request(), )?; Ok(Self::new( args.chunk_size, CacheFile::from_args(args, share_id), )) } pub fn upload_chunk(&mut self, http: &impl Client) -> sharry::Result> { let Some(mut uploading) = self.inner.pop_file(http) else { return Ok(None); }; debug!("{uploading} chunk {}", self.buffer.len()); // Initialize or fetch the existing ProgressBar let bar = &*self.progress.get_or_insert_with(|| { // Create a new bar with style let bar = ProgressBar::new(uploading.get_size()) .with_style( ProgressStyle::with_template(&format!( concat!( "{{msg:.yellow}}: {{bar:50.cyan/blue}} ", "{{binary_bytes:.magenta}}{}{{binary_total_bytes:.magenta}} ", "({{eta}})", ), style("/").magenta(), )) .unwrap(), ) .with_message(uploading.get_name().to_owned()) .with_position(uploading.get_offset()); bar.enable_steady_tick(Duration::from_millis(100)); bar }); let chunk = uploading .read(&mut self.buffer) .map_err(ClientError::from)?; if chunk.get_length() == 0 { return Err(ClientError::req_err("wtf")); } http.file_patch( chunk.get_patch_uri(), self.inner.alias_id(), chunk.get_offset(), chunk.get_data(), )?; match uploading.check_eof() { Ok(uploading) => { bar.set_position(uploading.get_offset()); self.inner.push_file(uploading); Ok(Some(())) } Err(path) => { debug!("Finished {:?}!", path.display()); bar.finish(); self.progress = None; self.inner.share_notify(http).unwrap(); // HACK unwrap Ok(self.inner.has_file().then_some(())) } } } pub fn file_names(&self) -> Vec<&str> { self.inner.file_names() } pub fn save(&self) -> io::Result<()> { self.inner.save() } pub fn clear(self) -> io::Result<()> { self.inner.clear() } }