1use http::{
3 header::{InvalidHeaderName, InvalidHeaderValue},
4 Error as RequestError,
5};
6use serde::Deserialize;
7use std::convert::Infallible;
8use std::result;
9
10pub type Result<T> = result::Result<T, Error>;
12
13#[derive(thiserror::Error, Debug)]
15#[error("value error: {0}")]
16pub struct ValueError(String);
17
18impl ValueError {
19 pub fn new<T: Into<String>>(value: T) -> Self {
20 Self(value.into())
21 }
22}
23
24impl From<&str> for ValueError {
25 fn from(err: &str) -> Self {
26 Self(err.to_string())
27 }
28}
29
30impl From<InvalidHeaderValue> for ValueError {
31 fn from(err: InvalidHeaderValue) -> Self {
32 return ValueError(err.to_string());
33 }
34}
35
36impl From<InvalidHeaderName> for ValueError {
37 fn from(err: InvalidHeaderName) -> Self {
38 return ValueError(err.to_string());
39 }
40}
41
42impl From<Infallible> for ValueError {
43 fn from(err: Infallible) -> Self {
44 return ValueError(err.to_string());
45 }
46}
47
48impl From<http::uri::InvalidUri> for ValueError {
49 fn from(err: http::uri::InvalidUri) -> Self {
50 return ValueError(err.to_string());
51 }
52}
53
54#[derive(thiserror::Error, Debug, Deserialize)]
56#[serde(rename_all = "PascalCase", rename = "Error")]
57#[error("S3Error: {message}")]
58pub struct S3Error {
59 pub code: String,
60 pub message: String,
61 #[serde(default)]
62 pub resource: String,
63 pub request_id: String,
64 pub host_id: Option<String>,
65 pub bucket_name: Option<String>,
66 pub object_name: Option<String>,
67}
68
69impl TryFrom<&[u8]> for S3Error {
70 type Error = crate::xml::error::Error;
71 fn try_from(res: &[u8]) -> std::result::Result<Self, Self::Error> {
72 return Ok(crate::xml::de::from_reader(res)?);
73 }
74}
75
76impl TryFrom<&str> for S3Error {
77 type Error = crate::xml::error::Error;
78 fn try_from(value: &str) -> std::result::Result<Self, Self::Error> {
79 value.as_bytes().try_into()
80 }
81}
82
83#[derive(thiserror::Error, Debug)]
88pub enum Error {
89 #[error("{0}")]
91 ValueError(String),
92
93 #[error("{0}")]
95 RequestError(#[from] RequestError),
96
97 #[error("{0}")]
99 XmlError(#[from] crate::xml::error::Error),
100
101 #[error("{0}")]
103 S3Error(#[from] S3Error),
104
105 #[error("{0}")]
107 HttpError(#[from] reqwest::Error),
108
109 #[error("Unexpected HTTP responses, status: {}", .0.status())]
111 UnknownResponse(reqwest::Response),
112
113 #[error("{0}")]
115 MessageDecodeError(String),
116
117 #[error("{0}")]
119 SelectObjectError(String),
120
121 #[error("{0}")]
123 IoError(#[from] std::io::Error),
124}
125
126impl<T: Into<ValueError>> From<T> for Error {
127 fn from(err: T) -> Self {
128 Error::ValueError(err.into().0)
129 }
130}
131
132impl From<reqwest::Response> for Error {
133 fn from(err: reqwest::Response) -> Self {
134 Self::UnknownResponse(err)
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use super::S3Error;
141 use crate::xml::error::Error as XmlError;
142
143 #[test]
144 fn test_s3_error() {
145 let res = r#"<?xml version="1.0" encoding="UTF-8"?>
146 <Error>
147 <Code>NoSuchKey</Code>
148 <Message>The resource you requested does not exist</Message>
149 <Resource>/mybucket/myfoto.jpg</Resource>
150 <RequestId>4442587FB7D0A2F9</RequestId>
151 </Error>"#;
152 let result: std::result::Result<S3Error, XmlError> = res.as_bytes().try_into();
153 assert!(result.is_ok());
154 println!("{:?}", result);
155 }
156}