shrupl/src/appstate.rs

172 lines
4.7 KiB
Rust
Raw Normal View History

use std::{
cell::{Ref, RefCell},
fmt, io,
time::Duration,
};
2025-06-02 23:57:17 +00:00
2025-06-05 17:37:35 +00:00
use console::style;
use indicatif::{ProgressBar, ProgressStyle};
2025-06-12 23:28:42 +00:00
use log::{debug, warn};
2025-06-02 23:57:17 +00:00
use super::{
cachefile::CacheFile,
2025-06-02 23:57:17 +00:00
cli::Cli,
file::{self, FileTrait},
sharry::{self, Client, ClientError},
2025-06-02 23:57:17 +00:00
};
pub struct AppState {
2025-06-12 23:43:03 +00:00
progress: RefCell<Option<ProgressBar>>,
buffer: Vec<u8>,
2025-06-04 21:02:35 +00:00
http: ureq::Agent,
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()
}
}
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 {
fn new(chunk_size: usize, http: ureq::Agent, inner: CacheFile) -> Self {
Self {
2025-06-12 23:43:03 +00:00
progress: None.into(),
buffer: vec![0; chunk_size * 1024 * 1024],
http,
inner,
}
}
2025-06-04 21:02:35 +00:00
pub fn try_resume(args: &Cli) -> Option<Self> {
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,
new_http(args.get_timeout()),
inner,
))
2025-06-04 21:02:35 +00:00
}
pub fn from_args(args: &Cli) -> sharry::Result<Self> {
let http = new_http(args.get_timeout());
let share_id = http.share_create(
&args.get_uri().endpoint("alias/upload/new"),
&args.alias,
args.get_share_request(),
)?;
2025-06-04 21:02:35 +00:00
Ok(Self::new(
args.chunk_size,
http,
CacheFile::from_args(args, share_id),
))
2025-06-04 21:02:35 +00:00
}
fn get_or_create_progressbar(&self, uploading: &file::Uploading) -> Ref<'_, ProgressBar> {
2025-06-12 23:43:03 +00:00
let mut slot = self.progress.borrow_mut();
if slot.is_none() {
2025-06-06 23:42:18 +00:00
let bar = ProgressBar::new(uploading.get_size())
2025-06-05 21:36:41 +00:00
.with_style(
ProgressStyle::with_template(&format!(
concat!(
"{{msg:.yellow}}: {{bar:50.cyan/blue}} ",
"{{binary_bytes:.magenta}}{}{{binary_total_bytes:.magenta}} ",
"({{eta}})",
),
style("/").magenta(),
))
2025-06-12 23:28:42 +00:00
.expect("style template is not valid"),
2025-06-05 21:36:41 +00:00
)
.with_position(uploading.get_offset())
.with_message(uploading.get_name().to_owned());
2025-06-05 21:36:41 +00:00
bar.enable_steady_tick(Duration::from_millis(100));
*slot = Some(bar);
}
drop(slot);
2025-06-12 23:43:03 +00:00
Ref::map(self.progress.borrow(), |opt| {
2025-06-12 23:28:42 +00:00
opt.as_ref().expect("somehow the slot holds None")
})
}
fn finish_bar(&self) {
2025-06-12 23:43:03 +00:00
let mut slot = self.progress.borrow_mut();
if let Some(bar) = slot.as_ref() {
bar.finish();
*slot = None;
}
}
pub fn upload_chunk(&mut self) -> sharry::Result<Option<()>> {
let Some(mut uploading) = self.inner.pop_file(&self.http) else {
2025-06-12 23:28:42 +00:00
self.inner
.share_notify(&self.http)
.unwrap_or_else(|e| warn!("Failed to notify the share: {e}"));
return Ok(None);
};
self.get_or_create_progressbar(&uploading);
debug!("{uploading} chunk {}", self.buffer.len());
2025-06-05 17:37:35 +00:00
let chunk = uploading
.read(&mut self.buffer)
.map_err(ClientError::from)?;
self.http.file_patch(
chunk.get_patch_uri(),
self.inner.alias_id(),
chunk.get_offset(),
chunk.get_data(),
)?;
match uploading.check_eof() {
Ok(uploading) => {
let bar = self.get_or_create_progressbar(&uploading);
bar.set_position(uploading.get_offset());
// BUG in `indicatif` crate?
// `set_position` does not force immediate redraw, so we also call `inc_length` here
bar.inc_length(0);
drop(bar);
self.inner.push_file(uploading);
2025-06-05 01:13:54 +00:00
Ok(Some(()))
}
Err(path) => {
2025-06-05 01:13:54 +00:00
debug!("Finished {:?}!", path.display());
self.finish_bar();
2025-06-05 11:20:27 +00:00
Ok(self.inner.has_file().then_some(()))
2025-06-05 01:13:54 +00:00
}
}
}
pub fn file_names(&self) -> Vec<&str> {
self.inner.file_names()
}
2025-06-04 21:02:35 +00:00
pub fn save(&self) -> io::Result<()> {
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<()> {
self.inner.clear()
2025-06-02 23:57:17 +00:00
}
}