connect_1password/
error.rs

1//! Error and Result module.
2
3use hyper::{header::InvalidHeaderValue, StatusCode};
4use regex::Regex;
5use std::{
6    error::Error as StdError,
7    fmt::{self, Display},
8    result::Result,
9    str::Utf8Error,
10};
11
12/// A simple type alias so as to DRY.
13pub type ConnectResult<T> = Result<T, Error>;
14
15/// Boxed error type
16pub type Cause = Box<dyn StdError + Send + Sync>;
17
18/// Error type
19pub struct Error {
20    inner: Box<ErrorImpl>,
21}
22
23struct ErrorImpl {
24    kind: Kind,
25    cause: Option<Cause>,
26}
27
28impl fmt::Debug for Error {
29    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
30        let mut f = f.debug_tuple("hyper::Error");
31        f.field(&self.inner.kind);
32        if let Some(ref cause) = self.inner.cause {
33            f.field(cause);
34        }
35        f.finish()
36    }
37}
38
39impl fmt::Display for Error {
40    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
41        if let Some(ref cause) = self.inner.cause {
42            write!(f, "{}: {}", self.description(), cause)
43        } else {
44            f.write_str(&self.description())
45        }
46    }
47}
48
49impl StdError for Error {
50    fn source(&self) -> Option<&(dyn StdError + 'static)> {
51        self.inner
52            .cause
53            .as_ref()
54            .map(|cause| &**cause as &(dyn StdError + 'static))
55    }
56}
57
58impl Error {
59    pub(super) fn new(kind: Kind) -> Error {
60        Error {
61            inner: Box::new(ErrorImpl { kind, cause: None }),
62        }
63    }
64
65    pub(super) fn with<C: Into<Cause>>(mut self, cause: C) -> Error {
66        self.inner.cause = Some(cause.into());
67        self
68    }
69
70    pub(crate) fn find_source<E: StdError + 'static>(&self) -> Option<&E> {
71        let mut cause = self.source();
72        while let Some(err) = cause {
73            if let Some(ref typed) = err.downcast_ref() {
74                return Some(typed);
75            }
76            cause = err.source();
77        }
78
79        // else
80        None
81    }
82
83    pub(super) fn new_network_error<E: Into<Cause>>(cause: E) -> Self {
84        Error::new(Kind::NetworkError).with(cause)
85    }
86
87    pub(super) fn new_parsing_error<E: Into<Cause>>(cause: E) -> Self {
88        Error::new(Kind::ParsingError).with(cause)
89    }
90
91    pub(super) fn new_retry_error<E: Into<Cause>>(cause: E) -> Self {
92        Error::new(Kind::RetryError).with(cause)
93    }
94
95    pub(super) fn new_connect_error(err: ConnectAPIError) -> Self {
96        Error::new(Kind::ConnectAPIError(err))
97    }
98
99    pub(super) fn new_internal_error() -> Self {
100        Error::new(Kind::InternalError)
101    }
102
103    /// The error's standalone message, without the message from the source.
104    pub fn message(&self) -> impl fmt::Display + '_ {
105        self.description()
106    }
107
108    fn description(&self) -> String {
109        match &self.inner.kind {
110            Kind::HyperError(_) => "this is a Hyper related error!".to_string(),
111            Kind::HyperHttpError(_) => "this is a Hyper HTTP related error!".to_string(),
112            Kind::InternalError => "internal error".to_string(),
113            Kind::InvalidHeaderValue => "invalid header value".to_string(),
114            Kind::NetworkError => "network error".to_string(),
115            Kind::NotImplementedError => "not implemented error".to_string(),
116            Kind::ParsingError => "parsing error".to_string(),
117            Kind::RetryError => "retry error".to_string(),
118            Kind::RequestNotSuccessful(err) => {
119                format!("client returned an unsuccessful HTTP status code: {}", err)
120            }
121            Kind::SerdeJsonError(_) => "serde deserialization error".to_string(),
122            Kind::Utf8Error => "parsing bytes experienced a UTF8 error".to_string(),
123            Kind::CustomError(err) => {
124                format!("Error: {}", err)
125            }
126            Kind::ConnectAPIError(err) => {
127                format!("Connect API error: {}", err)
128            }
129        }
130    }
131}
132
133/// Wrapper type which contains a failed request's status code and body.
134#[derive(Debug)]
135pub struct RequestNotSuccessful {
136    /// Status code returned by the HTTP call.
137    pub status: StatusCode,
138    /// Body returned by the HTTP call.
139    pub body: String,
140}
141
142impl RequestNotSuccessful {
143    /// Create a new unsuccessful request error.
144    pub fn new(status: StatusCode, body: String) -> Self {
145        Self { status, body }
146    }
147}
148
149impl StdError for RequestNotSuccessful {}
150
151impl Display for RequestNotSuccessful {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        write!(f, "StatusCode: {}, Body: {}", self.status, self.body)
154    }
155}
156
157/// Wrapper type which contains Vault errors.
158#[derive(Debug)]
159pub struct ConnectAPIError {
160    /// Error message from the API.
161    pub message: String,
162    /// Status code returned by the HTTP call.
163    pub status: String,
164}
165
166impl ConnectAPIError {
167    /// Create a new unsuccessful request error.
168    pub fn new(status: String, message: &str) -> Self {
169        Self {
170            status: status.to_string(),
171            message: message.to_string(),
172        }
173    }
174}
175
176impl StdError for ConnectAPIError {}
177
178impl Display for ConnectAPIError {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        write!(f, "StatusCode: {}, Message: {}", self.status, self.message)
181    }
182}
183
184/// Wrapper type for custom errors.
185#[derive(Debug)]
186pub struct CustomError {
187    /// Error message.
188    pub message: String,
189}
190
191impl CustomError {
192    /// Create a new custom error.
193    pub fn new(message: &str) -> Self {
194        Self {
195            message: message.to_string(),
196        }
197    }
198}
199
200impl StdError for CustomError {}
201
202impl Display for CustomError {
203    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204        write!(f, "Message: {}", self.message)
205    }
206}
207
208#[derive(Debug)]
209pub(super) enum Kind {
210    CustomError(CustomError),
211
212    /// The failure was due to a Hyper error
213    HyperError(hyper::Error),
214
215    /// The failure was due to a Hyper error
216    HyperHttpError(hyper::http::Error),
217
218    InternalError,
219
220    InvalidHeaderValue,
221
222    /// The failure was due to the network client not working properly.
223    NetworkError,
224
225    NotImplementedError,
226
227    ParsingError,
228
229    RetryError,
230
231    RequestNotSuccessful(RequestNotSuccessful),
232
233    SerdeJsonError(serde_json::Error),
234
235    Utf8Error,
236
237    ConnectAPIError(ConnectAPIError),
238}
239
240impl fmt::Display for Kind {
241    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
242        match self {
243            &Self::HyperError(_) => {
244                write!(f, "HyperError")
245            }
246            &Self::HyperHttpError(_) => {
247                write!(f, "HyperHttpError")
248            }
249            Self::InternalError => {
250                write!(f, "InternalError")
251            }
252            Self::InvalidHeaderValue => {
253                write!(f, "InvalidHeaderValue")
254            }
255            Self::NetworkError => {
256                write!(f, "NetworkError")
257            }
258            Self::NotImplementedError => {
259                write!(f, "NotImplementedError")
260            }
261            Self::ParsingError => {
262                write!(f, "ParsingError")
263            }
264            Self::RetryError => {
265                write!(f, "RetryError")
266            }
267            &Self::RequestNotSuccessful(_) => {
268                write!(f, "RequestNotSuccessful")
269            }
270            &Self::SerdeJsonError(_) => {
271                write!(f, "SerdeJsonError")
272            }
273            Self::Utf8Error => {
274                write!(f, "Utf8Error")
275            }
276            &Self::ConnectAPIError(_) => {
277                write!(f, "ConnectAPIError")
278            }
279            &Self::CustomError(_) => {
280                write!(f, "CustomError")
281            }
282        }
283    }
284}
285
286impl From<hyper::Error> for Error {
287    fn from(err: hyper::Error) -> Self {
288        Error::new(Kind::HyperError(err))
289    }
290}
291
292impl From<hyper::http::Error> for Error {
293    fn from(err: hyper::http::Error) -> Self {
294        Error::new(Kind::HyperHttpError(err))
295    }
296}
297
298impl From<InvalidHeaderValue> for Error {
299    fn from(_err: InvalidHeaderValue) -> Self {
300        Error::new(Kind::InvalidHeaderValue)
301    }
302}
303
304impl From<RequestNotSuccessful> for Error {
305    fn from(err: RequestNotSuccessful) -> Self {
306        Error::new(Kind::RequestNotSuccessful(err))
307    }
308}
309
310impl From<Utf8Error> for Error {
311    fn from(err: Utf8Error) -> Self {
312        Error::new(Kind::Utf8Error).with(err)
313    }
314}
315
316impl From<serde_json::Error> for Error {
317    fn from(err: serde_json::Error) -> Self {
318        Error::new(Kind::SerdeJsonError(err))
319    }
320}
321
322/// Defines an error from the 1Password Connect API
323#[derive(Debug)]
324pub struct OPError {
325    pub(super) status_code: Option<u16>,
326    pub(super) captures: Option<Vec<String>>,
327}
328
329/// Handle Connect API error response
330pub fn process_connect_error_response(err_message: String) -> Result<OPError, Error> {
331    let input_re = Regex::new(r#"(StatusCode):\s+(\d+)"#).unwrap();
332
333    // Execute the Regex
334    let captures = input_re.captures(&err_message).map(|captures| {
335        captures
336            .iter() // All the captured groups
337            .skip(1) // Skipping the complete match
338            .flat_map(|c| c) // Ignoring all empty optional matches
339            .map(|c| c.as_str()) // Grab the original strings
340            .collect::<Vec<_>>() // Create a vector
341    });
342
343    dbg!(&captures);
344
345    // Match against the captured values as a slice
346    let status_code: Option<u16> = match captures.as_ref().map(|c| c.as_slice()) {
347        Some(["StatusCode", x]) => {
348            let x = x.parse().expect("can't parse number");
349            Some(x)
350        }
351        _ => None,
352    };
353
354    let return_captures: Option<Vec<String>> =
355        captures.map(|b| b.into_iter().map(|c| c.to_owned()).collect::<Vec<_>>());
356
357    Ok(OPError {
358        status_code,
359        captures: return_captures,
360    })
361}