104 lines
2.8 KiB
Rust
104 lines
2.8 KiB
Rust
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 appstate caching
|
|
/// - impl PartialEq..Ord to handle multiple files given
|
|
#[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<String>,
|
|
}
|
|
|
|
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<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(
|
|
io::ErrorKind::InvalidInput,
|
|
"Not a regular file",
|
|
))
|
|
}
|
|
}
|
|
|
|
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_file_hash(&self.path, self.size, f)?);
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// start uploading this file
|
|
///
|
|
/// - tries to create a new entry in a share
|
|
/// - expects endpoint like `{base_uri}/alias/upload/{share_id}/files/tus`
|
|
/// - consumes `self` into a `file::Uploading` struct
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// TODO documentation after `ClientError` rework
|
|
pub fn start_upload(
|
|
self,
|
|
client: &impl sharry::Client,
|
|
uri: &sharry::Uri,
|
|
alias_id: &sharry::AliasID,
|
|
share_id: &sharry::ShareID,
|
|
) -> crate::Result<Uploading> {
|
|
let file_id = client.file_create(uri, alias_id, share_id, &self)?;
|
|
|
|
Ok(Uploading::new(self.path, self.size, self.hash, file_id))
|
|
}
|
|
}
|
|
|
|
impl<'t> FileTrait<'t> for Checked {
|
|
/// get a reference to the file's name
|
|
///
|
|
/// Uses `SharryFile::extract_file_name`, which may **panic**!
|
|
fn get_name(&'t self) -> &'t str {
|
|
<Self as FileTrait>::extract_file_name(&self.path)
|
|
}
|
|
|
|
/// get the file's size
|
|
fn get_size(&self) -> u64 {
|
|
self.size
|
|
}
|
|
|
|
fn check_hash(&self, on_progress: impl Fn(u64)) -> crate::Result<()> {
|
|
super::check_file_hash(&self.path, self.size, self.hash.as_ref(), on_progress)
|
|
}
|
|
}
|