Compare commits

...

3 commits

Author SHA1 Message Date
4bf18631d4 bug: don't allow chunk_size of 0 2025-06-15 00:46:02 +00:00
56223b3ae8 [wip] documentation
- added more notes
- `main.rs` logging
- clearer matching of `ClientError::ResponseStatus`
2025-06-15 00:44:28 +00:00
205af655e5 remove unnecessary Uri::endpoint call 2025-06-14 12:34:58 +00:00
6 changed files with 69 additions and 19 deletions

View file

@ -1,4 +1,4 @@
outline of sharry uploading # Outline of sharry upload API
1. POST to "new" route 1. POST to "new" route
- uri: https://sharry.yavook.de/api/v2/alias/upload/new - uri: https://sharry.yavook.de/api/v2/alias/upload/new
@ -32,10 +32,29 @@ outline of sharry uploading
- res.status == 200 - res.status == 200
- res_json.success, res_json.message - res_json.success, res_json.message
hints
- https://stackoverflow.com/questions/59586787/rust-how-to-do-http-put-of-large-files # Links
ideas - yvk repo: https://code.yavook.de/jmm/shrupl
- sharry issue: https://github.com/eikek/sharry/issues/1659
- ureq: https://stackoverflow.com/questions/59586787/rust-how-to-do-http-put-of-large-files
- hashing: https://duckduckgo.com/?q=rust+get+file+hash&t=canonical&ia=web
- https://stackoverflow.com/q/69787906
- https://github.com/RustCrypto/hashes
- max errors => stop
# Ideas
- cli functions
- max retries => stop
- "continue" and "new" flags to avoid user interaction
- "quiet" flag to disable output entirely
- "verbose" flag to adjust RUST_LOG for `shrupl` crate
- client error rework
- current variants are too "low level"
- use variants like `InvalidEndpoint`, `InvalidAlias` etc.
- hashing
- store file hashes with all `file::*` variants
- check hashes on "continue"

View file

@ -31,11 +31,11 @@ impl FileState {
fn start_upload( fn start_upload(
self, self,
http: &impl Client, http: &impl Client,
endpoint: &str, endpoint: impl FnOnce() -> String,
alias_id: &str, alias_id: &str,
) -> sharry::Result<file::Uploading> { ) -> sharry::Result<file::Uploading> {
match self { match self {
FileState::C(checked) => checked.start_upload(http, endpoint, alias_id), FileState::C(checked) => checked.start_upload(http, &endpoint(), alias_id),
FileState::U(uploading) => Ok(uploading), FileState::U(uploading) => Ok(uploading),
} }
} }
@ -105,11 +105,12 @@ impl CacheFile {
pub fn pop_file(&mut self, http: &impl Client) -> Option<file::Uploading> { pub fn pop_file(&mut self, http: &impl Client) -> Option<file::Uploading> {
if let Some(state) = self.files.pop_front() { if let Some(state) = self.files.pop_front() {
let endpoint = self let endpoint = || {
.uri self.uri
.endpoint(format!("alias/upload/{}/files/tus", self.share_id)); .endpoint(format!("alias/upload/{}/files/tus", self.share_id))
};
// TODO somehow retry // TODO somehow retry
Some(state.start_upload(http, &endpoint, &self.alias_id).unwrap()) // HACK unwrap Some(state.start_upload(http, endpoint, &self.alias_id).unwrap()) // HACK unwrap
} else { } else {
None None
} }

View file

@ -4,7 +4,11 @@ use std::{
time::Duration, time::Duration,
}; };
use clap::{Parser, builder::PossibleValuesParser}; use clap::{
Parser,
builder::{PossibleValuesParser, TypedValueParser},
value_parser,
};
use super::{ use super::{
file::Checked, file::Checked,
@ -43,7 +47,11 @@ pub struct Cli {
max_views: u32, max_views: u32,
/// Chunk size for uploading, in MiB /// Chunk size for uploading, in MiB
#[arg(short, long, default_value_t = 10, value_name = "N")] #[arg(
short, long,
default_value_t = 10, value_name = "M",
value_parser = value_parser!(u32).range(1..).map(|s| s as usize),
)]
pub chunk_size: usize, pub chunk_size: usize,
/// Base URL for Sharry Instance /// Base URL for Sharry Instance

View file

@ -9,13 +9,26 @@ use crate::sharry;
use super::{FileTrait, Uploading}; use super::{FileTrait, Uploading};
/// Description of an existing, regular file
///
/// - impl Debug, Clone, Hash for `clap` compatibility
/// - impl serde for appstate caching
/// - impl Ord to handle multiple files given
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] #[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
pub struct Checked { pub struct Checked {
/// canonical path to a regular file
path: PathBuf, path: PathBuf,
/// size of that file
size: u64, size: u64,
} }
impl Checked { impl Checked {
/// create a new checked file from some path reference
///
/// # Errors
///
/// - calls `fs::metadata(path)` or `fs::canonicalize`
/// - path does not correspond to a regular file
pub fn new(value: impl AsRef<Path>) -> io::Result<Self> { pub fn new(value: impl AsRef<Path>) -> io::Result<Self> {
let meta = fs::metadata(&value)?; let meta = fs::metadata(&value)?;
if meta.is_file() { if meta.is_file() {
@ -31,6 +44,15 @@ impl Checked {
} }
} }
/// 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
///
/// -
pub fn start_upload( pub fn start_upload(
self, self,
client: &impl sharry::Client, client: &impl sharry::Client,
@ -51,6 +73,7 @@ impl<'t> FileTrait<'t> for Checked {
<Self as FileTrait>::extract_file_name(&self.path) <Self as FileTrait>::extract_file_name(&self.path)
} }
/// get the file's size
fn get_size(&self) -> u64 { fn get_size(&self) -> u64 {
self.size self.size
} }

View file

@ -20,7 +20,9 @@ pub trait FileTrait<'t> {
.expect("bad file name") .expect("bad file name")
} }
/// get a reference to the file's name
fn get_name(&'t self) -> &'t str; fn get_name(&'t self) -> &'t str;
/// get the file's size
fn get_size(&self) -> u64; fn get_size(&self) -> u64;
} }

View file

@ -50,10 +50,7 @@ fn prompt_continue() -> bool {
fn print_error(e: &ClientError) { fn print_error(e: &ClientError) {
if let Some(cause) = match e { if let Some(cause) = match e {
// known errors // known errors
ClientError::ResponseStatus { ClientError::ResponseStatus { actual: 403, .. } => Some("Alias ID"),
actual: 403,
expected: _,
} => Some("Alias ID"),
ClientError::StdIo(_) => Some("URL"), ClientError::StdIo(_) => Some("URL"),
// unknown error // unknown error
_ => None, _ => None,
@ -100,7 +97,7 @@ fn main() {
}; };
let args = Cli::parse(); let args = Cli::parse();
info!("args: {args:?}"); info!("args: {args:#?}");
let mut state = AppState::try_resume(&args) let mut state = AppState::try_resume(&args)
.and_then(|state| prompt_continue().then_some(state)) .and_then(|state| prompt_continue().then_some(state))
@ -125,7 +122,7 @@ fn main() {
} }
}); });
info!("continuing with state: {state:?}"); info!("continuing with state: {state:#?}");
let fns_magenta = state let fns_magenta = state
.file_names() .file_names()