use std::{ fs, io::{self, Write}, path::{Path, PathBuf}, }; use log::{debug, error, trace}; use serde::{Deserialize, Serialize}; use super::{ cli::Cli, sharry::{Alias, FileChecked, FileUploading, Share}, }; #[derive(Serialize, Deserialize, Debug)] pub struct AppState { #[serde(skip)] file_name: PathBuf, alias: Alias, share: Share, files: Vec, } #[derive(Serialize, Deserialize, Debug)] enum FileState { C(FileChecked), U(FileUploading), } impl AppState { fn cache_dir() -> Option { let dir_name = dirs_next::cache_dir()?.join("shrupl"); trace!("cachedir: {}", dir_name.display()); Some(dir_name) } fn cache_file(args: &Cli) -> Option { let file_name = Self::cache_dir()?.join(format!("{}.json", args.get_hash())); trace!("cachefile: {}", file_name.display()); Some(file_name) } fn load(file_name: impl AsRef) -> io::Result { let content = fs::read_to_string(file_name)?; serde_json::from_str(&content).map_err(io::Error::other) } pub fn try_resume(args: &Cli) -> Option { let file_name = Self::cache_file(args)?; Self::load(&file_name) .inspect_err(|e| debug!("could not resume from {}: {e}", file_name.display())) .map(|state| { debug!("successfully loaded AppState"); Self { file_name, alias: state.alias, share: state.share, files: state.files, } }) .ok() } pub fn from_args(args: Cli, agent: &ureq::Agent) -> Option { let file_name = Self::cache_file(&args)?; let alias = args.get_alias(); let share = Share::create(&agent, &alias, args.get_share_request()) .inspect_err(|e| error!("could not create Share: {e}")) .ok()?; let files: Vec<_> = args.files.into_iter().map(|f| FileState::C(f)).collect(); Some(Self { file_name, alias, share, files, }) } pub fn save(&self) -> io::Result<()> { let cache_dir = Self::cache_dir() .ok_or_else(|| io::Error::other("could not determine cache directory"))?; fs::create_dir_all(cache_dir)?; let json = serde_json::to_string_pretty(self).map_err(io::Error::other)?; let mut file = fs::File::create(&self.file_name)?; file.write_all(json.as_bytes())?; trace!("successfully saved AppState"); Ok(()) } pub fn delete(self) -> io::Result<()> { fs::remove_file(self.file_name)?; trace!("successfully cleared AppState"); Ok(()) } }