restructure + refactor sharry::file

This commit is contained in:
Jörn-Michael Miehe 2025-05-27 10:44:06 +00:00
parent c5809d8faf
commit 28039fd286
4 changed files with 128 additions and 109 deletions

View file

@ -22,12 +22,15 @@ fn main() {
let share = Share::create(&agent, &alias, share).unwrap();
info!("share: {share:?}");
let file = File::create(&agent, &share, "/lib/x86_64-linux-gnu/liblldb-14.so.1").unwrap();
let file = File::new("/lib/x86_64-linux-gnu/liblldb-14.so.1")
.unwrap()
.create(&agent, &share)
.unwrap();
info!("file: {file:?}");
for chunk in file.chunked(10 * 1024 * 1024) {
println!("chunk len: {}", chunk.bytes.len());
file.upload_chunk(&agent, &chunk)
file.upload_chunk(&agent, &alias, &chunk)
.inspect_err(|e| error!("error: {e}"))
.unwrap();
}

View file

@ -6,13 +6,13 @@ use std::{
use log::error;
pub struct ChunkedFile<'t> {
pub struct FileChunks<'t> {
path: &'t PathBuf,
cnum: u64,
csize: usize,
}
impl<'t> ChunkedFile<'t> {
impl<'t> FileChunks<'t> {
pub(super) fn new(path: &'t PathBuf, chunk_size: usize) -> Self {
Self {
path,
@ -22,7 +22,7 @@ impl<'t> ChunkedFile<'t> {
}
}
impl Iterator for ChunkedFile<'_> {
impl Iterator for FileChunks<'_> {
type Item = Chunk;
fn next(&mut self) -> Option<Self::Item> {

View file

@ -1,99 +0,0 @@
use std::{
ffi::OsStr,
fs::metadata,
io::{Read, Seek},
path::PathBuf,
};
use log::debug;
use ureq::{Error::Other, http::StatusCode};
use super::super::{
alias::{Alias, SharryAlias},
share::Share,
};
use super::chunkedfile::{Chunk, ChunkedFile};
#[derive(Debug)]
pub struct File<'t> {
alias: &'t Alias,
path: PathBuf,
patch_uri: String,
}
impl<'t> File<'t> {
pub fn create(
http: &ureq::Agent,
share: &'t Share,
path: impl Into<PathBuf>,
) -> Result<Self, ureq::Error> {
let path: PathBuf = path.into();
let filename = 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", metadata(&path)?.len())
.send_empty()?;
if res.status() != StatusCode::CREATED {
return Err(Other("unexpected response status".into()));
}
let location = res
.headers()
.get("Location")
.ok_or_else(|| Other("Location header not found".into()))?
.to_str()
.map_err(|_| Other("Location header invalid".into()))?;
debug!("location: {location}");
Ok(Self {
alias: share.alias,
path,
patch_uri: location.into(),
})
}
pub fn chunked(&self, chunk_size: usize) -> ChunkedFile {
ChunkedFile::new(&self.path, chunk_size)
}
pub fn upload_chunk(&self, http: &ureq::Agent, chunk: &Chunk) -> Result<(), ureq::Error> {
debug!("upload uri: {:?}", self.patch_uri);
let res = http
.patch(&self.patch_uri)
.sharry_header(self.alias)
.header("Upload-Offset", chunk.offset)
.send(&chunk.bytes)?;
if res.status() != StatusCode::NO_CONTENT {
return Err(Other("unexpected response status".into()));
}
let offset = res
.headers()
.get("Upload-Offset")
.ok_or_else(|| Other("Upload-Offset header not found".into()))?
.to_str()
.map_err(|_| Other("Upload-Offset header invalid".into()))?
.parse::<u64>()
.map_err(|_| Other("Upload-Offset header not an integer".into()))?;
if chunk.after() != offset {
return Err(Other("unexpected offset response".into()));
}
Ok(())
}
}

View file

@ -1,7 +1,122 @@
#![allow(unused_imports)]
mod chunks;
mod chunkedfile;
mod file;
use std::{ffi::OsStr, fs::metadata, io, path::PathBuf};
pub use chunkedfile::{Chunk, ChunkedFile};
pub use file::File;
use log::{debug, error};
use ureq::{Error::Other, http::StatusCode};
use super::{
alias::{Alias, SharryAlias},
share::Share,
};
pub use chunks::{Chunk, FileChunks};
#[derive(Debug)]
pub struct File {
path: PathBuf,
name: String,
size: u64,
patch_uri: Option<String>,
}
impl File {
pub fn new(path: impl Into<PathBuf>) -> io::Result<Self> {
let path: PathBuf = path.into();
let name = path
.file_name()
.and_then(OsStr::to_str)
.unwrap_or("file.bin")
.to_string();
let size = metadata(&path)?.len();
Ok(Self {
path,
name,
size,
patch_uri: None,
})
}
pub fn create(self, http: &ureq::Agent, share: &Share) -> Result<Self, ureq::Error> {
if self.patch_uri.is_some() {
return Err(Other("patch_uri already set".into()));
}
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", &self.name)
.header("Upload-Length", self.size)
.send_empty()?;
if res.status() != StatusCode::CREATED {
return Err(Other("unexpected response status".into()));
}
let location = res
.headers()
.get("Location")
.ok_or_else(|| Other("Location header not found".into()))?
.to_str()
.map_err(|_| Other("Location header invalid".into()))?
.to_string();
debug!("received uri: {location}");
Ok(Self {
path: self.path,
name: self.name,
size: self.size,
patch_uri: Some(location),
})
}
pub fn chunked(&self, chunk_size: usize) -> FileChunks {
FileChunks::new(&self.path, chunk_size)
}
pub fn upload_chunk(
&self,
http: &ureq::Agent,
alias: &Alias,
chunk: &Chunk,
) -> Result<(), ureq::Error> {
let patch_uri = self
.patch_uri
.as_ref()
.ok_or_else(|| Other("unset patch_uri".into()))?;
debug!("upload uri: {patch_uri:?}");
let res = http
.patch(patch_uri)
.sharry_header(alias)
.header("Upload-Offset", chunk.offset)
.send(&chunk.bytes)?;
if res.status() != StatusCode::NO_CONTENT {
return Err(Other("unexpected response status".into()));
}
let offset = res
.headers()
.get("Upload-Offset")
.ok_or_else(|| Other("Upload-Offset header not found".into()))?
.to_str()
.map_err(|_| Other("Upload-Offset header invalid".into()))?
.parse::<u64>()
.map_err(|_| Other("Upload-Offset header not an integer".into()))?;
if chunk.after() != offset {
return Err(Other("unexpected offset response".into()));
}
Ok(())
}
}