use std::{ collections::VecDeque, fs, io::{self, Write}, path::PathBuf, }; use log::trace; use serde::{Deserialize, Serialize}; use crate::{ cli::Cli, error, file::{self, Chunk}, sharry::{Client, Uri}, }; #[derive(Serialize, Deserialize, Debug)] pub struct CacheFile { #[serde(skip)] file_name: PathBuf, uri: Uri, alias_id: String, share_id: String, uploading: Option, 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, uploading: None, files: args.files.clone().into(), } } pub fn queue_empty(&self) -> bool { self.files.is_empty() } pub fn get_uploading( &mut self, client: &impl Client, ) -> error::Result> { if self.uploading.is_some() { Ok(self.uploading.as_mut()) } else if let Some(chk) = self.files.pop_front() { let upl = chk.start_upload(client, &self.uri, &self.alias_id, &self.share_id)?; self.uploading.replace(upl); Ok(self.uploading.as_mut()) } else { Ok(None) } } pub fn expect_uploading(&mut self) -> &mut file::Uploading { self.uploading .as_mut() .expect("expect_uploading called while not uploading") } pub fn peek_uploading(&self) -> Option<&file::Uploading> { self.uploading.as_ref() } pub fn check_eof(&mut self) -> Option { if let Some(upl) = self.uploading.take() { match upl.check_eof() { Ok(upl) => self.uploading = Some(upl), Err(p) => return Some(p), } } None } pub fn rewind_chunk(mut self) -> Option { self.uploading = Some( self.uploading .take() .expect("rewind_chunk called while not uploading") .rewind()?, ); Some(self) } pub fn abort_upload(&mut self) { self.files.push_front( self.uploading .take() .expect("abort_upload called while not uploading") .abort(), ); } pub fn share_notify(&self, client: &impl Client) -> error::Result<()> { client.share_notify(&self.uri, &self.alias_id, &self.share_id) } pub fn file_patch(&self, client: &impl Client, chunk: &Chunk) -> error::Result<()> { client.file_patch(&self.uri, &self.alias_id, &self.share_id, chunk) } 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(()) } }