2025-06-04 13:25:00 +00:00
|
|
|
use std::{
|
|
|
|
|
ffi::OsStr,
|
2025-06-05 22:08:24 +00:00
|
|
|
fs, io,
|
2025-06-04 13:25:00 +00:00
|
|
|
path::{Path, PathBuf},
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
use log::debug;
|
|
|
|
|
use serde::{Deserialize, Serialize};
|
2025-06-06 22:25:39 +00:00
|
|
|
use ureq::http::{HeaderValue, StatusCode};
|
2025-06-04 13:25:00 +00:00
|
|
|
|
2025-06-06 15:21:49 +00:00
|
|
|
use super::{Alias, FileUploading, Share, SharryAlias, SharryFile};
|
2025-06-04 13:25:00 +00:00
|
|
|
|
2025-06-04 16:44:24 +00:00
|
|
|
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
2025-06-04 13:25:00 +00:00
|
|
|
pub struct FileChecked {
|
|
|
|
|
path: PathBuf,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl FileChecked {
|
|
|
|
|
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)?,
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
Err(io::Error::new(
|
2025-06-05 22:08:24 +00:00
|
|
|
io::ErrorKind::InvalidInput,
|
2025-06-04 13:25:00 +00:00
|
|
|
"Not a regular file",
|
|
|
|
|
))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub fn start_upload(
|
|
|
|
|
self,
|
|
|
|
|
http: &ureq::Agent,
|
|
|
|
|
alias: &Alias,
|
|
|
|
|
share: &Share,
|
|
|
|
|
) -> io::Result<FileUploading> {
|
2025-06-06 23:42:18 +00:00
|
|
|
let size = self.get_size();
|
2025-06-04 13:25:00 +00:00
|
|
|
|
|
|
|
|
let res = {
|
|
|
|
|
let endpoint = alias.get_endpoint(format!("alias/upload/{}/files/tus", share.id));
|
|
|
|
|
|
|
|
|
|
(http.post(endpoint))
|
|
|
|
|
.sharry_header(alias)
|
2025-06-06 23:42:18 +00:00
|
|
|
.header("Sharry-File-Name", self.get_name())
|
2025-06-04 13:25:00 +00:00
|
|
|
.header("Upload-Length", size)
|
|
|
|
|
.send_empty()
|
2025-06-06 15:21:49 +00:00
|
|
|
.map_err(ureq::Error::into_io)?
|
2025-06-04 13:25:00 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if res.status() != StatusCode::CREATED {
|
|
|
|
|
return Err(io::Error::other("unexpected response status"));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
let location = (res.headers().get("Location"))
|
|
|
|
|
.ok_or_else(|| io::Error::other("Location header not found"))?
|
|
|
|
|
.to_str()
|
|
|
|
|
.map_err(|_| io::Error::other("Location header invalid"))?
|
|
|
|
|
.to_string();
|
|
|
|
|
|
|
|
|
|
debug!("patch uri: {location}");
|
|
|
|
|
|
2025-06-04 16:58:18 +00:00
|
|
|
Ok(FileUploading::new(self.path, size, location))
|
2025-06-04 13:25:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
2025-06-06 15:21:49 +00:00
|
|
|
|
|
|
|
|
impl<'t> SharryFile<'t> for FileChecked {
|
|
|
|
|
/// get a reference to the file's name
|
|
|
|
|
///
|
2025-06-06 22:25:39 +00:00
|
|
|
/// Uses `SharryFile::extract_file_name`, which may **panic**!
|
2025-06-06 23:42:18 +00:00
|
|
|
fn get_name(&'t self) -> &'t str {
|
2025-06-06 15:21:49 +00:00
|
|
|
<Self as SharryFile>::extract_file_name(&self.path)
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-06 23:42:18 +00:00
|
|
|
fn get_size(&self) -> u64 {
|
|
|
|
|
fs::metadata(&self.path).unwrap().len()
|
2025-06-06 15:21:49 +00:00
|
|
|
}
|
|
|
|
|
}
|