use http::{
header::{InvalidHeaderName, InvalidHeaderValue},
Error as RequestError,
};
use serde::Deserialize;
use std::convert::Infallible;
use std::result;
pub type Result<T> = result::Result<T, Error>;
#[derive(thiserror::Error, Debug)]
#[error("value error: {0}")]
pub struct ValueError(String);
impl ValueError {
pub fn new<T: Into<String>>(value: T) -> Self {
Self(value.into())
}
}
impl From<&str> for ValueError {
fn from(err: &str) -> Self {
Self(err.to_string())
}
}
impl From<InvalidHeaderValue> for ValueError {
fn from(err: InvalidHeaderValue) -> Self {
return ValueError(err.to_string());
}
}
impl From<InvalidHeaderName> for ValueError {
fn from(err: InvalidHeaderName) -> Self {
return ValueError(err.to_string());
}
}
impl From<Infallible> for ValueError {
fn from(err: Infallible) -> Self {
return ValueError(err.to_string());
}
}
impl From<http::uri::InvalidUri> for ValueError {
fn from(err: http::uri::InvalidUri) -> Self {
return ValueError(err.to_string());
}
}
#[derive(thiserror::Error, Debug, Deserialize)]
#[serde(rename_all = "PascalCase", rename = "Error")]
#[error("S3Error: {message}")]
pub struct S3Error {
pub code: String,
pub message: String,
#[serde(default)]
pub resource: String,
pub request_id: String,
pub host_id: Option<String>,
pub bucket_name: Option<String>,
pub object_name: Option<String>,
}
impl TryFrom<&[u8]> for S3Error {
type Error = crate::xml::error::Error;
fn try_from(res: &[u8]) -> std::result::Result<Self, Self::Error> {
return Ok(crate::xml::de::from_reader(res)?);
}
}
impl TryFrom<&str> for S3Error {
type Error = crate::xml::error::Error;
fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
value.as_bytes().try_into()
}
}
#[derive(thiserror::Error, Debug)]
pub enum Error {
#[error("{0}")]
ValueError(String),
#[error("{0}")]
RequestError(#[from] RequestError),
#[error("{0}")]
XmlError(#[from] crate::xml::error::Error),
#[error("{0}")]
S3Error(#[from] S3Error),
#[error("{0}")]
HttpError(#[from] reqwest::Error),
#[error("Unexpected HTTP responses, status: {}", .0.status())]
UnknownResponse(reqwest::Response),
#[error("{0}")]
MessageDecodeError(String),
#[error("{0}")]
SelectObjectError(String),
#[error("{0}")]
IoError(#[from] std::io::Error),
}
impl<T: Into<ValueError>> From<T> for Error {
fn from(err: T) -> Self {
Error::ValueError(err.into().0)
}
}
impl From<reqwest::Response> for Error {
fn from(err: reqwest::Response) -> Self {
Self::UnknownResponse(err)
}
}
#[cfg(test)]
mod tests {
use super::S3Error;
use crate::xml::error::Error as XmlError;
#[test]
fn test_s3_error() {
let res = r#"<?xml version="1.0" encoding="UTF-8"?>
<Error>
<Code>NoSuchKey</Code>
<Message>The resource you requested does not exist</Message>
<Resource>/mybucket/myfoto.jpg</Resource>
<RequestId>4442587FB7D0A2F9</RequestId>
</Error>"#;
let result: std::result::Result<S3Error, XmlError> = res.as_bytes().try_into();
assert!(result.is_ok());
println!("{:?}", result);
}
}