From 393feec12541692e84521e59c37a3185f6776b68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= <40151420+ldericher@users.noreply.github.com> Date: Tue, 24 Jun 2025 00:11:24 +0000 Subject: [PATCH] [wip] implement handling "missing share" - rework `CacheFile` API - fix `AppState` using new `CacheFile` - fix `main` using new `AppState` - logging enhancement --- src/appstate.rs | 112 +++++++++++++++++++---------------------------- src/cachefile.rs | 30 ++++++++----- src/impl_ureq.rs | 2 + src/main.rs | 37 +++++++--------- 4 files changed, 81 insertions(+), 100 deletions(-) diff --git a/src/appstate.rs b/src/appstate.rs index 29edeb9..2617861 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -1,8 +1,4 @@ -use std::{ - cell::{Ref, RefCell}, - fmt, io, - time::Duration, -}; +use std::{cell::RefCell, fmt, io, time::Duration}; use console::style; use indicatif::{ProgressBar, ProgressStyle}; @@ -11,7 +7,7 @@ use log::{debug, warn}; use crate::{ cachefile::CacheFile, cli::Cli, - file::{self, Chunk, FileTrait}, + file::{Chunk, FileTrait}, sharry::{self, Client}, }; @@ -61,10 +57,14 @@ impl AppState { Ok(Self::new(http, CacheFile::from_args(args, share_id))) } - fn get_or_create_progressbar(&self, uploading: &file::Uploading) -> Ref<'_, ProgressBar> { + fn with_progressbar(&self, drop_bar: bool, f: impl FnOnce(&ProgressBar)) { + let Some(upl) = self.inner.peek_uploading() else { + return; + }; + let mut slot = self.progress.borrow_mut(); if slot.is_none() { - let bar = ProgressBar::new(uploading.get_size()) + let bar = ProgressBar::no_length() .with_style( ProgressStyle::with_template(&format!( concat!( @@ -76,45 +76,45 @@ impl AppState { )) .expect("style template is not valid"), ) - .with_position(uploading.get_offset()) - .with_message(uploading.get_name().to_owned()); + .with_message(upl.get_name().to_owned()); bar.enable_steady_tick(Duration::from_millis(100)); *slot = Some(bar); } - drop(slot); - Ref::map(self.progress.borrow(), |opt| { - opt.as_ref().expect("somehow the slot holds None") - }) - } + let bar = slot.as_ref().expect("somehow the slot holds None"); - fn drop_progressbar(&self, f: impl FnOnce(&ProgressBar)) { - let mut slot = self.progress.borrow_mut(); - if let Some(bar) = slot.as_ref() { - f(bar); + bar.set_position(upl.get_offset()); + // BUG in `indicatif` crate? + // `set_position` does not force an immediate redraw, so we also call `inc_length` here + bar.set_length(upl.get_size()); + // bar.inc_length(0); + + f(bar); + + if drop_bar { *slot = None; } } - fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> sharry::Result>> { - let Some(mut uploading) = self.inner.pop_uploading(&self.http)? else { - self.inner - .share_notify(&self.http) - .unwrap_or_else(|e| warn!("Failed to notify the share: {e}")); + fn touch_progressbar(&self) { + self.with_progressbar(false, |_| ()); + } + fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> sharry::Result>> { + if self.inner.get_uploading(&self.http)?.is_none() { return Ok(None); - }; + } + + self.touch_progressbar(); + + let uploading = self + .inner + .get_uploading(&self.http)? + .expect("we just checked!"); debug!("{uploading:?}"); - self.get_or_create_progressbar(&uploading); - - let chunk = { - let result = uploading.read(buffer); - self.inner.push_uploading(uploading); - - result? - }; + let chunk = uploading.read(buffer)?; debug!("{chunk:?}"); Ok(Some(chunk)) @@ -123,13 +123,9 @@ impl AppState { fn is_done(&mut self) -> bool { if let Some(path) = self.inner.check_eof() { debug!("Finished {:?}!", path.display()); - self.drop_progressbar(ProgressBar::finish); - } else if let Some(uploading) = self.inner.peek_uploading() { - let bar = self.get_or_create_progressbar(uploading); - bar.set_position(uploading.get_offset()); - // BUG in `indicatif` crate? - // `set_position` does not force an immediate redraw, so we also call `inc_length` here - bar.inc_length(0); + self.with_progressbar(true, ProgressBar::finish); + } else if self.inner.peek_uploading().is_some() { + self.touch_progressbar(); return false; } @@ -139,6 +135,10 @@ impl AppState { pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> sharry::Result { let Some(chunk) = self.next_chunk(buffer)? else { + self.inner + .share_notify(&self.http) + .unwrap_or_else(|e| warn!("Failed to notify the share: {e}")); + return Ok(true); }; @@ -148,38 +148,14 @@ impl AppState { } pub fn rewind_chunk(mut self) -> Option { - let uploading = if let Some(state) = self.inner.pop_file() { - match state { - FileState::U(s) => s, - FileState::C(s) => {} - } - } else { - warn!("rewind_chunk called on empty queue"); - return None; - }; - - let Some(FileState::U(uploading)) = self.inner.pop_file() else { - warn!("rewind_chunk called in invalid state"); - return None; - }; - - self.inner.push_file(FileState::U(uploading.rewind()?)); + self.inner = self.inner.rewind_chunk()?; Some(self) } - pub fn requeue_file(mut self) -> Option { - let Some(uploading) = self.inner.pop_file(&self.http) else { - warn!("requeue_file called on empty queue"); - return None; - }; - - let checked = uploading.abort(); - self.inner.requeue_file(checked); - - self.drop_progressbar(|pb| pb.abandon()); - - Some(self) + pub fn abort_upload(&mut self) { + self.inner.abort_upload(); + self.with_progressbar(true, ProgressBar::abandon); } pub fn rebuild_share(self, args: &Cli) -> sharry::Result { diff --git a/src/cachefile.rs b/src/cachefile.rs index 12d3478..0478355 100644 --- a/src/cachefile.rs +++ b/src/cachefile.rs @@ -82,24 +82,22 @@ impl CacheFile { self.files.is_empty() } - pub fn pop_uploading( + pub fn get_uploading( &mut self, client: &impl Client, - ) -> sharry::Result> { - if let Some(upl) = self.uploading.take() { - Ok(Some(upl)) + ) -> sharry::Result> { + if self.uploading.is_some() { + Ok(self.uploading.as_mut()) } else if let Some(chk) = self.files.pop_front() { let upl = chk.start_upload(client, &self.uri, &self.alias_id, &self.share_id)?; - Ok(Some(upl)) + self.uploading.replace(upl); + + Ok(self.uploading.as_mut()) } else { Ok(None) } } - pub fn push_uploading(&mut self, uploading: file::Uploading) { - self.uploading.replace(uploading); - } - pub fn peek_uploading(&self) -> Option<&file::Uploading> { self.uploading.as_ref() } @@ -107,7 +105,7 @@ impl CacheFile { pub fn check_eof(&mut self) -> Option { if let Some(upl) = self.uploading.take() { match upl.check_eof() { - Ok(upl) => self.push_uploading(upl), + Ok(upl) => self.uploading = Some(upl), Err(p) => return Some(p), } } @@ -115,9 +113,19 @@ impl CacheFile { None } + pub fn rewind_chunk(mut self) -> Option { + let Some(upl) = self.uploading.take() else { + panic!("rewind_chunk called while not uploading"); + }; + + self.uploading = Some(upl.rewind()?); + + Some(self) + } + pub fn abort_upload(&mut self) { let Some(upl) = self.uploading.take() else { - panic!("abort called while not uploading"); + panic!("abort_upload called while not uploading"); }; self.files.push_front(upl.abort()); diff --git a/src/impl_ureq.rs b/src/impl_ureq.rs index 87d4c12..d8c50fd 100644 --- a/src/impl_ureq.rs +++ b/src/impl_ureq.rs @@ -72,6 +72,8 @@ impl sharry::Client for ureq::Agent { debug!("{res:?}"); if res.success && (res.message == "Share created.") { + trace!("new share id: {:?}", res.id); + Ok(res.id) } else { Err(ClientError::response(format!("{res:?}"))) diff --git a/src/main.rs b/src/main.rs index 9d3ef63..cd711d6 100644 --- a/src/main.rs +++ b/src/main.rs @@ -89,40 +89,35 @@ fn main() { Err(e) => { Log::handle(&e); - match e { - ClientError::InvalidParameter(p) => match p { + 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!("requeueing file {fid:?}"); - trace!("File {fid:?} requeued"); - state = s; + state.abort_upload(); } // TODO Error 404: Share not found Parameter::ShareID(sid) => { - // requeue file + trace!("rebuilding share {sid:?}"); + + // rebuild share let Ok(s) = state.rebuild_share(&args) else { Log::error("Failed to rebuild share!"); }; - - trace!("Share {sid:?} rebuilt"); state = s; } p => Log::error(format_args!("Unexpected {p}!")), - }, - _ => { - // retry chunk - let Some(s) = state.rewind_chunk() else { - Log::error("Failed to retry chunk!"); - }; - tries += 1; - - trace!("State rewound, retrying last chunk (tries: {tries})"); - state = s; } + } else { + // retry chunk + let Some(s) = state.rewind_chunk() else { + Log::error("Failed to retry chunk!"); + }; + tries += 1; + + trace!("State rewound, retrying last chunk (tries: {tries})"); + state = s; } } Ok(false) => {