diff --git a/src/appstate.rs b/src/appstate.rs index f3bcbcf..1c0d1f2 100644 --- a/src/appstate.rs +++ b/src/appstate.rs @@ -13,7 +13,9 @@ use serde::{Deserialize, Serialize}; use super::{ cli::Cli, - sharry::{Alias, ChunkState, FileChecked, FileUploading, Share, SharryFile, UploadError}, + sharry::{ + ChunkState, Client, ClientError, FileChecked, FileUploading, SharryFile, UploadError, Uri, + }, }; #[derive(Serialize, Deserialize, Debug)] @@ -23,8 +25,9 @@ pub struct AppState { #[serde(skip)] progress: Option, - alias: Alias, - share: Share, + uri: Uri, + alias_id: String, + share_id: String, files: VecDeque, } @@ -88,27 +91,30 @@ impl AppState { Self { file_name, progress: None, - alias: state.alias, - share: state.share, + uri: state.uri, + alias_id: state.alias_id, + share_id: state.share_id, files: state.files, } }) .ok() } - pub fn from_args(args: &Cli, http: &ureq::Agent) -> Result { + pub fn from_args(args: &Cli, http: &impl Client) -> Result { let file_name = Self::cache_file(args); - let alias = args.get_alias(); - let share = Share::create(http, &alias, args.get_share_request())?; + let uri = args.get_uri(); + let alias_id = args.alias.clone(); + let share_id = http.sharry_share_create(&uri, &alias_id, args.get_share_request())?; let files: VecDeque<_> = args.files.clone().into_iter().map(FileState::C).collect(); Ok(Self { file_name, progress: None, - alias, - share, + uri, + alias_id, + share_id, files, }) } @@ -123,7 +129,9 @@ impl AppState { chunk_size: usize, ) -> Result, UploadError> { let uploading = if let Some(state) = self.files.pop_front() { - state.start_upload(http, &self.alias, &self.share).unwrap() // HACK unwrap + state + .start_upload(http, &self.alias_id, &self.share_id) + .unwrap() // HACK unwrap } else { return Ok(None); }; @@ -152,7 +160,7 @@ impl AppState { bar }); - match uploading.upload_chunk(http, &self.alias, chunk_size) { + match uploading.upload_chunk(http, &self.alias_id, chunk_size) { ChunkState::Ok(upl) => { bar.set_position(upl.get_offset()); self.files.push_front(FileState::U(upl)); @@ -166,7 +174,7 @@ impl AppState { debug!("Finished {:?}!", path.display()); bar.finish(); self.progress = None; - self.share.notify(http, &self.alias).unwrap(); // HACK unwrap + self.share_id.notify(http, &self.alias_id).unwrap(); // HACK unwrap Ok(self.files.front().map(drop)) } diff --git a/src/cli.rs b/src/cli.rs index 6e96e3b..5e853d1 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -5,7 +5,7 @@ use std::{ use clap::{Parser, builder::PossibleValuesParser}; -use super::sharry::{Alias, FileChecked, NewShareRequest, Uri}; +use super::sharry::{FileChecked, NewShareRequest, Uri}; #[derive(Parser, Debug, Hash)] #[command(version, about, long_about = None)] @@ -46,7 +46,7 @@ pub struct Cli { url: String, /// ID of a public alias to use - alias: String, + pub alias: String, /// Files to upload to the new share #[arg(value_name = "FILE", required = true, value_parser = parse_sharry_file)] @@ -66,8 +66,8 @@ impl Cli { (!self.timeout.is_zero()).then_some(self.timeout) } - pub fn get_alias(&self) -> Alias { - Alias::new(Uri::with_protocol(&self.protocol, &self.url), &self.alias) + pub fn get_uri(&self) -> Uri { + Uri::with_protocol(&self.protocol, &self.url) } pub fn get_share_request(&self) -> NewShareRequest { @@ -83,7 +83,7 @@ impl Cli { }; let mut hasher = DefaultHasher::new(); - (self.get_alias(), file_refs).hash(&mut hasher); + (self.get_uri(), &self.alias, file_refs).hash(&mut hasher); format!("{:x}", hasher.finish()) } diff --git a/src/main.rs b/src/main.rs index 7e17e70..10379c5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -18,6 +18,7 @@ use ureq::Agent; use appstate::AppState; use cli::Cli; +use sharry::ClientError; fn main() { println!( @@ -64,8 +65,11 @@ fn main() { } Err(e) => { if let Some(cause) = match e { - ureq::Error::StatusCode(403) => Some("Alias ID"), - ureq::Error::Io(_) => Some("URL"), + ClientError::ResponseStatus { + actual: _, + expected: 403, + } => Some("Alias ID"), + ClientError::FileIO(_) => Some("URL"), _ => None, } { info!("handling error: {e:?}"); diff --git a/src/sharry/alias.rs b/src/sharry/alias.rs deleted file mode 100644 index ba51bce..0000000 --- a/src/sharry/alias.rs +++ /dev/null @@ -1,36 +0,0 @@ -use std::fmt::{Debug, Display}; - -use log::debug; -use serde::{Deserialize, Serialize}; -use ureq::RequestBuilder; - -use super::api::Uri; - -#[derive(Serialize, Deserialize, Debug, Hash)] -pub struct Alias { - pub(super) uri: Uri, - pub(super) id: String, -} - -pub(super) trait SharryAlias { - fn sharry_header(self, alias: &Alias) -> Self; -} - -impl SharryAlias for RequestBuilder { - fn sharry_header(self, alias: &Alias) -> Self { - self.header("Sharry-Alias", &alias.id) - } -} - -impl Alias { - pub fn new(uri: Uri, id: impl Into) -> Self { - Self { uri, id: id.into() } - } - - pub(super) fn get_endpoint(&self, endpoint: impl Display + Debug) -> String { - let uri = format!("{}/{}", self.uri, endpoint); - debug!("endpoint uri: {uri:?}"); - - uri - } -} diff --git a/src/sharry/client.rs b/src/sharry/client.rs index 85951ee..1adcb5a 100644 --- a/src/sharry/client.rs +++ b/src/sharry/client.rs @@ -1,6 +1,7 @@ -use std::{error::Error, io}; +use std::{error::Error, fmt::Display, io}; use log::debug; +use thiserror::Error; use super::{ api::{NewShareRequest, NewShareResponse, NotifyShareResponse, Uri}, @@ -33,7 +34,7 @@ pub trait Client { // fn sharry_file_patch(&self); } -#[derive(Debug, thiserror::Error)] +#[derive(Debug, Error)] pub enum ClientError { #[error("file I/O error: {0}")] FileIO(#[from] io::Error), @@ -49,9 +50,20 @@ pub enum ClientError { #[error("unexpected response content: {0}")] ResponseContent(String), - // - // #[error("could not parse offset header")] - // ResponseOffset, +} + +impl ClientError { + fn req_err(msg: impl Display) -> Self { + ClientError::Request(msg.to_string()) + } + + fn res_parse_err(msg: impl Display) -> Self { + ClientError::ResponseParsing(msg.to_string()) + } + + fn res_content_err(msg: impl Display) -> Self { + ClientError::ResponseContent(msg.to_string()) + } } impl Client for ureq::Agent { @@ -67,10 +79,10 @@ impl Client for ureq::Agent { self.post(endpoint) .header("Sharry-Alias", alias_id) .send_json(data) - .map_err(|e| ClientError::Request(e.to_string()))? + .map_err(|e| ClientError::req_err(e))? .body_mut() .read_json::() - .map_err(|e| ClientError::ResponseParsing(e.to_string()))? + .map_err(|e| ClientError::res_parse_err(e))? }; debug!("response: {res:?}"); @@ -78,7 +90,7 @@ impl Client for ureq::Agent { if res.success && (res.message == "Share created.") { Ok(res.id) } else { - Err(ClientError::ResponseContent(format!("{res:?}"))) + Err(ClientError::res_content_err(format!("{res:?}"))) } } @@ -94,10 +106,10 @@ impl Client for ureq::Agent { self.post(endpoint) .header("Sharry-Alias", alias_id) .send_empty() - .map_err(|e| ClientError::Request(e.to_string()))? + .map_err(|e| ClientError::req_err(e))? .body_mut() .read_json::() - .map_err(|e| ClientError::ResponseParsing(e.to_string()))? + .map_err(|e| ClientError::res_parse_err(e))? }; debug!("response: {res:?}"); @@ -122,7 +134,7 @@ impl Client for ureq::Agent { .header("Sharry-File-Name", file.get_name()) .header("Upload-Length", size) .send_empty() - .map_err(|e| ClientError::Request(e.to_string()))? + .map_err(|e| ClientError::req_err(e))? }; if res.status() != ureq::http::StatusCode::CREATED { @@ -133,9 +145,9 @@ impl Client for ureq::Agent { } let location = (res.headers().get("Location")) - .ok_or_else(|| ClientError::ResponseParsing("Location header not found".to_owned()))? + .ok_or_else(|| ClientError::res_parse_err("Location header not found"))? .to_str() - .map_err(|_| ClientError::ResponseParsing("Location header invalid".to_owned()))? + .map_err(|e| ClientError::res_parse_err(e))? .to_string(); debug!("patch uri: {location}"); diff --git a/src/sharry/file/checked.rs b/src/sharry/file/checked.rs index e31adc9..4a77c23 100644 --- a/src/sharry/file/checked.rs +++ b/src/sharry/file/checked.rs @@ -8,7 +8,7 @@ use log::debug; use serde::{Deserialize, Serialize}; use ureq::http::{HeaderValue, StatusCode}; -use super::{Alias, FileUploading, Share, SharryAlias, SharryFile}; +use super::{FileUploading, SharryFile}; #[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)] pub struct FileChecked { diff --git a/src/sharry/file/mod.rs b/src/sharry/file/mod.rs index c3e4502..49b715d 100644 --- a/src/sharry/file/mod.rs +++ b/src/sharry/file/mod.rs @@ -9,8 +9,6 @@ use std::{ pub use checked::FileChecked; pub use uploading::{ChunkState, FileUploading, UploadError}; -use super::{Alias, Share, alias::SharryAlias}; - pub trait SharryFile<'t> { /// extract the filename part of a `Path` reference /// diff --git a/src/sharry/file/uploading.rs b/src/sharry/file/uploading.rs index 15fa3d9..608a7f3 100644 --- a/src/sharry/file/uploading.rs +++ b/src/sharry/file/uploading.rs @@ -9,7 +9,7 @@ use log::debug; use serde::{Deserialize, Serialize}; use ureq::http::{HeaderValue, StatusCode}; -use super::{Alias, SharryAlias, SharryFile}; +use super::SharryFile; #[derive(Serialize, Deserialize, Debug)] pub struct FileUploading { diff --git a/src/sharry/mod.rs b/src/sharry/mod.rs index 05ce16e..36ed161 100644 --- a/src/sharry/mod.rs +++ b/src/sharry/mod.rs @@ -1,13 +1,9 @@ #![allow(unused_imports)] -mod alias; mod api; mod client; mod file; -mod share; -pub use alias::Alias; pub use api::{NewShareRequest, Uri}; -// pub use client::SharryClient; +pub use client::{Client, ClientError}; pub use file::{ChunkState, FileChecked, FileUploading, SharryFile, UploadError}; -pub use share::Share; diff --git a/src/sharry/share.rs b/src/sharry/share.rs deleted file mode 100644 index 0733bd4..0000000 --- a/src/sharry/share.rs +++ /dev/null @@ -1,48 +0,0 @@ -use log::debug; -use serde::{Deserialize, Serialize}; - -use super::{ - alias::{Alias, SharryAlias}, - api::{NewShareRequest, NewShareResponse, NotifyShareResponse}, -}; - -#[derive(Serialize, Deserialize, Debug)] -pub struct Share { - pub(super) id: String, -} - -impl Share { - pub fn create( - http: &ureq::Agent, - alias: &Alias, - data: NewShareRequest, - ) -> Result { - let res = (http.post(alias.get_endpoint("alias/upload/new"))) - .sharry_header(alias) - .send_json(data)? - .body_mut() - .read_json::()?; - - debug!("response: {res:?}"); - - if !(res.success && (res.message == "Share created.")) { - return Err(ureq::Error::Other("unexpected json response".into())); - } - - Ok(Self { id: res.id }) - } - - pub fn notify(&self, http: &ureq::Agent, alias: &Alias) -> Result<(), ureq::Error> { - let endpoint = alias.get_endpoint(format!("alias/mail/notify/{}", self.id)); - - let res = (http.post(endpoint)) - .sharry_header(alias) - .send_empty()? - .body_mut() - .read_json::()?; - - debug!("response: {res:?}"); - - Ok(()) - } -}