shrupl/src/impl_ureq.rs

183 lines
5.6 KiB
Rust

use log::{debug, trace};
use crate::{
error,
file::{self, FileTrait},
sharry::{self, FileID, Uri},
};
fn find_cause(
uri: &Uri,
alias_id: &str,
share_id: Option<&str>,
file_id: Option<&FileID>,
) -> impl FnOnce(ureq::Error) -> error::Error {
move |error| match error {
ureq::Error::StatusCode(403) => {
trace!("HTTP Error 403: Alias not found!");
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 {
error::Error::InvalidParameter(error::Parameter::FileID(file_id.clone()))
} else if let Some(share_id) = share_id {
error::Error::InvalidParameter(error::Parameter::ShareID(share_id.to_owned()))
} else {
error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string()))
}
}
ureq::Error::Io(error) => {
trace!("std::io::Error {error:?}");
if let Some(msg) = error.get_ref().map(ToString::to_string) {
if msg.starts_with("failed to lookup address information") {
error::Error::InvalidParameter(error::Parameter::Uri(uri.to_string()))
} else {
error.into()
}
} else {
error.into()
}
}
error => error::Error::Unknown(error.to_string()),
}
}
impl sharry::Client for ureq::Agent {
fn share_create(
&self,
uri: &Uri,
alias_id: &str,
data: sharry::NewShareRequest,
) -> error::Result<String> {
let res = {
let endpoint = uri.share_create();
let mut res = self
.post(&endpoint)
.header("Sharry-Alias", alias_id)
.send_json(data)
.map_err(find_cause(uri, alias_id, None, None))?;
trace!("{endpoint:?} response: {res:?}");
error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?;
res.body_mut()
.read_json::<sharry::NewShareResponse>()
.map_err(error::Error::response)?
};
debug!("{res:?}");
if res.success && (res.message == "Share created.") {
trace!("new share id: {:?}", res.id);
Ok(res.id)
} else {
Err(error::Error::response(format!("{res:?}")))
}
}
fn share_notify(&self, uri: &Uri, alias_id: &str, share_id: &str) -> error::Result<()> {
let res = {
let endpoint = uri.share_notify(share_id);
let mut res = self
.post(&endpoint)
.header("Sharry-Alias", alias_id)
.send_empty()
.map_err(find_cause(uri, alias_id, Some(share_id), None))?;
trace!("{endpoint:?} response: {res:?}");
error::Error::res_status_check(res.status(), ureq::http::StatusCode::OK)?;
res.body_mut()
.read_json::<sharry::NotifyShareResponse>()
.map_err(error::Error::response)?
};
debug!("{res:?}");
Ok(())
}
fn file_create(
&self,
uri: &Uri,
alias_id: &str,
share_id: &str,
file: &file::Checked,
) -> error::Result<FileID> {
let res = {
let endpoint = uri.file_create(share_id);
let res = self
.post(&endpoint)
.header("Sharry-Alias", alias_id)
.header("Sharry-File-Name", file.get_name())
.header("Upload-Length", file.get_size())
.send_empty()
.map_err(find_cause(uri, alias_id, Some(share_id), None))?;
trace!("{endpoint:?} response: {res:?}");
error::Error::res_status_check(res.status(), ureq::http::StatusCode::CREATED)?;
res
};
let location = (res.headers().get("Location"))
.ok_or_else(|| error::Error::response("Location header not found"))?
.to_str()
.map_err(error::Error::response)?
.to_string();
FileID::try_from(location)
}
fn file_patch(
&self,
uri: &Uri,
alias_id: &str,
share_id: &str,
chunk: &file::Chunk,
) -> error::Result<()> {
let res = {
let endpoint = uri.file_patch(share_id, chunk.get_file_id());
let res = self
.patch(&endpoint)
.header("Sharry-Alias", alias_id)
.header("Upload-Offset", chunk.get_offset())
.send(chunk.get_data())
.map_err(find_cause(
uri,
alias_id,
Some(share_id),
Some(chunk.get_file_id()),
))?;
trace!("{endpoint:?} response: {res:?}");
error::Error::res_status_check(res.status(), ureq::http::StatusCode::NO_CONTENT)?;
res
};
let res_offset = (res.headers().get("Upload-Offset"))
.ok_or_else(|| error::Error::response("Upload-Offset header not found"))?
.to_str()
.map_err(error::Error::response)?
.parse::<u64>()
.map_err(error::Error::response)?;
if chunk.get_behind() == res_offset {
Ok(())
} else {
Err(error::Error::response(format!(
"Unexpected Upload-Offset: {} (expected {})",
res_offset,
chunk.get_behind()
)))
}
}
}