Compare commits
3 commits
10bb4feef5
...
4bf18631d4
| Author | SHA1 | Date | |
|---|---|---|---|
| 4bf18631d4 | |||
| 56223b3ae8 | |||
| 205af655e5 |
6 changed files with 69 additions and 19 deletions
|
|
@ -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"
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
12
src/cli.rs
12
src/cli.rs
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue