[wip] impl Client for ureq::Agent

- Client is now owned by `AppState`
- minor cleanups
This commit is contained in:
Jörn-Michael Miehe 2025-06-12 23:01:16 +00:00
parent b9a0e1eeb0
commit 9f1e0cfc6c
5 changed files with 46 additions and 28 deletions

View file

@ -19,6 +19,7 @@ pub struct AppState {
current_bar: RefCell<Option<ProgressBar>>, current_bar: RefCell<Option<ProgressBar>>,
buffer: Vec<u8>, buffer: Vec<u8>,
http: ureq::Agent,
inner: CacheFile, inner: CacheFile,
} }
@ -30,11 +31,19 @@ impl fmt::Debug for AppState {
} }
} }
fn new_http(timeout: Option<Duration>) -> ureq::Agent {
ureq::Agent::config_builder()
.timeout_global(timeout)
.build()
.into()
}
impl AppState { impl AppState {
fn new(chunk_size: usize, inner: CacheFile) -> Self { fn new(chunk_size: usize, http: ureq::Agent, inner: CacheFile) -> Self {
Self { Self {
current_bar: None.into(), current_bar: None.into(),
buffer: vec![0; chunk_size * 1024 * 1024], buffer: vec![0; chunk_size * 1024 * 1024],
http,
inner, inner,
} }
} }
@ -44,10 +53,16 @@ impl AppState {
.inspect_err(|e| debug!("could not resume from hash {:?}: {e}", args.get_hash())) .inspect_err(|e| debug!("could not resume from hash {:?}: {e}", args.get_hash()))
.ok()?; .ok()?;
Some(Self::new(args.chunk_size, inner)) Some(Self::new(
args.chunk_size,
new_http(args.get_timeout()),
inner,
))
} }
pub fn from_args(args: &Cli, http: &impl Client) -> sharry::Result<Self> { pub fn from_args(args: &Cli) -> sharry::Result<Self> {
let http = new_http(args.get_timeout());
let share_id = http.share_create( let share_id = http.share_create(
&args.get_uri().endpoint("alias/upload/new"), &args.get_uri().endpoint("alias/upload/new"),
&args.alias, &args.alias,
@ -56,6 +71,7 @@ impl AppState {
Ok(Self::new( Ok(Self::new(
args.chunk_size, args.chunk_size,
http,
CacheFile::from_args(args, share_id), CacheFile::from_args(args, share_id),
)) ))
} }
@ -95,9 +111,9 @@ impl AppState {
} }
} }
pub fn upload_chunk(&mut self, http: &impl Client) -> sharry::Result<Option<()>> { pub fn upload_chunk(&mut self) -> sharry::Result<Option<()>> {
let Some(mut uploading) = self.inner.pop_file(http) else { let Some(mut uploading) = self.inner.pop_file(&self.http) else {
self.inner.share_notify(http).unwrap(); // HACK unwrap self.inner.share_notify(&self.http).unwrap(); // HACK unwrap
return Ok(None); return Ok(None);
}; };
@ -110,7 +126,7 @@ impl AppState {
.read(&mut self.buffer) .read(&mut self.buffer)
.map_err(ClientError::from)?; .map_err(ClientError::from)?;
http.file_patch( self.http.file_patch(
chunk.get_patch_uri(), chunk.get_patch_uri(),
self.inner.alias_id(), self.inner.alias_id(),
chunk.get_offset(), chunk.get_offset(),

View file

@ -1,4 +1,5 @@
use std::{ use std::{
fmt,
hash::{DefaultHasher, Hash, Hasher}, hash::{DefaultHasher, Hash, Hasher},
time::Duration, time::Duration,
}; };
@ -10,7 +11,7 @@ use super::{
sharry::{NewShareRequest, Uri}, sharry::{NewShareRequest, Uri},
}; };
#[derive(Parser, Debug, Hash)] #[derive(Parser, Hash)]
#[command(version, about, long_about = None)] #[command(version, about, long_about = None)]
pub struct Cli { pub struct Cli {
/// Timeout in seconds for HTTP actions (set 0 or invalid to disable) /// Timeout in seconds for HTTP actions (set 0 or invalid to disable)
@ -56,6 +57,20 @@ pub struct Cli {
pub files: Vec<Checked>, pub files: Vec<Checked>,
} }
impl fmt::Debug for Cli {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Cli")
.field("uri", &self.get_uri())
.field("alias", &self.alias)
.field("timeout", &self.get_timeout())
.field("chunk_size", &self.chunk_size)
.field("share_request", &self.get_share_request())
.field("files", &self.files)
.field("hash", &self.get_hash())
.finish_non_exhaustive()
}
}
fn parse_seconds(data: &str) -> Result<Duration, String> { fn parse_seconds(data: &str) -> Result<Duration, String> {
data.parse().or(Ok(0)).map(Duration::from_secs) data.parse().or(Ok(0)).map(Duration::from_secs)
} }

View file

@ -16,7 +16,6 @@ use clap::Parser;
use console::style; use console::style;
use dialoguer::{Confirm, theme::ColorfulTheme}; use dialoguer::{Confirm, theme::ColorfulTheme};
use log::{error, info}; use log::{error, info};
use ureq::Agent;
use appstate::AppState; use appstate::AppState;
use cli::Cli; use cli::Cli;
@ -76,12 +75,6 @@ fn main() {
let args = Cli::parse(); let args = Cli::parse();
info!("args: {args:?}"); info!("args: {args:?}");
info!("timeout: {:?}", args.get_timeout());
let agent: Agent = Agent::config_builder()
.timeout_global(args.get_timeout())
.build()
.into();
let mut state = AppState::try_resume(&args) let mut state = AppState::try_resume(&args)
.and_then(|state| { .and_then(|state| {
@ -94,7 +87,7 @@ fn main() {
.unwrap_or_else(|| { .unwrap_or_else(|| {
check_ctrlc(); check_ctrlc();
match AppState::from_args(&args, &agent) { match AppState::from_args(&args) {
Ok(state) => { Ok(state) => {
state.save().unwrap(); // HACK unwrap state.save().unwrap(); // HACK unwrap
state state
@ -115,7 +108,7 @@ fn main() {
); );
loop { loop {
match state.upload_chunk(&agent) { match state.upload_chunk() {
Err(e) => error!("error: {e:?}"), Err(e) => error!("error: {e:?}"),
Ok(None) => { Ok(None) => {
info!("all uploads done"); info!("all uploads done");

View file

@ -31,7 +31,7 @@ impl fmt::Display for Uri {
} }
} }
#[derive(Serialize)] #[derive(Serialize, Debug)]
#[allow(non_snake_case)] #[allow(non_snake_case)]
pub struct NewShareRequest { pub struct NewShareRequest {
name: String, name: String,

View file

@ -21,7 +21,7 @@ pub trait Client {
file_size: u64, file_size: u64,
) -> Result<String>; ) -> Result<String>;
fn file_patch(&self, patch_uri: &str, alias_id: &str, offset: u64, chunk: &[u8]) -> Result<()>; fn file_patch(&self, endpoint: &str, alias_id: &str, offset: u64, chunk: &[u8]) -> Result<()>;
} }
#[derive(Debug, Error)] #[derive(Debug, Error)]
@ -86,8 +86,6 @@ impl Client for ureq::Agent {
alias_id: &str, alias_id: &str,
data: NewShareRequest, data: NewShareRequest,
) -> Result<String> { ) -> Result<String> {
// let endpoint = uri.get_endpoint("alias/upload/new");
let mut res = self let mut res = self
.post(endpoint) .post(endpoint)
.header("Sharry-Alias", alias_id) .header("Sharry-Alias", alias_id)
@ -112,8 +110,6 @@ impl Client for ureq::Agent {
} }
fn share_notify(&self, endpoint: &str, alias_id: &str) -> Result<()> { fn share_notify(&self, endpoint: &str, alias_id: &str) -> Result<()> {
// let endpoint = uri.get_endpoint(format!("alias/mail/notify/{}", share_id));
let mut res = self let mut res = self
.post(endpoint) .post(endpoint)
.header("Sharry-Alias", alias_id) .header("Sharry-Alias", alias_id)
@ -140,8 +136,6 @@ impl Client for ureq::Agent {
file_name: &str, file_name: &str,
file_size: u64, file_size: u64,
) -> Result<String> { ) -> Result<String> {
// let endpoint = uri.get_endpoint(format!("alias/upload/{}/files/tus", share_id));
let res = self let res = self
.post(endpoint) .post(endpoint)
.header("Sharry-Alias", alias_id) .header("Sharry-Alias", alias_id)
@ -164,15 +158,15 @@ impl Client for ureq::Agent {
Ok(location) Ok(location)
} }
fn file_patch(&self, patch_uri: &str, alias_id: &str, offset: u64, chunk: &[u8]) -> Result<()> { fn file_patch(&self, endpoint: &str, alias_id: &str, offset: u64, chunk: &[u8]) -> Result<()> {
let res = self let res = self
.patch(patch_uri) .patch(endpoint)
.header("Sharry-Alias", alias_id) .header("Sharry-Alias", alias_id)
.header("Upload-Offset", offset) .header("Upload-Offset", offset)
.send(chunk) .send(chunk)
.map_err(ClientError::from)?; .map_err(ClientError::from)?;
trace!("{patch_uri:?} response: {res:?}"); trace!("{endpoint:?} response: {res:?}");
ClientError::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?; ClientError::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?;
let res_offset = (res.headers().get("Upload-Offset")) let res_offset = (res.headers().get("Upload-Offset"))