shrupl/src/appstate.rs

163 lines
4.5 KiB
Rust
Raw Normal View History

use std::{fmt, io, path::PathBuf, 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-05 01:44:39 +00:00
use log::{debug, trace};
2025-06-02 23:57:17 +00:00
use serde::{Deserialize, Serialize};
use super::{
cli::Cli,
file::FileTrait,
savedstate::SavedState,
sharry::{self, Client, ClientError},
2025-06-02 23:57:17 +00:00
};
#[derive(Serialize, Deserialize)]
2025-06-02 23:57:17 +00:00
pub struct AppState {
2025-06-05 17:37:35 +00:00
#[serde(skip)]
progress: Option<ProgressBar>,
#[serde(skip)]
buffer: Vec<u8>,
2025-06-04 21:02:35 +00:00
inner: SavedState,
}
impl fmt::Debug for AppState {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AppState")
.field("inner", &self.inner)
.finish()
}
}
2025-06-02 23:57:17 +00:00
impl AppState {
2025-06-04 21:23:21 +00:00
fn cache_dir() -> PathBuf {
let dir_name = dirs_next::cache_dir()
.expect("could not determine cache directory")
.join("shrupl");
2025-06-04 21:02:35 +00:00
2025-06-05 01:45:25 +00:00
trace!("cachedir: {:?}", dir_name.display());
2025-06-04 21:23:21 +00:00
dir_name
2025-06-04 21:02:35 +00:00
}
2025-06-04 21:23:21 +00:00
fn cache_file(args: &Cli) -> PathBuf {
let file_name = Self::cache_dir().join(format!("{}.json", args.get_hash()));
2025-06-04 21:02:35 +00:00
2025-06-05 01:45:25 +00:00
trace!("cachefile: {:?}", file_name.display());
2025-06-04 21:23:21 +00:00
file_name
2025-06-04 21:02:35 +00:00
}
fn new(chunk_size: usize, inner: SavedState) -> Self {
Self {
progress: None,
buffer: vec![0; chunk_size * 1024 * 1024],
inner,
}
}
2025-06-04 21:02:35 +00:00
pub fn try_resume(args: &Cli) -> Option<Self> {
2025-06-04 21:23:21 +00:00
let file_name = Self::cache_file(args);
let inner = SavedState::load(&file_name)
2025-06-05 01:45:25 +00:00
.inspect_err(|e| debug!("could not resume from {:?}: {e}", file_name.display()))
.ok()?;
Some(Self::new(args.chunk_size, inner))
2025-06-04 21:02:35 +00:00
}
pub fn from_args(args: &Cli, http: &impl Client) -> sharry::Result<Self> {
let uri = args.get_uri();
let share_id = http.share_create(
&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,
SavedState::new(
Self::cache_file(&args),
uri,
args.alias.clone(),
share_id,
&args.files,
),
))
2025-06-04 21:02:35 +00:00
}
2025-06-05 17:37:35 +00:00
pub fn file_names(&self) -> Vec<&str> {
self.inner.file_names()
2025-06-05 17:37:35 +00:00
}
pub fn upload_chunk(&mut self, http: &impl Client) -> sharry::Result<Option<()>> {
let Some(mut uploading) = self.inner.pop_file(http) else {
2025-06-05 22:08:24 +00:00
return Ok(None);
2025-06-05 01:13:54 +00:00
};
debug!("{uploading} chunk {}", self.buffer.len());
2025-06-05 01:13:54 +00:00
// Initialize or fetch the existing ProgressBar
2025-06-05 21:36:41 +00:00
let bar = &*self.progress.get_or_insert_with(|| {
// Create a new bar with style
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(),
))
.unwrap(),
)
2025-06-06 23:42:18 +00:00
.with_message(uploading.get_name().to_owned())
.with_position(uploading.get_offset());
2025-06-05 21:36:41 +00:00
bar.enable_steady_tick(Duration::from_millis(100));
bar
});
2025-06-05 17:37:35 +00:00
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.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);
2025-06-05 01:13:54 +00:00
Ok(Some(()))
}
Err(path) => {
2025-06-05 01:13:54 +00:00
debug!("Finished {:?}!", path.display());
2025-06-05 21:36:41 +00:00
bar.finish();
2025-06-05 17:37:35 +00:00
self.progress = None;
let endpoint = self
.uri
.endpoint(format!("alias/mail/notify/{}", self.share_id));
http.share_notify(&endpoint, &self.alias_id).unwrap(); // HACK unwrap
2025-06-05 11:20:27 +00:00
Ok(self.inner.has_file().then_some(()))
2025-06-05 01:13:54 +00:00
}
}
}
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
}
}