use std::{ collections::VecDeque, fs, io::{self, Write}, path::PathBuf, }; use log::trace; use serde::{Deserialize, Serialize}; use super::{ cli::Cli, file::{self, FileTrait}, sharry::{self, Client, Uri}, }; #[derive(Serialize, Deserialize, Debug)] enum FileState { C(file::Checked), U(file::Uploading), } impl FileState { fn file_name(&self) -> &str { match self { FileState::C(c) => c.get_name(), FileState::U(u) => u.get_name(), } } fn start_upload( self, http: &impl Client, endpoint: &str, alias_id: &str, ) -> sharry::Result { match self { FileState::C(checked) => checked.start_upload(http, endpoint, alias_id), FileState::U(uploading) => Ok(uploading), } } } #[derive(Serialize, Deserialize, Debug)] pub struct CacheFile { #[serde(skip)] file_name: PathBuf, uri: Uri, alias_id: String, share_id: String, files: VecDeque, } impl CacheFile { fn cache_dir() -> PathBuf { let dir_name = dirs_next::cache_dir() .expect("could not determine cache directory") .join("shrupl"); trace!("cachedir: {:?}", dir_name.display()); dir_name } fn cache_file(args: &Cli) -> PathBuf { let file_name = Self::cache_dir().join(format!("{}.json", args.get_hash())); trace!("cachefile: {:?}", file_name.display()); file_name } pub fn try_resume(args: &Cli) -> io::Result { let file_name = Self::cache_file(args); let state: Self = { let file = fs::File::open(&file_name)?; let reader = io::BufReader::new(file); serde_json::from_reader(reader).map_err(io::Error::other)? }; Ok(Self { file_name, ..state }) } pub fn from_args(args: &Cli, share_id: String) -> Self { Self { file_name: Self::cache_file(args), uri: args.get_uri(), alias_id: args.alias.clone(), share_id, files: args.files.clone().into_iter().map(FileState::C).collect(), } } pub fn alias_id(&self) -> &str { &self.alias_id } pub fn file_names(&self) -> Vec<&str> { self.files.iter().map(FileState::file_name).collect() } pub fn has_file(&self) -> bool { !self.files.is_empty() } pub fn pop_file(&mut self, http: &impl Client) -> Option { if let Some(state) = self.files.pop_front() { let endpoint = self .uri .endpoint(format!("alias/upload/{}/files/tus", self.share_id)); Some(state.start_upload(http, &endpoint, &self.alias_id).unwrap()) // HACK unwrap } else { None } } pub fn push_file(&mut self, file: file::Uploading) { self.files.push_front(FileState::U(file)); } pub fn share_notify(&self, http: &impl Client) -> sharry::Result<()> { let endpoint = self .uri .endpoint(format!("alias/mail/notify/{}", self.share_id)); http.share_notify(&endpoint, &self.alias_id) } pub fn save(&self) -> io::Result<()> { let cache_dir = self.file_name.parent().ok_or_else(|| { io::Error::other(format!("orphan file {:?}", self.file_name.display())) })?; 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!("updated {:?}", self.file_name.display()); Ok(()) } pub fn clear(self) -> io::Result<()> { fs::remove_file(&self.file_name)?; trace!("removed {:?}", self.file_name.display()); Ok(()) } }