From d197c06688e09cf989d9d7688868f02d704a62b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= <40151420+ldericher@users.noreply.github.com> Date: Mon, 2 Jun 2025 23:57:17 +0000 Subject: [PATCH] struct AppState --- Cargo.lock | 92 ++++++++++++++++++++++++++++++++++++++++++ Cargo.toml | 2 + src/appstate.rs | 49 ++++++++++++++++++++++ src/cli.rs | 23 +++++++++-- src/main.rs | 6 +++ src/sharry/alias.rs | 3 +- src/sharry/file/mod.rs | 7 +++- src/sharry/share.rs | 3 +- 8 files changed, 179 insertions(+), 6 deletions(-) create mode 100644 src/appstate.rs diff --git a/Cargo.lock b/Cargo.lock index 4299e43..f7120d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -73,6 +73,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bitflags" +version = "2.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" + [[package]] name = "bytes" version = "1.10.1" @@ -187,6 +193,27 @@ dependencies = [ "powerfmt", ] +[[package]] +name = "dirs-next" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +dependencies = [ + "cfg-if", + "dirs-sys-next", +] + +[[package]] +name = "dirs-sys-next" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +dependencies = [ + "libc", + "redox_users", + "winapi", +] + [[package]] name = "displaydoc" version = "0.2.5" @@ -460,6 +487,16 @@ version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags", + "libc", +] + [[package]] name = "litemap" version = "0.8.0" @@ -565,6 +602,17 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror", +] + [[package]] name = "regex" version = "1.11.1" @@ -701,9 +749,11 @@ name = "shrupl" version = "0.1.0" dependencies = [ "clap", + "dirs-next", "env_logger", "log", "serde", + "serde_json", "ureq", ] @@ -753,6 +803,26 @@ dependencies = [ "syn", ] +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "time" version = "0.3.41" @@ -898,6 +968,28 @@ dependencies = [ "rustls-pki-types", ] +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + [[package]] name = "windows-sys" version = "0.52.0" diff --git a/Cargo.toml b/Cargo.toml index e585b7a..b424a76 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,9 +6,11 @@ description = "ShrUpl is a tool to upload files to a Sharry Instance through a p [dependencies] clap = { version = "4.5.38", features = ["derive"] } +dirs-next = "2.0.0" env_logger = "0.11.8" log = "0.4.27" serde = { version = "1.0.219", features = ["derive"] } +serde_json = "1.0.140" ureq = { version = "3.0.11", features = ["json"] } [profile.release] diff --git a/src/appstate.rs b/src/appstate.rs new file mode 100644 index 0000000..583c21a --- /dev/null +++ b/src/appstate.rs @@ -0,0 +1,49 @@ +use std::{ + fs, + io::{self, Write}, + path::Path, +}; + +use log::{debug, trace}; +use serde::{Deserialize, Serialize}; + +use super::{ + cli::Cli, + sharry::{Alias, File, Share}, +}; + +#[derive(Serialize, Deserialize, Debug)] +pub struct AppState { + alias: Alias, + share: Share, + files: Vec, +} + +impl AppState { + fn load(file_name: impl AsRef) -> io::Result { + let content = fs::read_to_string(file_name)?; + let state = serde_json::from_str(&content).map_err(io::Error::other)?; + + Ok(state) + } + + fn save(&self, file_name: impl AsRef) -> io::Result<()> { + let json = serde_json::to_string_pretty(self).map_err(io::Error::other)?; + let mut file = fs::File::create(file_name)?; + file.write_all(json.as_bytes())?; + + Ok(()) + } + + pub fn try_resume(args: &Cli) -> Option { + let file_name = dirs_next::cache_dir()? + .join("shrupl") + .join(format!("{}.json", args.get_hash())); + + trace!("loading from {}", file_name.display()); + + Self::load(&file_name) + .inspect_err(|e| debug!("could not resume from {}: {e}", file_name.display())) + .ok() + } +} diff --git a/src/cli.rs b/src/cli.rs index f875f9c..a83a1a3 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -1,15 +1,18 @@ -use std::time::Duration; +use std::{ + hash::{DefaultHasher, Hash, Hasher}, + time::Duration, +}; use clap::{Parser, builder::PossibleValuesParser}; -use super::sharry::{File, Alias, Uri, NewShareRequest}; +use super::sharry::{Alias, File, NewShareRequest, Uri}; #[derive(Parser, Debug, Hash)] #[command(version, about, long_about = None)] pub struct Cli { /// Timeout in seconds for HTTP actions (set 0 or invalid to disable) #[arg( - short, long, + short, long, default_value = "10", value_name = "SECS", value_parser = parse_seconds, )] @@ -70,4 +73,18 @@ impl Cli { pub fn get_share_request(&self) -> NewShareRequest { NewShareRequest::new(&self.name, self.description.as_ref(), self.max_views) } + + pub fn get_hash(&self) -> String { + let file_refs = { + let mut refs: Vec<_> = self.files.iter().map(File::get_path).collect(); + refs.sort_unstable(); + + refs + }; + + let mut hasher = DefaultHasher::new(); + (self.get_alias(), file_refs).hash(&mut hasher); + + format!("{:x}", hasher.finish()) + } } diff --git a/src/main.rs b/src/main.rs index 7a34958..cc51e17 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,3 +1,4 @@ +mod appstate; mod cli; mod sharry; @@ -5,6 +6,7 @@ use clap::Parser; use log::{error, info}; use ureq::Agent; +use appstate::AppState; use cli::Cli; use sharry::Share; @@ -20,6 +22,10 @@ fn main() { .build() .into(); + if let Some(state) = AppState::try_resume(&args) { + info!("state: {state:?}"); + } + let alias = args.get_alias(); let share = Share::create(&agent, &alias, args.get_share_request()).unwrap(); info!("share: {share:?}"); diff --git a/src/sharry/alias.rs b/src/sharry/alias.rs index 8bd2cf7..bd8a8d0 100644 --- a/src/sharry/alias.rs +++ b/src/sharry/alias.rs @@ -1,11 +1,12 @@ use std::fmt::{Debug, Display}; use log::debug; +use serde::{Deserialize, Serialize}; use ureq::RequestBuilder; use super::api::Uri; -#[derive(Debug, Hash)] +#[derive(Serialize, Deserialize, Debug, Hash)] pub struct Alias { pub(super) api_uri: String, pub(super) id: String, diff --git a/src/sharry/file/mod.rs b/src/sharry/file/mod.rs index e948103..6abae89 100644 --- a/src/sharry/file/mod.rs +++ b/src/sharry/file/mod.rs @@ -9,6 +9,7 @@ use std::{ }; use log::{debug, error}; +use serde::{Deserialize, Serialize}; use ureq::{Error::Other, http::StatusCode}; use super::{ @@ -17,7 +18,7 @@ use super::{ }; pub use chunks::{Chunk, FileChunks}; -#[derive(Debug, Clone)] +#[derive(Serialize, Deserialize, Debug, Clone)] pub struct File { abs_path: PathBuf, name: String, @@ -52,6 +53,10 @@ impl File { }) } + pub fn get_path(&self) -> &Path { + &self.abs_path + } + pub fn create( self, http: &ureq::Agent, diff --git a/src/sharry/share.rs b/src/sharry/share.rs index b9254bd..0733bd4 100644 --- a/src/sharry/share.rs +++ b/src/sharry/share.rs @@ -1,11 +1,12 @@ use log::debug; +use serde::{Deserialize, Serialize}; use super::{ alias::{Alias, SharryAlias}, api::{NewShareRequest, NewShareResponse, NotifyShareResponse}, }; -#[derive(Debug)] +#[derive(Serialize, Deserialize, Debug)] pub struct Share { pub(super) id: String, }