[wip] impl Client for ureq::Agent
- remove `sharry::alias` and `sharry::share` (reduced to IDs) - use `sharry_share_create` in `AppState`
This commit is contained in:
parent
90cecd015e
commit
09af480379
10 changed files with 60 additions and 126 deletions
|
|
@ -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<ProgressBar>,
|
||||
|
||||
alias: Alias,
|
||||
share: Share,
|
||||
uri: Uri,
|
||||
alias_id: String,
|
||||
share_id: String,
|
||||
files: VecDeque<FileState>,
|
||||
}
|
||||
|
||||
|
|
@ -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<Self, ureq::Error> {
|
||||
pub fn from_args(args: &Cli, http: &impl Client) -> Result<Self, ClientError> {
|
||||
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<Option<()>, 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))
|
||||
}
|
||||
|
|
|
|||
10
src/cli.rs
10
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())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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:?}");
|
||||
|
|
|
|||
|
|
@ -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<B> SharryAlias for RequestBuilder<B> {
|
||||
fn sharry_header(self, alias: &Alias) -> Self {
|
||||
self.header("Sharry-Alias", &alias.id)
|
||||
}
|
||||
}
|
||||
|
||||
impl Alias {
|
||||
pub fn new(uri: Uri, id: impl Into<String>) -> 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
|
||||
}
|
||||
}
|
||||
|
|
@ -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::<NewShareResponse>()
|
||||
.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::<NotifyShareResponse>()
|
||||
.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}");
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
///
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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<Self, ureq::Error> {
|
||||
let res = (http.post(alias.get_endpoint("alias/upload/new")))
|
||||
.sharry_header(alias)
|
||||
.send_json(data)?
|
||||
.body_mut()
|
||||
.read_json::<NewShareResponse>()?;
|
||||
|
||||
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::<NotifyShareResponse>()?;
|
||||
|
||||
debug!("response: {res:?}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue