shrupl/src/file/checked.rs

115 lines
3 KiB
Rust
Raw Normal View History

use std::{
2025-06-05 22:08:24 +00:00
fs, io,
path::{Path, PathBuf},
};
use serde::{Deserialize, Serialize};
2025-06-27 01:55:43 +00:00
use crate::sharry;
use super::{FileTrait, Uploading};
/// Description of an existing, regular file
///
2025-06-28 09:24:46 +00:00
/// - 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
path: PathBuf,
/// size of that file
size: u64,
/// hash of that file
hash: Option<String>,
}
impl AsRef<[u8]> for Checked {
fn as_ref(&self) -> &[u8] {
self.path.as_os_str().as_encoded_bytes()
}
}
impl Checked {
pub(super) fn new_direct(path: PathBuf, size: u64, hash: Option<String>) -> Self {
Self { path, size, hash }
}
/// 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<Path>) -> io::Result<Self> {
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(
2025-06-05 22:08:24 +00:00
io::ErrorKind::InvalidInput,
"Not a regular file",
))
}
}
2025-07-03 18:27:41 +00:00
/// calculate and store hash for this file
///
/// # Errors
///
/// - from `file::compute_hash`
/// - Mismatch if file already hashed
///
/// TODO this could use an error variant like `IllegalInvocation`
2025-06-27 01:55:43 +00:00
pub fn hash(&mut self, f: impl Fn(u64)) -> crate::Result<()> {
if self.hash.is_some() {
2025-06-27 01:55:43 +00:00
return Err(crate::Error::mismatch("unhashed file", self.path.display()));
}
self.hash = Some(super::compute_hash(&self.path, self.size, f)?);
Ok(())
}
/// start uploading this file
///
/// - try to create a new file using the client
/// - consume `self` into a `file::Uploading` struct
///
/// # Errors
///
2025-07-03 18:27:41 +00:00
/// - from `sharry::Client::file_create`
pub fn start_upload(
self,
client: &impl sharry::Client,
uri: &sharry::Uri,
2025-06-27 01:47:38 +00:00
alias_id: &sharry::AliasID,
share_id: &sharry::ShareID,
2025-06-27 01:55:43 +00:00
) -> crate::Result<Uploading> {
let file_id = client.file_create(uri, alias_id, share_id, &self)?;
Ok(Uploading::new_direct(
self.path, self.size, self.hash, file_id,
))
}
}
2025-06-06 15:21:49 +00:00
2025-07-03 18:35:50 +00:00
impl FileTrait for Checked {
fn get_name(&self) -> &str {
<Self as FileTrait>::extract_file_name(&self.path)
2025-06-06 15:21:49 +00:00
}
2025-06-06 23:42:18 +00:00
fn get_size(&self) -> u64 {
self.size
2025-06-06 15:21:49 +00:00
}
2025-07-03 18:27:41 +00:00
fn check_hash(&self, on_progress: impl FnMut(u64)) -> crate::Result<()> {
super::check_hash(&self.path, self.size, self.hash.as_deref(), on_progress)
}
2025-06-06 15:21:49 +00:00
}