From e391821fe558909efb29b5c6445ffa7392707cb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B6rn-Michael=20Miehe?= <40151420+ldericher@users.noreply.github.com> Date: Wed, 25 Jun 2025 16:48:00 +0000 Subject: [PATCH] adjust crate structure - move `sharry::client::ClientError` to `error::Error` --- src/appstate.rs | 11 +++--- src/bin/shrupl.rs | 11 ++++-- src/cachefile.rs | 9 +++-- src/error.rs | 76 +++++++++++++++++++++++++++++++++++++ src/file/checked.rs | 4 +- src/impl_ureq.rs | 51 ++++++++++++------------- src/lib.rs | 3 +- src/output.rs | 7 ++-- src/sharry/client.rs | 89 ++++---------------------------------------- src/sharry/mod.rs | 4 +- 10 files changed, 135 insertions(+), 130 deletions(-) create mode 100644 src/error.rs diff --git a/src/appstate.rs b/src/appstate.rs index 9f423d1..46e273b 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -7,8 +7,9 @@ use log::{debug, warn}; use crate::{ cachefile::CacheFile, cli::Cli, + error, file::{Chunk, FileTrait}, - sharry::{self, Client}, + sharry::Client, }; pub struct AppState { @@ -50,7 +51,7 @@ impl AppState { Some(Self::new(new_http(args.get_timeout()), inner)) } - pub fn from_args(args: &Cli) -> sharry::Result { + pub fn from_args(args: &Cli) -> error::Result { let http = new_http(args.get_timeout()); let share_id = http.share_create(&args.get_uri(), &args.alias, args.get_share_request())?; @@ -102,7 +103,7 @@ impl AppState { self.with_progressbar(f, true); } - fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> sharry::Result>> { + fn next_chunk<'t>(&mut self, buffer: &'t mut [u8]) -> error::Result>> { if self.inner.get_uploading(&self.http)?.is_none() { return Ok(None); } @@ -118,7 +119,7 @@ impl AppState { Ok(Some(chunk)) } - pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> sharry::Result { + pub fn upload_chunk(&mut self, buffer: &mut [u8]) -> error::Result { let Some(chunk) = self.next_chunk(buffer)? else { self.inner .share_notify(&self.http) @@ -151,7 +152,7 @@ impl AppState { self.drop_progressbar(ProgressBar::abandon); } - pub fn rebuild_share(self, args: &Cli) -> sharry::Result { + pub fn rebuild_share(self, args: &Cli) -> error::Result { let share_id = self.http .share_create(&args.get_uri(), &args.alias, args.get_share_request())?; diff --git a/src/bin/shrupl.rs b/src/bin/shrupl.rs index 06d45f2..401b4f7 100644 --- a/src/bin/shrupl.rs +++ b/src/bin/shrupl.rs @@ -10,7 +10,10 @@ use clap::Parser; use console::{StyledObject, style}; use log::{debug, info, trace}; -use shrupl::{AppState, Cli, ClientError, Log, Parameter, SHRUPL, output}; +use shrupl::{ + AppState, Cli, error, + output::{self, Log, SHRUPL}, +}; fn main() { let check_ctrlc = { @@ -78,16 +81,16 @@ fn main() { Err(e) => { Log::handle(&e); - if let ClientError::InvalidParameter(p) = e { + if let error::Error::InvalidParameter(p) = e { match p { // Error 404 (File not found) - Parameter::FileID(fid) => { + error::Parameter::FileID(fid) => { debug!("requeueing file {fid:?}"); state.abort_upload(); } // Error 404 (Share not found) - Parameter::ShareID(sid) => { + error::Parameter::ShareID(sid) => { // TODO ask debug!("rebuilding share {sid:?}"); diff --git a/src/cachefile.rs b/src/cachefile.rs index 8266f41..f14c23f 100644 --- a/src/cachefile.rs +++ b/src/cachefile.rs @@ -10,8 +10,9 @@ use serde::{Deserialize, Serialize}; use crate::{ cli::Cli, + error, file::{self, Chunk}, - sharry::{self, Client, Uri}, + sharry::{Client, Uri}, }; #[derive(Serialize, Deserialize, Debug)] @@ -74,7 +75,7 @@ impl CacheFile { pub fn get_uploading( &mut self, client: &impl Client, - ) -> sharry::Result> { + ) -> error::Result> { if self.uploading.is_some() { Ok(self.uploading.as_mut()) } else if let Some(chk) = self.files.pop_front() { @@ -128,11 +129,11 @@ impl CacheFile { ); } - pub fn share_notify(&self, client: &impl Client) -> sharry::Result<()> { + pub fn share_notify(&self, client: &impl Client) -> error::Result<()> { client.share_notify(&self.uri, &self.alias_id, &self.share_id) } - pub fn file_patch(&self, client: &impl Client, chunk: &Chunk) -> sharry::Result<()> { + pub fn file_patch(&self, client: &impl Client, chunk: &Chunk) -> error::Result<()> { client.file_patch(&self.uri, &self.alias_id, &self.share_id, chunk) } diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..515f567 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,76 @@ +use std::fmt; + +#[derive(Debug, thiserror::Error)] +pub enum Parameter { + #[error("given URI {0:?}")] + Uri(String), + + #[error("given Alias ID {0:?}")] + AliasID(String), + + #[error("stored Share ID {0:?}")] + ShareID(String), + + #[error("stored File ID {0:?}")] + FileID(String), +} + +impl Parameter { + fn is_fatal(&self) -> bool { + matches!(self, Self::Uri(_) | Self::AliasID(_)) + } +} + +#[derive(Debug, thiserror::Error)] +pub enum Error { + #[error(transparent)] + StdIo(#[from] std::io::Error), + + #[error("response error: {0}")] + Response(String), + + #[error("Invalid {0}")] + InvalidParameter(Parameter), + + #[error("Unknown error: {0}")] + Unknown(String), +} + +#[allow(clippy::needless_pass_by_value)] +fn into_string(val: impl ToString) -> String { + val.to_string() +} + +impl Error { + pub fn res_status_check(actual: T, expected: T) -> Result<()> + where + T: PartialEq + fmt::Display + Copy, + { + if actual == expected { + Ok(()) + } else { + Err(Self::Response(format!( + "unexpected status: {actual} (expected {expected})" + ))) + } + } + + pub fn response(e: impl ToString) -> Self { + Self::Response(into_string(e)) + } + + pub fn unknown(e: impl ToString) -> Self { + Self::Unknown(into_string(e)) + } + + #[must_use] + pub fn is_fatal(&self) -> bool { + match self { + Self::InvalidParameter(p) => p.is_fatal(), + Self::Unknown(_) => true, + _ => false, + } + } +} + +pub type Result = std::result::Result; diff --git a/src/file/checked.rs b/src/file/checked.rs index 58bdf79..c80535b 100644 --- a/src/file/checked.rs +++ b/src/file/checked.rs @@ -5,7 +5,7 @@ use std::{ use serde::{Deserialize, Serialize}; -use crate::sharry; +use crate::{error, sharry}; use super::{FileTrait, Uploading}; @@ -59,7 +59,7 @@ impl Checked { uri: &sharry::Uri, alias_id: &str, share_id: &str, - ) -> sharry::Result { + ) -> error::Result { let file_id = client.file_create(uri, alias_id, share_id, &self)?; Ok(Uploading::new(self.path, self.size, file_id)) diff --git a/src/impl_ureq.rs b/src/impl_ureq.rs index 2047a54..1ffd188 100644 --- a/src/impl_ureq.rs +++ b/src/impl_ureq.rs @@ -1,8 +1,9 @@ use log::{debug, trace}; use crate::{ + error, file::{self, FileTrait}, - sharry::{self, ClientError, Uri}, + sharry::{self, Uri}, }; fn find_cause( @@ -10,22 +11,22 @@ fn find_cause( alias_id: &str, share_id: Option<&str>, file_id: Option<&str>, -) -> impl FnOnce(ureq::Error) -> ClientError { +) -> impl FnOnce(ureq::Error) -> error::Error { move |error| match error { ureq::Error::StatusCode(403) => { trace!("HTTP Error 403: Alias not found!"); - ClientError::InvalidParameter(sharry::Parameter::AliasID(alias_id.to_owned())) + error::Error::InvalidParameter(error::Parameter::AliasID(alias_id.to_owned())) } ureq::Error::StatusCode(404) => { trace!("HTTP Error 404: Share and/or file may have been deleted!"); if let Some(file_id) = file_id { - ClientError::InvalidParameter(sharry::Parameter::FileID(file_id.to_owned())) + error::Error::InvalidParameter(error::Parameter::FileID(file_id.to_owned())) } else if let Some(share_id) = share_id { - ClientError::InvalidParameter(sharry::Parameter::ShareID(share_id.to_owned())) + error::Error::InvalidParameter(error::Parameter::ShareID(share_id.to_owned())) } else { - ClientError::InvalidParameter(sharry::Parameter::Uri(uri.to_string())) + error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string())) } } ureq::Error::Io(error) => { @@ -33,7 +34,7 @@ fn find_cause( if let Some(msg) = error.get_ref().map(ToString::to_string) { if msg.starts_with("failed to lookup address information") { - ClientError::InvalidParameter(sharry::Parameter::Uri(uri.to_string())) + error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string())) } else { error.into() } @@ -41,7 +42,7 @@ fn find_cause( error.into() } } - error => ClientError::unknown(error), + error => error::Error::unknown(error), } } @@ -51,7 +52,7 @@ impl sharry::Client for ureq::Agent { uri: &Uri, alias_id: &str, data: sharry::NewShareRequest, - ) -> sharry::Result { + ) -> error::Result { let res = { let endpoint = uri.share_create(); @@ -62,11 +63,11 @@ impl sharry::Client for ureq::Agent { .map_err(find_cause(uri, alias_id, None, None))?; trace!("{endpoint:?} response: {res:?}"); - ClientError::res_status_check(res.status(), ureq::http::StatusCode::OK)?; + error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?; res.body_mut() .read_json::() - .map_err(ClientError::response)? + .map_err(error::Error::response)? }; debug!("{res:?}"); @@ -76,11 +77,11 @@ impl sharry::Client for ureq::Agent { Ok(res.id) } else { - Err(ClientError::response(format!("{res:?}"))) + Err(error::Error::response(format!("{res:?}"))) } } - fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> sharry::Result<()> { + fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> error::Result<()> { let res = { let endpoint = uri.share_notify(share_id); @@ -91,11 +92,11 @@ impl sharry::Client for ureq::Agent { .map_err(find_cause(uri, alias_id, Some(share_id), None))?; trace!("{endpoint:?} response: {res:?}"); - ClientError::res_status_check(res.status(), ureq::http::StatusCode::OK)?; + error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?; res.body_mut() .read_json::() - .map_err(ClientError::response)? + .map_err(error::Error::response)? }; debug!("{res:?}"); @@ -109,7 +110,7 @@ impl sharry::Client for ureq::Agent { alias_id: &str, share_id: &str, file: &file::Checked, - ) -> sharry::Result { + ) -> error::Result { let res = { let endpoint = uri.file_create(share_id); @@ -122,14 +123,14 @@ impl sharry::Client for ureq::Agent { .map_err(find_cause(uri, alias_id, Some(share_id), None))?; trace!("{endpoint:?} response: {res:?}"); - ClientError::res_status_check(res.status(), ureq::http::StatusCode::CREATED)?; + error::Error::res_status_check(res.status(), ureq::http::StatusCode::CREATED)?; res }; let location = (res.headers().get("Location")) - .ok_or_else(|| ClientError::response("Location header not found"))? + .ok_or_else(|| error::Error::response("Location header not found"))? .to_str() - .map_err(ClientError::response)? + .map_err(error::Error::response)? .to_string(); let file_id = Self::get_file_id(&location)?; @@ -145,7 +146,7 @@ impl sharry::Client for ureq::Agent { alias_id: &str, share_id: &str, chunk: &file::Chunk, - ) -> sharry::Result<()> { + ) -> error::Result<()> { let res = { let endpoint = uri.file_patch(share_id, chunk.get_file_id()); @@ -162,21 +163,21 @@ impl sharry::Client for ureq::Agent { ))?; trace!("{endpoint:?} response: {res:?}"); - ClientError::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?; + error::Error::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?; res }; let res_offset = (res.headers().get("Upload-Offset")) - .ok_or_else(|| ClientError::response("Upload-Offset header not found"))? + .ok_or_else(|| error::Error::response("Upload-Offset header not found"))? .to_str() - .map_err(ClientError::response)? + .map_err(error::Error::response)? .parse::() - .map_err(ClientError::response)?; + .map_err(error::Error::response)?; if chunk.get_behind() == res_offset { Ok(()) } else { - Err(ClientError::response(format!( + Err(error::Error::response(format!( "Unexpected Upload-Offset: {} (expected {})", res_offset, chunk.get_behind() diff --git a/src/lib.rs b/src/lib.rs index 27e5322..d28d9f2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,6 +4,7 @@ mod appstate; mod cachefile; mod cli; +pub mod error; mod file; mod impl_ureq; pub mod output; @@ -11,5 +12,3 @@ mod sharry; pub use appstate::AppState; pub use cli::Cli; -pub use output::{Log, SHRUPL}; -pub use sharry::{ClientError, Parameter}; diff --git a/src/output.rs b/src/output.rs index db164bd..ccf0d73 100644 --- a/src/output.rs +++ b/src/output.rs @@ -4,13 +4,12 @@ use console::{StyledObject, style}; use dialoguer::{Select, theme::ColorfulTheme}; use log::{error, info}; -use crate::sharry; - type StaticStyled<'t> = LazyLock>; pub static SHRUPL: StaticStyled = LazyLock::new(|| style("ShrUpl").yellow().bold()); -#[must_use] pub fn prompt_continue() -> bool { +#[must_use] +pub fn prompt_continue() -> bool { let prompt = format!( "This operation has previously been stopped. {}", style("How to proceed?").cyan() @@ -59,7 +58,7 @@ impl Log { process::exit(1); } - pub fn handle(e: &sharry::ClientError) { + pub fn handle(e: &crate::error::Error) { if e.is_fatal() { // react to fatal error error!("fatal error: {e:?}"); diff --git a/src/sharry/client.rs b/src/sharry/client.rs index 0469710..517a40a 100644 --- a/src/sharry/client.rs +++ b/src/sharry/client.rs @@ -1,15 +1,14 @@ -use std::{fmt, sync::LazyLock}; +use std::sync::LazyLock; use log::trace; use regex::Regex; -use thiserror::Error; -use crate::file; +use crate::{error, file}; use super::api::{NewShareRequest, Uri}; pub trait Client { - fn get_file_id(uri: &str) -> super::Result<&str> { + fn get_file_id(uri: &str) -> error::Result<&str> { /// Pattern breakdown: /// - `^([^:/?#]+)://` – scheme (anything but `:/?#`) + `"://"` /// - `([^/?#]+)` – authority/host (anything but `/?#`) @@ -33,7 +32,7 @@ pub trait Client { { Ok(fid) } else { - Err(super::ClientError::unknown(format!( + Err(error::Error::unknown(format!( "Could not extract File ID from {uri:?}" ))) } @@ -44,9 +43,9 @@ pub trait Client { uri: &Uri, alias_id: &str, data: NewShareRequest, - ) -> super::Result; + ) -> error::Result; - fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> super::Result<()>; + fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> error::Result<()>; fn file_create( &self, @@ -54,7 +53,7 @@ pub trait Client { alias_id: &str, share_id: &str, file: &file::Checked, - ) -> super::Result; + ) -> error::Result; fn file_patch( &self, @@ -62,7 +61,7 @@ pub trait Client { alias_id: &str, share_id: &str, chunk: &file::Chunk, - ) -> super::Result<()>; + ) -> error::Result<()>; } // TODO move into tests subdir @@ -85,75 +84,3 @@ pub trait Client { // assert!(Client::get_file_id(bad).is_err()); // } // } - -#[derive(Debug, Error)] -pub enum Parameter { - #[error("given URI {0:?}")] - Uri(String), - - #[error("given Alias ID {0:?}")] - AliasID(String), - - #[error("stored Share ID {0:?}")] - ShareID(String), - - #[error("stored File ID {0:?}")] - FileID(String), -} - -impl Parameter { - fn is_fatal(&self) -> bool { - matches!(self, Self::Uri(_) | Self::AliasID(_)) - } -} - -#[derive(Debug, Error)] -pub enum ClientError { - #[error(transparent)] - StdIo(#[from] std::io::Error), - - #[error("response error: {0}")] - Response(String), - - #[error("Invalid {0}")] - InvalidParameter(Parameter), - - #[error("Unknown error: {0}")] - Unknown(String), -} - -#[allow(clippy::needless_pass_by_value)] -fn into_string(val: impl ToString) -> String { - val.to_string() -} - -impl ClientError { - pub fn res_status_check(actual: T, expected: T) -> super::Result<()> - where - T: PartialEq + fmt::Display + Copy, - { - if actual == expected { - Ok(()) - } else { - Err(Self::Response(format!( - "unexpected status: {actual} (expected {expected})" - ))) - } - } - - pub fn response(e: impl ToString) -> Self { - Self::Response(into_string(e)) - } - - pub fn unknown(e: impl ToString) -> Self { - Self::Unknown(into_string(e)) - } - - #[must_use] pub fn is_fatal(&self) -> bool { - match self { - Self::InvalidParameter(p) => p.is_fatal(), - Self::Unknown(_) => true, - _ => false, - } - } -} diff --git a/src/sharry/mod.rs b/src/sharry/mod.rs index 87a65f5..5da85d2 100644 --- a/src/sharry/mod.rs +++ b/src/sharry/mod.rs @@ -2,6 +2,4 @@ mod api; mod client; pub use api::{NewShareRequest, NewShareResponse, NotifyShareResponse, Uri}; -pub use client::{Client, ClientError, Parameter}; - -pub type Result = std::result::Result; +pub use client::Client;