[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::{
|
use super::{
|
||||||
cli::Cli,
|
cli::Cli,
|
||||||
sharry::{Alias, ChunkState, FileChecked, FileUploading, Share, SharryFile, UploadError},
|
sharry::{
|
||||||
|
ChunkState, Client, ClientError, FileChecked, FileUploading, SharryFile, UploadError, Uri,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
|
@ -23,8 +25,9 @@ pub struct AppState {
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
progress: Option<ProgressBar>,
|
progress: Option<ProgressBar>,
|
||||||
|
|
||||||
alias: Alias,
|
uri: Uri,
|
||||||
share: Share,
|
alias_id: String,
|
||||||
|
share_id: String,
|
||||||
files: VecDeque<FileState>,
|
files: VecDeque<FileState>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -88,27 +91,30 @@ impl AppState {
|
||||||
Self {
|
Self {
|
||||||
file_name,
|
file_name,
|
||||||
progress: None,
|
progress: None,
|
||||||
alias: state.alias,
|
uri: state.uri,
|
||||||
share: state.share,
|
alias_id: state.alias_id,
|
||||||
|
share_id: state.share_id,
|
||||||
files: state.files,
|
files: state.files,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.ok()
|
.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 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();
|
let files: VecDeque<_> = args.files.clone().into_iter().map(FileState::C).collect();
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
file_name,
|
file_name,
|
||||||
progress: None,
|
progress: None,
|
||||||
alias,
|
uri,
|
||||||
share,
|
alias_id,
|
||||||
|
share_id,
|
||||||
files,
|
files,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
@ -123,7 +129,9 @@ impl AppState {
|
||||||
chunk_size: usize,
|
chunk_size: usize,
|
||||||
) -> Result<Option<()>, UploadError> {
|
) -> Result<Option<()>, UploadError> {
|
||||||
let uploading = if let Some(state) = self.files.pop_front() {
|
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 {
|
} else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
@ -152,7 +160,7 @@ impl AppState {
|
||||||
bar
|
bar
|
||||||
});
|
});
|
||||||
|
|
||||||
match uploading.upload_chunk(http, &self.alias, chunk_size) {
|
match uploading.upload_chunk(http, &self.alias_id, chunk_size) {
|
||||||
ChunkState::Ok(upl) => {
|
ChunkState::Ok(upl) => {
|
||||||
bar.set_position(upl.get_offset());
|
bar.set_position(upl.get_offset());
|
||||||
self.files.push_front(FileState::U(upl));
|
self.files.push_front(FileState::U(upl));
|
||||||
|
|
@ -166,7 +174,7 @@ impl AppState {
|
||||||
debug!("Finished {:?}!", path.display());
|
debug!("Finished {:?}!", path.display());
|
||||||
bar.finish();
|
bar.finish();
|
||||||
self.progress = None;
|
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))
|
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 clap::{Parser, builder::PossibleValuesParser};
|
||||||
|
|
||||||
use super::sharry::{Alias, FileChecked, NewShareRequest, Uri};
|
use super::sharry::{FileChecked, NewShareRequest, Uri};
|
||||||
|
|
||||||
#[derive(Parser, Debug, Hash)]
|
#[derive(Parser, Debug, Hash)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
|
@ -46,7 +46,7 @@ pub struct Cli {
|
||||||
url: String,
|
url: String,
|
||||||
|
|
||||||
/// ID of a public alias to use
|
/// ID of a public alias to use
|
||||||
alias: String,
|
pub alias: String,
|
||||||
|
|
||||||
/// Files to upload to the new share
|
/// Files to upload to the new share
|
||||||
#[arg(value_name = "FILE", required = true, value_parser = parse_sharry_file)]
|
#[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)
|
(!self.timeout.is_zero()).then_some(self.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_alias(&self) -> Alias {
|
pub fn get_uri(&self) -> Uri {
|
||||||
Alias::new(Uri::with_protocol(&self.protocol, &self.url), &self.alias)
|
Uri::with_protocol(&self.protocol, &self.url)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_share_request(&self) -> NewShareRequest {
|
pub fn get_share_request(&self) -> NewShareRequest {
|
||||||
|
|
@ -83,7 +83,7 @@ impl Cli {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut hasher = DefaultHasher::new();
|
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())
|
format!("{:x}", hasher.finish())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,7 @@ use ureq::Agent;
|
||||||
|
|
||||||
use appstate::AppState;
|
use appstate::AppState;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
|
use sharry::ClientError;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
println!(
|
println!(
|
||||||
|
|
@ -64,8 +65,11 @@ fn main() {
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(cause) = match e {
|
if let Some(cause) = match e {
|
||||||
ureq::Error::StatusCode(403) => Some("Alias ID"),
|
ClientError::ResponseStatus {
|
||||||
ureq::Error::Io(_) => Some("URL"),
|
actual: _,
|
||||||
|
expected: 403,
|
||||||
|
} => Some("Alias ID"),
|
||||||
|
ClientError::FileIO(_) => Some("URL"),
|
||||||
_ => None,
|
_ => None,
|
||||||
} {
|
} {
|
||||||
info!("handling error: {e:?}");
|
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 log::debug;
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
api::{NewShareRequest, NewShareResponse, NotifyShareResponse, Uri},
|
api::{NewShareRequest, NewShareResponse, NotifyShareResponse, Uri},
|
||||||
|
|
@ -33,7 +34,7 @@ pub trait Client {
|
||||||
// fn sharry_file_patch(&self);
|
// fn sharry_file_patch(&self);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, thiserror::Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
#[error("file I/O error: {0}")]
|
#[error("file I/O error: {0}")]
|
||||||
FileIO(#[from] io::Error),
|
FileIO(#[from] io::Error),
|
||||||
|
|
@ -49,9 +50,20 @@ pub enum ClientError {
|
||||||
|
|
||||||
#[error("unexpected response content: {0}")]
|
#[error("unexpected response content: {0}")]
|
||||||
ResponseContent(String),
|
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 {
|
impl Client for ureq::Agent {
|
||||||
|
|
@ -67,10 +79,10 @@ impl Client for ureq::Agent {
|
||||||
self.post(endpoint)
|
self.post(endpoint)
|
||||||
.header("Sharry-Alias", alias_id)
|
.header("Sharry-Alias", alias_id)
|
||||||
.send_json(data)
|
.send_json(data)
|
||||||
.map_err(|e| ClientError::Request(e.to_string()))?
|
.map_err(|e| ClientError::req_err(e))?
|
||||||
.body_mut()
|
.body_mut()
|
||||||
.read_json::<NewShareResponse>()
|
.read_json::<NewShareResponse>()
|
||||||
.map_err(|e| ClientError::ResponseParsing(e.to_string()))?
|
.map_err(|e| ClientError::res_parse_err(e))?
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("response: {res:?}");
|
debug!("response: {res:?}");
|
||||||
|
|
@ -78,7 +90,7 @@ impl Client for ureq::Agent {
|
||||||
if res.success && (res.message == "Share created.") {
|
if res.success && (res.message == "Share created.") {
|
||||||
Ok(res.id)
|
Ok(res.id)
|
||||||
} else {
|
} 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)
|
self.post(endpoint)
|
||||||
.header("Sharry-Alias", alias_id)
|
.header("Sharry-Alias", alias_id)
|
||||||
.send_empty()
|
.send_empty()
|
||||||
.map_err(|e| ClientError::Request(e.to_string()))?
|
.map_err(|e| ClientError::req_err(e))?
|
||||||
.body_mut()
|
.body_mut()
|
||||||
.read_json::<NotifyShareResponse>()
|
.read_json::<NotifyShareResponse>()
|
||||||
.map_err(|e| ClientError::ResponseParsing(e.to_string()))?
|
.map_err(|e| ClientError::res_parse_err(e))?
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("response: {res:?}");
|
debug!("response: {res:?}");
|
||||||
|
|
@ -122,7 +134,7 @@ impl Client for ureq::Agent {
|
||||||
.header("Sharry-File-Name", file.get_name())
|
.header("Sharry-File-Name", file.get_name())
|
||||||
.header("Upload-Length", size)
|
.header("Upload-Length", size)
|
||||||
.send_empty()
|
.send_empty()
|
||||||
.map_err(|e| ClientError::Request(e.to_string()))?
|
.map_err(|e| ClientError::req_err(e))?
|
||||||
};
|
};
|
||||||
|
|
||||||
if res.status() != ureq::http::StatusCode::CREATED {
|
if res.status() != ureq::http::StatusCode::CREATED {
|
||||||
|
|
@ -133,9 +145,9 @@ impl Client for ureq::Agent {
|
||||||
}
|
}
|
||||||
|
|
||||||
let location = (res.headers().get("Location"))
|
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()
|
.to_str()
|
||||||
.map_err(|_| ClientError::ResponseParsing("Location header invalid".to_owned()))?
|
.map_err(|e| ClientError::res_parse_err(e))?
|
||||||
.to_string();
|
.to_string();
|
||||||
|
|
||||||
debug!("patch uri: {location}");
|
debug!("patch uri: {location}");
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ureq::http::{HeaderValue, StatusCode};
|
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)]
|
#[derive(Debug, Clone, Hash, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct FileChecked {
|
pub struct FileChecked {
|
||||||
|
|
|
||||||
|
|
@ -9,8 +9,6 @@ use std::{
|
||||||
pub use checked::FileChecked;
|
pub use checked::FileChecked;
|
||||||
pub use uploading::{ChunkState, FileUploading, UploadError};
|
pub use uploading::{ChunkState, FileUploading, UploadError};
|
||||||
|
|
||||||
use super::{Alias, Share, alias::SharryAlias};
|
|
||||||
|
|
||||||
pub trait SharryFile<'t> {
|
pub trait SharryFile<'t> {
|
||||||
/// extract the filename part of a `Path` reference
|
/// extract the filename part of a `Path` reference
|
||||||
///
|
///
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ use log::debug;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use ureq::http::{HeaderValue, StatusCode};
|
use ureq::http::{HeaderValue, StatusCode};
|
||||||
|
|
||||||
use super::{Alias, SharryAlias, SharryFile};
|
use super::SharryFile;
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
pub struct FileUploading {
|
pub struct FileUploading {
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,9 @@
|
||||||
#![allow(unused_imports)]
|
#![allow(unused_imports)]
|
||||||
|
|
||||||
mod alias;
|
|
||||||
mod api;
|
mod api;
|
||||||
mod client;
|
mod client;
|
||||||
mod file;
|
mod file;
|
||||||
mod share;
|
|
||||||
|
|
||||||
pub use alias::Alias;
|
|
||||||
pub use api::{NewShareRequest, Uri};
|
pub use api::{NewShareRequest, Uri};
|
||||||
// pub use client::SharryClient;
|
pub use client::{Client, ClientError};
|
||||||
pub use file::{ChunkState, FileChecked, FileUploading, SharryFile, UploadError};
|
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