2025-06-24 01:35:21 +00:00
|
|
|
use std::{fmt, io, time::Duration};
|
2025-06-02 23:57:17 +00:00
|
|
|
|
2025-06-24 21:58:18 +00:00
|
|
|
use indicatif::{ProgressBar, ProgressDrawTarget};
|
2025-06-12 23:28:42 +00:00
|
|
|
use log::{debug, warn};
|
2025-06-02 23:57:17 +00:00
|
|
|
|
2025-06-18 13:04:04 +00:00
|
|
|
use crate::{
|
2025-06-19 10:34:28 +00:00
|
|
|
cachefile::CacheFile,
|
2025-06-02 23:57:17 +00:00
|
|
|
cli::Cli,
|
2025-06-24 00:11:24 +00:00
|
|
|
file::{Chunk, FileTrait},
|
2025-06-24 21:58:18 +00:00
|
|
|
output::new_progressbar,
|
2025-06-13 17:03:25 +00:00
|
|
|
sharry::{self, Client},
|
2025-06-02 23:57:17 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct AppState {
|
2025-06-24 01:35:21 +00:00
|
|
|
progress: Option<ProgressBar>,
|
2025-06-12 23:01:16 +00:00
|
|
|
http: ureq::Agent,
|
2025-06-12 16:07:54 +00:00
|
|
|
inner: CacheFile,
|
2025-06-04 13:25:00 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-11 00:28:02 +00:00
|
|
|
impl fmt::Debug for AppState {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
f.debug_struct("AppState")
|
2025-06-12 09:26:11 +00:00
|
|
|
.field("inner", &self.inner)
|
2025-06-12 16:25:15 +00:00
|
|
|
.finish_non_exhaustive()
|
2025-06-11 00:28:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-12 23:01:16 +00:00
|
|
|
fn new_http(timeout: Option<Duration>) -> ureq::Agent {
|
|
|
|
|
ureq::Agent::config_builder()
|
|
|
|
|
.timeout_global(timeout)
|
|
|
|
|
.build()
|
|
|
|
|
.into()
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-02 23:57:17 +00:00
|
|
|
impl AppState {
|
2025-06-13 22:27:15 +00:00
|
|
|
fn new(http: ureq::Agent, inner: CacheFile) -> Self {
|
2025-06-11 00:28:02 +00:00
|
|
|
Self {
|
2025-06-24 01:35:21 +00:00
|
|
|
progress: None,
|
2025-06-12 23:01:16 +00:00
|
|
|
http,
|
2025-06-12 09:26:11 +00:00
|
|
|
inner,
|
2025-06-11 00:28:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-04 21:02:35 +00:00
|
|
|
pub fn try_resume(args: &Cli) -> Option<Self> {
|
2025-06-12 16:07:54 +00:00
|
|
|
let inner = CacheFile::try_resume(args)
|
|
|
|
|
.inspect_err(|e| debug!("could not resume from hash {:?}: {e}", args.get_hash()))
|
2025-06-12 09:26:11 +00:00
|
|
|
.ok()?;
|
|
|
|
|
|
2025-06-24 21:58:18 +00:00
|
|
|
inner.peek_uploading();
|
|
|
|
|
|
2025-06-13 22:27:15 +00:00
|
|
|
Some(Self::new(new_http(args.get_timeout()), inner))
|
2025-06-04 21:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-12 23:01:16 +00:00
|
|
|
pub fn from_args(args: &Cli) -> sharry::Result<Self> {
|
|
|
|
|
let http = new_http(args.get_timeout());
|
|
|
|
|
|
2025-06-18 13:09:34 +00:00
|
|
|
let share_id = http.share_create(&args.get_uri(), &args.alias, args.get_share_request())?;
|
2025-06-04 21:02:35 +00:00
|
|
|
|
2025-06-24 21:58:18 +00:00
|
|
|
Ok(Self::new(http, CacheFile::from_args(args, share_id)?))
|
2025-06-04 21:02:35 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-24 01:35:21 +00:00
|
|
|
fn with_progressbar(&mut self, f: impl FnOnce(&ProgressBar), drop_bar: bool) {
|
2025-06-24 19:34:11 +00:00
|
|
|
let bar = &*self.progress.get_or_insert_with(new_progressbar);
|
2025-06-24 01:11:11 +00:00
|
|
|
|
|
|
|
|
if let Some(upl) = self.inner.peek_uploading() {
|
|
|
|
|
if bar.length().is_none() {
|
2025-06-24 01:53:27 +00:00
|
|
|
bar.set_draw_target(ProgressDrawTarget::stderr());
|
2025-06-24 01:11:11 +00:00
|
|
|
bar.set_length(upl.get_size());
|
|
|
|
|
bar.set_message(upl.get_name().to_owned());
|
|
|
|
|
bar.enable_steady_tick(Duration::from_millis(100));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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.inc_length(0);
|
2025-06-12 22:26:48 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-24 00:11:24 +00:00
|
|
|
f(bar);
|
|
|
|
|
|
|
|
|
|
if drop_bar {
|
2025-06-24 01:35:21 +00:00
|
|
|
self.progress = None;
|
2025-06-12 22:26:48 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-24 01:35:21 +00:00
|
|
|
fn touch_progressbar(&mut self) {
|
|
|
|
|
self.with_progressbar(|_| (), false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fn drop_progressbar(&mut self, f: impl FnOnce(&ProgressBar)) {
|
|
|
|
|
self.with_progressbar(f, true);
|
2025-06-24 00:11:24 +00:00
|
|
|
}
|
2025-06-12 22:26:48 +00:00
|
|
|
|
2025-06-24 00:11:24 +00:00
|
|
|
fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> sharry::Result<Option<Chunk<'t>>> {
|
|
|
|
|
if self.inner.get_uploading(&self.http)?.is_none() {
|
2025-06-13 17:03:25 +00:00
|
|
|
return Ok(None);
|
2025-06-24 00:11:24 +00:00
|
|
|
}
|
2025-06-12 22:26:48 +00:00
|
|
|
|
2025-06-24 00:11:24 +00:00
|
|
|
self.touch_progressbar();
|
2025-06-05 17:37:35 +00:00
|
|
|
|
2025-06-24 02:05:27 +00:00
|
|
|
let uploading = self.inner.expect_uploading();
|
2025-06-24 00:11:24 +00:00
|
|
|
debug!("{uploading:?}");
|
2025-06-10 23:39:08 +00:00
|
|
|
|
2025-06-24 00:11:24 +00:00
|
|
|
let chunk = uploading.read(buffer)?;
|
2025-06-13 16:02:37 +00:00
|
|
|
debug!("{chunk:?}");
|
|
|
|
|
|
2025-06-13 17:03:25 +00:00
|
|
|
Ok(Some(chunk))
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-13 22:27:15 +00:00
|
|
|
pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> sharry::Result<bool> {
|
|
|
|
|
let Some(chunk) = self.next_chunk(buffer)? else {
|
2025-06-24 00:11:24 +00:00
|
|
|
self.inner
|
|
|
|
|
.share_notify(&self.http)
|
|
|
|
|
.unwrap_or_else(|e| warn!("Failed to notify the share: {e}"));
|
|
|
|
|
|
2025-06-13 17:03:25 +00:00
|
|
|
return Ok(true);
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-18 13:09:34 +00:00
|
|
|
self.inner.file_patch(&self.http, &chunk)?;
|
2025-06-13 17:03:25 +00:00
|
|
|
|
2025-06-24 01:53:27 +00:00
|
|
|
self.touch_progressbar();
|
|
|
|
|
|
|
|
|
|
if let Some(path) = self.inner.check_eof() {
|
|
|
|
|
debug!("Finished {:?}!", path.display());
|
|
|
|
|
self.drop_progressbar(ProgressBar::finish);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Ok(self.inner.peek_uploading().is_none() && self.inner.queue_empty())
|
2025-06-05 01:13:54 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-18 23:40:27 +00:00
|
|
|
pub fn rewind_chunk(mut self) -> Option<Self> {
|
2025-06-24 00:11:24 +00:00
|
|
|
self.inner = self.inner.rewind_chunk()?;
|
2025-06-13 23:00:36 +00:00
|
|
|
|
|
|
|
|
Some(self)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-24 00:11:24 +00:00
|
|
|
pub fn abort_upload(&mut self) {
|
|
|
|
|
self.inner.abort_upload();
|
2025-06-24 01:35:21 +00:00
|
|
|
self.drop_progressbar(ProgressBar::abandon);
|
2025-06-18 19:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-18 23:40:27 +00:00
|
|
|
pub fn rebuild_share(self, args: &Cli) -> sharry::Result<Self> {
|
|
|
|
|
let share_id =
|
|
|
|
|
self.http
|
|
|
|
|
.share_create(&args.get_uri(), &args.alias, args.get_share_request())?;
|
|
|
|
|
|
2025-06-24 21:58:18 +00:00
|
|
|
Ok(Self::new(self.http, CacheFile::from_args(args, share_id)?))
|
2025-06-18 23:40:27 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-04 21:02:35 +00:00
|
|
|
pub fn save(&self) -> io::Result<()> {
|
2025-06-12 09:26:11 +00:00
|
|
|
self.inner.save()
|
2025-06-02 23:57:17 +00:00
|
|
|
}
|
|
|
|
|
|
2025-06-04 21:23:21 +00:00
|
|
|
pub fn clear(self) -> io::Result<()> {
|
2025-06-12 09:26:11 +00:00
|
|
|
self.inner.clear()
|
2025-06-02 23:57:17 +00:00
|
|
|
}
|
|
|
|
|
}
|