Compare commits
2 commits
9b1f7f872c
...
fb06725f05
| Author | SHA1 | Date | |
|---|---|---|---|
| fb06725f05 | |||
| 67da081ef9 |
4 changed files with 206 additions and 151 deletions
146
src/appstate.rs
146
src/appstate.rs
|
|
@ -1,10 +1,4 @@
|
||||||
use std::{
|
use std::{fmt, io, path::PathBuf, time::Duration};
|
||||||
collections::VecDeque,
|
|
||||||
fmt, fs,
|
|
||||||
io::{self, Write},
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
time::Duration,
|
|
||||||
};
|
|
||||||
|
|
||||||
use console::style;
|
use console::style;
|
||||||
use indicatif::{ProgressBar, ProgressStyle};
|
use indicatif::{ProgressBar, ProgressStyle};
|
||||||
|
|
@ -13,68 +7,29 @@ use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::{
|
use super::{
|
||||||
cli::Cli,
|
cli::Cli,
|
||||||
file::{self, FileTrait},
|
file::FileTrait,
|
||||||
sharry::{self, Client, ClientError, Uri},
|
savedstate::SavedState,
|
||||||
|
sharry::{self, Client, ClientError},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize)]
|
#[derive(Serialize, Deserialize)]
|
||||||
pub struct AppState {
|
pub struct AppState {
|
||||||
#[serde(skip)]
|
|
||||||
file_name: PathBuf,
|
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
progress: Option<ProgressBar>,
|
progress: Option<ProgressBar>,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
buffer: Vec<u8>,
|
buffer: Vec<u8>,
|
||||||
|
|
||||||
uri: Uri,
|
inner: SavedState,
|
||||||
alias_id: String,
|
|
||||||
share_id: String,
|
|
||||||
files: VecDeque<FileState>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for AppState {
|
impl fmt::Debug for AppState {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
f.debug_struct("AppState")
|
f.debug_struct("AppState")
|
||||||
.field("file_name", &self.file_name)
|
.field("inner", &self.inner)
|
||||||
.field("uri", &self.uri)
|
|
||||||
.field("alias_id", &self.alias_id)
|
|
||||||
.field("share_id", &self.share_id)
|
|
||||||
.field("files", &self.files)
|
|
||||||
.finish()
|
.finish()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Serialize, Deserialize, Debug)]
|
|
||||||
enum FileState {
|
|
||||||
C(file::Checked),
|
|
||||||
U(file::Uploading),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FileState {
|
|
||||||
fn file_name(&self) -> &str {
|
|
||||||
match self {
|
|
||||||
FileState::C(checked) => checked.get_name(),
|
|
||||||
FileState::U(uploading) => uploading.get_name(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn start_upload(
|
|
||||||
self,
|
|
||||||
http: &impl Client,
|
|
||||||
uri: &Uri,
|
|
||||||
alias_id: &str,
|
|
||||||
share_id: &str,
|
|
||||||
) -> sharry::Result<file::Uploading> {
|
|
||||||
match self {
|
|
||||||
FileState::C(checked) => {
|
|
||||||
let endpoint = &uri.endpoint(format!("alias/upload/{share_id}/files/tus"));
|
|
||||||
checked.start_upload(http, endpoint, alias_id)
|
|
||||||
}
|
|
||||||
FileState::U(uploading) => Ok(uploading),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl AppState {
|
impl AppState {
|
||||||
fn cache_dir() -> PathBuf {
|
fn cache_dir() -> PathBuf {
|
||||||
let dir_name = dirs_next::cache_dir()
|
let dir_name = dirs_next::cache_dir()
|
||||||
|
|
@ -92,90 +47,55 @@ impl AppState {
|
||||||
file_name
|
file_name
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(
|
fn new(chunk_size: usize, inner: SavedState) -> Self {
|
||||||
file_name: PathBuf,
|
|
||||||
chunk_size: usize,
|
|
||||||
uri: Uri,
|
|
||||||
alias_id: String,
|
|
||||||
share_id: String,
|
|
||||||
files: VecDeque<FileState>,
|
|
||||||
) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
file_name,
|
|
||||||
progress: None,
|
progress: None,
|
||||||
buffer: vec![0; chunk_size * 1024 * 1024],
|
buffer: vec![0; chunk_size * 1024 * 1024],
|
||||||
uri,
|
inner,
|
||||||
alias_id,
|
|
||||||
share_id,
|
|
||||||
files,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn load(file_name: &Path, chunk_size: usize) -> io::Result<Self> {
|
|
||||||
let file = fs::File::open(file_name)?;
|
|
||||||
serde_json::from_reader(io::BufReader::new(file))
|
|
||||||
.map_err(io::Error::other)
|
|
||||||
.map(|state: Self| {
|
|
||||||
debug!("successfully loaded AppState");
|
|
||||||
|
|
||||||
Self::new(
|
|
||||||
file_name.to_owned(),
|
|
||||||
chunk_size,
|
|
||||||
state.uri,
|
|
||||||
state.alias_id,
|
|
||||||
state.share_id,
|
|
||||||
state.files,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn try_resume(args: &Cli) -> Option<Self> {
|
pub fn try_resume(args: &Cli) -> Option<Self> {
|
||||||
let file_name = Self::cache_file(args);
|
let file_name = Self::cache_file(args);
|
||||||
|
let inner = SavedState::load(&file_name)
|
||||||
Self::load(&file_name, args.chunk_size)
|
|
||||||
.inspect_err(|e| debug!("could not resume from {:?}: {e}", file_name.display()))
|
.inspect_err(|e| debug!("could not resume from {:?}: {e}", file_name.display()))
|
||||||
.ok()
|
.ok()?;
|
||||||
|
|
||||||
|
Some(Self::new(args.chunk_size, inner))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_args(args: &Cli, http: &impl Client) -> sharry::Result<Self> {
|
pub fn from_args(args: &Cli, http: &impl Client) -> sharry::Result<Self> {
|
||||||
let file_name = Self::cache_file(args);
|
|
||||||
|
|
||||||
let uri = args.get_uri();
|
let uri = args.get_uri();
|
||||||
let alias_id = args.alias.clone();
|
|
||||||
let share_id = http.share_create(
|
let share_id = http.share_create(
|
||||||
&uri.endpoint("alias/upload/new"),
|
&uri.endpoint("alias/upload/new"),
|
||||||
&alias_id,
|
&args.alias,
|
||||||
args.get_share_request(),
|
args.get_share_request(),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let files: VecDeque<_> = args.files.clone().into_iter().map(FileState::C).collect();
|
|
||||||
|
|
||||||
Ok(Self::new(
|
Ok(Self::new(
|
||||||
file_name,
|
|
||||||
args.chunk_size,
|
args.chunk_size,
|
||||||
uri,
|
SavedState::new(
|
||||||
alias_id,
|
Self::cache_file(&args),
|
||||||
share_id,
|
uri,
|
||||||
files,
|
args.alias.clone(),
|
||||||
|
share_id,
|
||||||
|
&args.files,
|
||||||
|
),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn file_names(&self) -> Vec<&str> {
|
pub fn file_names(&self) -> Vec<&str> {
|
||||||
self.files.iter().map(FileState::file_name).collect()
|
self.inner.file_names()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn upload_chunk(&mut self, http: &impl Client) -> sharry::Result<Option<()>> {
|
pub fn upload_chunk(&mut self, http: &impl Client) -> sharry::Result<Option<()>> {
|
||||||
let mut uploading = if let Some(state) = self.files.pop_front() {
|
let Some(mut uploading) = self.inner.pop_file(http) else {
|
||||||
state
|
|
||||||
.start_upload(http, &self.uri, &self.alias_id, &self.share_id)
|
|
||||||
.unwrap() // HACK unwrap
|
|
||||||
} else {
|
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("{uploading} chunk {}", self.buffer.len());
|
debug!("{uploading} chunk {}", self.buffer.len());
|
||||||
|
|
||||||
// Initialize or fetch the existing ProgressBar in one call:
|
// Initialize or fetch the existing ProgressBar
|
||||||
let bar = &*self.progress.get_or_insert_with(|| {
|
let bar = &*self.progress.get_or_insert_with(|| {
|
||||||
// Create a new bar with style
|
// Create a new bar with style
|
||||||
let bar = ProgressBar::new(uploading.get_size())
|
let bar = ProgressBar::new(uploading.get_size())
|
||||||
|
|
@ -199,7 +119,7 @@ impl AppState {
|
||||||
|
|
||||||
let chunk = uploading
|
let chunk = uploading
|
||||||
.read(&mut self.buffer)
|
.read(&mut self.buffer)
|
||||||
.map_err(ClientError::req_err)?;
|
.map_err(ClientError::from)?;
|
||||||
if chunk.get_length() == 0 {
|
if chunk.get_length() == 0 {
|
||||||
return Err(ClientError::req_err("wtf"));
|
return Err(ClientError::req_err("wtf"));
|
||||||
}
|
}
|
||||||
|
|
@ -214,7 +134,7 @@ impl AppState {
|
||||||
match uploading.check_eof() {
|
match uploading.check_eof() {
|
||||||
Ok(uploading) => {
|
Ok(uploading) => {
|
||||||
bar.set_position(uploading.get_offset());
|
bar.set_position(uploading.get_offset());
|
||||||
self.files.push_front(FileState::U(uploading));
|
self.inner.push_file(uploading);
|
||||||
Ok(Some(()))
|
Ok(Some(()))
|
||||||
}
|
}
|
||||||
Err(path) => {
|
Err(path) => {
|
||||||
|
|
@ -227,26 +147,16 @@ impl AppState {
|
||||||
.endpoint(format!("alias/mail/notify/{}", self.share_id));
|
.endpoint(format!("alias/mail/notify/{}", self.share_id));
|
||||||
http.share_notify(&endpoint, &self.alias_id).unwrap(); // HACK unwrap
|
http.share_notify(&endpoint, &self.alias_id).unwrap(); // HACK unwrap
|
||||||
|
|
||||||
Ok(self.files.front().map(drop))
|
Ok(self.inner.has_file().then_some(()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn save(&self) -> io::Result<()> {
|
pub fn save(&self) -> io::Result<()> {
|
||||||
fs::create_dir_all(Self::cache_dir())?;
|
self.inner.save()
|
||||||
|
|
||||||
let json = serde_json::to_string_pretty(self).map_err(io::Error::other)?;
|
|
||||||
let mut file = fs::File::create(&self.file_name)?;
|
|
||||||
file.write_all(json.as_bytes())?;
|
|
||||||
|
|
||||||
trace!("updated {:?}", self.file_name.display());
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn clear(self) -> io::Result<()> {
|
pub fn clear(self) -> io::Result<()> {
|
||||||
fs::remove_file(&self.file_name)?;
|
self.inner.clear()
|
||||||
|
|
||||||
trace!("removed {:?}", self.file_name.display());
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
60
src/main.rs
60
src/main.rs
|
|
@ -1,10 +1,11 @@
|
||||||
mod appstate;
|
mod appstate;
|
||||||
mod cli;
|
mod cli;
|
||||||
mod file;
|
mod file;
|
||||||
|
mod savedstate;
|
||||||
mod sharry;
|
mod sharry;
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
process::{self, exit},
|
process,
|
||||||
sync::{
|
sync::{
|
||||||
Arc,
|
Arc,
|
||||||
atomic::{AtomicBool, Ordering},
|
atomic::{AtomicBool, Ordering},
|
||||||
|
|
@ -21,6 +22,32 @@ use appstate::AppState;
|
||||||
use cli::Cli;
|
use cli::Cli;
|
||||||
use sharry::ClientError;
|
use sharry::ClientError;
|
||||||
|
|
||||||
|
fn print_error(e: &ClientError) {
|
||||||
|
if let Some(cause) = match e {
|
||||||
|
// known errors
|
||||||
|
ClientError::ResponseStatus {
|
||||||
|
actual: 403,
|
||||||
|
expected: _,
|
||||||
|
} => Some("Alias ID"),
|
||||||
|
ClientError::StdIo(_) => Some("URL"),
|
||||||
|
// unknown error
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
// handle known error
|
||||||
|
info!("known error: {e:?}");
|
||||||
|
println!(
|
||||||
|
"{} probably wrong: {}",
|
||||||
|
style("Error!").red().bold(),
|
||||||
|
style(cause).cyan(),
|
||||||
|
);
|
||||||
|
println!("{}", style(e.to_string()).yellow().italic());
|
||||||
|
} else {
|
||||||
|
// handle unknown error
|
||||||
|
error!("unknown error: {e} ({e:?})");
|
||||||
|
println!("{}", style("Unknown Error!").red().bold());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
env_logger::init();
|
env_logger::init();
|
||||||
|
|
||||||
|
|
@ -32,8 +59,8 @@ fn main() {
|
||||||
|
|
||||||
let check_ctrlc = {
|
let check_ctrlc = {
|
||||||
let stop = Arc::new(AtomicBool::new(false));
|
let stop = Arc::new(AtomicBool::new(false));
|
||||||
|
|
||||||
let stop_ctrlc = stop.clone();
|
let stop_ctrlc = stop.clone();
|
||||||
|
|
||||||
ctrlc::set_handler(move || {
|
ctrlc::set_handler(move || {
|
||||||
stop_ctrlc.store(true, Ordering::SeqCst);
|
stop_ctrlc.store(true, Ordering::SeqCst);
|
||||||
info!("stopping as soon as possible ...");
|
info!("stopping as soon as possible ...");
|
||||||
|
|
@ -42,7 +69,7 @@ fn main() {
|
||||||
|
|
||||||
move || {
|
move || {
|
||||||
if stop.load(Ordering::SeqCst) {
|
if stop.load(Ordering::SeqCst) {
|
||||||
process::exit(1);
|
process::exit(255);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -73,39 +100,20 @@ fn main() {
|
||||||
state
|
state
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let Some(cause) = match e {
|
print_error(&e);
|
||||||
ClientError::ResponseStatus {
|
process::exit(1);
|
||||||
actual: 403,
|
|
||||||
expected: _,
|
|
||||||
} => Some("Alias ID"),
|
|
||||||
// ClientError::FileIO(_) => Some("URL"),
|
|
||||||
_ => None,
|
|
||||||
} {
|
|
||||||
info!("handling error: {e:?}");
|
|
||||||
println!(
|
|
||||||
"{} probably wrong: {} – {:?}",
|
|
||||||
style("Error!").red().bold(),
|
|
||||||
style(cause).cyan(),
|
|
||||||
style(e.to_string()).yellow().italic()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
error!("unknown error: {e} – {e:?}");
|
|
||||||
println!("{}", style("Unknown Error!").red().bold());
|
|
||||||
}
|
|
||||||
|
|
||||||
exit(1);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
info!("continuing with state: {state:?}");
|
||||||
|
|
||||||
println!(
|
println!(
|
||||||
"{} uploading: {}",
|
"{} uploading: {}",
|
||||||
style("ShrUpl").yellow().bold(),
|
style("ShrUpl").yellow().bold(),
|
||||||
style(state.file_names().join(", ")).magenta(),
|
style(state.file_names().join(", ")).magenta(),
|
||||||
);
|
);
|
||||||
|
|
||||||
info!("continuing with state: {state:?}");
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match state.upload_chunk(&agent) {
|
match state.upload_chunk(&agent) {
|
||||||
Err(e) => error!("error: {e:?}"),
|
Err(e) => error!("error: {e:?}"),
|
||||||
|
|
|
||||||
133
src/savedstate.rs
Normal file
133
src/savedstate.rs
Normal file
|
|
@ -0,0 +1,133 @@
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
fs,
|
||||||
|
io::{self, Write},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
|
||||||
|
use log::trace;
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
file::{self, FileTrait},
|
||||||
|
sharry::{self, Client, Uri},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
enum FileState {
|
||||||
|
C(file::Checked),
|
||||||
|
U(file::Uploading),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FileState {
|
||||||
|
fn file_name(&self) -> &str {
|
||||||
|
match self {
|
||||||
|
FileState::C(c) => c.get_name(),
|
||||||
|
FileState::U(u) => u.get_name(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn start_upload(
|
||||||
|
self,
|
||||||
|
http: &impl Client,
|
||||||
|
uri: &Uri,
|
||||||
|
alias_id: &str,
|
||||||
|
share_id: &str,
|
||||||
|
) -> sharry::Result<file::Uploading> {
|
||||||
|
match self {
|
||||||
|
FileState::C(checked) => {
|
||||||
|
let endpoint = &uri.endpoint(format!("alias/upload/{share_id}/files/tus"));
|
||||||
|
checked.start_upload(http, endpoint, alias_id)
|
||||||
|
}
|
||||||
|
FileState::U(uploading) => Ok(uploading),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Serialize, Deserialize, Debug)]
|
||||||
|
pub struct SavedState {
|
||||||
|
#[serde(skip)]
|
||||||
|
file_name: PathBuf,
|
||||||
|
|
||||||
|
uri: Uri,
|
||||||
|
alias_id: String,
|
||||||
|
share_id: String,
|
||||||
|
files: VecDeque<FileState>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SavedState {
|
||||||
|
pub fn new(
|
||||||
|
file_name: PathBuf,
|
||||||
|
uri: Uri,
|
||||||
|
alias_id: String,
|
||||||
|
share_id: String,
|
||||||
|
files: &Vec<file::Checked>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
file_name,
|
||||||
|
uri,
|
||||||
|
alias_id,
|
||||||
|
share_id,
|
||||||
|
files: files.clone().into_iter().map(FileState::C).collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn load(file_name: &Path) -> io::Result<Self> {
|
||||||
|
let file = fs::File::open(file_name)?;
|
||||||
|
let state: Self =
|
||||||
|
serde_json::from_reader(io::BufReader::new(file)).map_err(io::Error::other)?;
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
file_name: file_name.to_owned(),
|
||||||
|
uri: state.uri,
|
||||||
|
alias_id: state.alias_id,
|
||||||
|
share_id: state.share_id,
|
||||||
|
files: state.files,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_names(&self) -> Vec<&str> {
|
||||||
|
self.files.iter().map(FileState::file_name).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn has_file(&self) -> bool {
|
||||||
|
!self.files.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn pop_file(&mut self, http: &impl Client) -> Option<file::Uploading> {
|
||||||
|
if let Some(state) = self.files.pop_front() {
|
||||||
|
Some(
|
||||||
|
state
|
||||||
|
.start_upload(http, &self.uri, &self.alias_id, &self.share_id)
|
||||||
|
.unwrap(),
|
||||||
|
) // HACK unwrap
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_file(&mut self, file: file::Uploading) {
|
||||||
|
self.files.push_front(FileState::U(file));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn save(&self) -> io::Result<()> {
|
||||||
|
let cache_dir = self.file_name.parent().ok_or_else(|| {
|
||||||
|
io::Error::other(format!("orphan file {:?}", self.file_name.display()))
|
||||||
|
})?;
|
||||||
|
fs::create_dir_all(cache_dir)?;
|
||||||
|
|
||||||
|
let json = serde_json::to_string_pretty(self).map_err(io::Error::other)?;
|
||||||
|
let mut file = fs::File::create(&self.file_name)?;
|
||||||
|
file.write_all(json.as_bytes())?;
|
||||||
|
|
||||||
|
trace!("updated {:?}", self.file_name.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn clear(self) -> io::Result<()> {
|
||||||
|
fs::remove_file(&self.file_name)?;
|
||||||
|
|
||||||
|
trace!("removed {:?}", self.file_name.display());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -26,15 +26,18 @@ pub trait Client {
|
||||||
|
|
||||||
#[derive(Debug, Error)]
|
#[derive(Debug, Error)]
|
||||||
pub enum ClientError {
|
pub enum ClientError {
|
||||||
|
#[error(transparent)]
|
||||||
|
StdIo(#[from] std::io::Error),
|
||||||
|
|
||||||
#[error("network request failed: {0}")]
|
#[error("network request failed: {0}")]
|
||||||
Request(String),
|
Request(String),
|
||||||
|
|
||||||
|
#[error("unexpected response status: {actual} (expected {expected})")]
|
||||||
|
ResponseStatus { actual: u16, expected: u16 },
|
||||||
|
|
||||||
#[error("response parsing failed: {0}")]
|
#[error("response parsing failed: {0}")]
|
||||||
ResponseParsing(String),
|
ResponseParsing(String),
|
||||||
|
|
||||||
#[error("unexpected response status: {actual} (expected {expected:?})")]
|
|
||||||
ResponseStatus { actual: u16, expected: Option<u16> },
|
|
||||||
|
|
||||||
#[error("unexpected response content: {0}")]
|
#[error("unexpected response content: {0}")]
|
||||||
ResponseContent(String),
|
ResponseContent(String),
|
||||||
}
|
}
|
||||||
|
|
@ -57,7 +60,7 @@ impl ClientError {
|
||||||
} else {
|
} else {
|
||||||
Err(Self::ResponseStatus {
|
Err(Self::ResponseStatus {
|
||||||
actual: actual.into(),
|
actual: actual.into(),
|
||||||
expected: Some(expected.into()),
|
expected: expected.into(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -66,11 +69,12 @@ impl ClientError {
|
||||||
impl From<ureq::Error> for ClientError {
|
impl From<ureq::Error> for ClientError {
|
||||||
fn from(value: ureq::Error) -> Self {
|
fn from(value: ureq::Error) -> Self {
|
||||||
match value {
|
match value {
|
||||||
ureq::Error::StatusCode(status) => ClientError::ResponseStatus {
|
ureq::Error::StatusCode(status) => Self::ResponseStatus {
|
||||||
actual: status,
|
actual: status,
|
||||||
expected: None,
|
expected: 200,
|
||||||
},
|
},
|
||||||
error => Self::Request(error.to_string()),
|
ureq::Error::Io(e) => e.into(),
|
||||||
|
error => Self::req_err(error),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue