shrupl/src/sharry/file.rs

135 lines
3.3 KiB
Rust

use std::{
ffi::OsStr,
fs,
io::{self, Read, Seek},
os::unix::fs::FileExt,
path::{Path, PathBuf},
str::FromStr,
};
use log::{debug, error};
use ureq::http::StatusCode;
use super::{
alias::{Alias, SharryAlias},
share::Share,
};
#[derive(Debug)]
pub struct File<'t> {
alias: &'t Alias,
file_path: PathBuf,
patch_uri: String,
}
impl<'t> File<'t> {
pub fn create(
http: &ureq::Agent,
share: &'t Share,
file_path: impl Into<PathBuf>,
) -> Result<Self, ureq::Error> {
let file_path: PathBuf = file_path.into();
let filename = file_path
.file_name()
.and_then(OsStr::to_str)
.unwrap_or("file.bin");
let endpoint = share
.alias
.get_endpoint(format!("alias/upload/{}/files/tus", share.id));
let res = http
.post(endpoint)
.sharry_header(share.alias)
.header("Sharry-File-Name", filename)
.header("Upload-Length", fs::metadata(&file_path)?.len())
.send_empty()?;
if res.status() != StatusCode::CREATED {
return Err(ureq::Error::Other("unexpected response status".into()));
}
let location = res
.headers()
.get("Location")
.ok_or_else(|| ureq::Error::Other("Location header not found".into()))?
.to_str()
.map_err(|_| ureq::Error::Other("Location header invalid".into()))?;
debug!("location: {}", location);
Ok(Self {
alias: share.alias,
file_path,
patch_uri: location.into(),
})
}
pub fn chunks(&self, chunk_size: usize) -> FileChunks {
FileChunks::new(&self.file_path, chunk_size)
}
// pub fn upload(&mut self) -> io::Result<()> {
// let mut f = fs::File::open(&self.path)?;
// f.seek(io::SeekFrom::Start(0));
// let patch_size = f.read(&mut self.buffer)?;
// let patch_data = &self.buffer[0..patch_size];
// Ok(())
// }
}
pub struct FileChunks<'t> {
num: u64,
path: &'t PathBuf,
buffer: Vec<u8>,
data_len: Option<usize>,
}
impl<'t> FileChunks<'t> {
fn new(path: &'t PathBuf, chunk_size: usize) -> Self {
Self {
num: 0,
path,
buffer: vec![0; chunk_size],
data_len: None,
}
}
fn get_offset(&self) -> Option<u64> {
let buffer_len: u64 = self
.buffer
.len()
.try_into()
.inspect_err(|e| error!("Error converting length: {}", e))
.ok()?;
Some(self.num * buffer_len)
}
}
impl<'t> Iterator for FileChunks<'t> {
type Item = &'t [u8];
fn next(&mut self) -> Option<Self::Item> {
let mut f = fs::File::open(&self.path)
.inspect_err(|e| error!("Error opening file: {}", e))
.ok()?;
f.seek(io::SeekFrom::Start(self.get_offset()?)).ok()?;
self.data_len = {
let read_len = f
.read(&mut self.buffer)
.inspect_err(|e| error!("Error reading file: {}", e))
.ok()?;
Some(read_len).filter(|l| *l > 0)
};
self.num += 1;
Some(unsafe { std::mem::transmute(&self.buffer[0..self.data_len?]) })
}
}