use std::{ fs, io, path::{Path, PathBuf}, }; use serde::{Deserialize, Serialize}; use crate::sharry; use super::{FileTrait, Uploading}; /// Description of an existing, regular file /// /// - impl `Clone` for `clap` compatibility /// - impl `serde` for cachefile handling /// - impl `PartialEq..Ord` to handle multiple files given /// - impl `AsRef<[u8]>` for hashing with `blake2b_simd` #[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct Checked { /// canonical path to a regular file pub(super) path: PathBuf, /// size of that file pub(super) size: u64, /// hash of that file pub(super) hash: Option, } impl AsRef<[u8]> for Checked { fn as_ref(&self) -> &[u8] { self.path.as_os_str().as_encoded_bytes() } } impl Checked { /// create a new checked file from some path reference /// /// # Errors /// /// - from `fs::metadata(path)` or `fs::canonicalize` /// - given path does not correspond to a regular file pub fn new(value: impl AsRef) -> io::Result { let meta = fs::metadata(&value)?; if meta.is_file() { Ok(Self { path: fs::canonicalize(&value)?, size: meta.len(), hash: None, }) } else { Err(io::Error::new( io::ErrorKind::InvalidInput, "Not a regular file", )) } } /// calculate and store hash for this file /// /// # Errors /// /// - from `file::compute_hash` /// - Mismatch if file already hashed pub fn hash(&mut self, f: impl Fn(u64)) -> crate::Result<()> { if self.hash.is_some() { return Err(crate::Error::mismatch("unhashed file", self.path.display())); } self.hash = Some(super::compute_hash(&self.path, self.size, f)?); Ok(()) } /// starts uploading this file /// /// - tries to create a new file using the client /// - consumes `self` into a `file::Uploading` struct /// /// # Errors /// /// - from `sharry::Client::file_create` pub fn start_upload( self, client: &impl sharry::Client, uri: &sharry::Uri, alias_id: &sharry::AliasID, share_id: &sharry::ShareID, ) -> crate::Result { let file_id = client.file_create(uri, alias_id, share_id, &self)?; Ok(Uploading::new(self.path, self.size, self.hash, file_id)) } } impl FileTrait for Checked { fn get_name(&self) -> &str { ::extract_file_name(&self.path) } fn get_size(&self) -> u64 { self.size } fn check_hash(&self, on_progress: impl FnMut(u64)) -> crate::Result<()> { super::check_hash( &self.path, self.size, self.hash.as_deref(), on_progress, ) } }