lambda_runtime_errors/
lib.rs

1//! The Lambda runtime errors crate defines the `LambdaErrorExt` trait
2//! that can be used by libriaries to return errors compatible with the
3//! AWS Lambda Rust runtime.
4//!
5//! This crate also exports the `lambda_runtime_errors_derive` crate to
6//! derive the `LambdaErrorExt` trait.
7//!
8//! ```rust,no-run
9//! use lambda_runtime_errors::*;
10//!
11//! // the generated error_type() method returns "crate::LambdaError"
12//! #[derive(LambdaErrorExt)]
13//! struct LambdaError;
14//! ```
15mod error_ext_impl;
16
17pub use crate::error_ext_impl::*;
18pub use lambda_runtime_errors_derive::*;
19
20use failure::{format_err, Compat, Error, Fail};
21use std::fmt;
22
23/// The `LambdaErrorExt` trait defines the `error_type()` method used
24/// by the AWS Lambda runtime client to generate `ErrorResponse`
25/// objects. The value returned by the `error_type()` method is used to
26/// populate the `errorType` field in the Lambda response.  This crate
27/// includes an implementation of this trait for most errors in the
28/// standard library. By default, error return their type name.
29pub trait LambdaErrorExt {
30    /// The value for this field should be an alphanumeric unique identifier
31    /// of the error type. For example `MyCustomError`.
32    ///
33    /// # Return
34    /// An alphanumeric identifier for the error
35    fn error_type(&self) -> &str;
36}
37
38impl LambdaErrorExt for Error {
39    fn error_type(&self) -> &str {
40        self.find_root_cause().name().unwrap_or_else(|| "FailureError")
41    }
42}
43
44// We implement this trait here so that we can use the Compat type
45// in the lambda-runtime crate - heaps of fun between failure and std::error
46impl LambdaErrorExt for Compat<Error> {
47    fn error_type(&self) -> &str {
48        "CompatFailureError"
49    }
50}
51
52/// `Result` type extension for AWS that makes it easy to generate a `HandlerError`
53/// object or a `Compat<Error>` from the failure crate using an existing result.
54/// This trait should be imported from the `lambda_runtime_core` or `lambda_runtime`
55/// crates.
56pub trait LambdaResultExt<OK, ERR> {
57    /// Takes the incoming `Result` and maps it to a Result that returns an `HandlerError` object.
58    /// The `HandlerError` type already includes implementations of the `From` trait for most
59    /// standard library errors. This method is intended to be used when a the `From` trait is not
60    /// implemented.
61    ///
62    /// # Example
63    ///
64    /// ```rust,no_run
65    /// use lambda_runtime_core::{Context, LambdaResultExt, HandlerError, lambda};
66    /// use std::error::Error as StdError;
67    ///
68    /// fn main() -> Result<(), Box<dyn StdError>> {
69    ///     lambda!(my_handler);
70    ///     Ok(())
71    /// }
72    ///
73    /// fn my_handler(_event: Vec<u8>, _ctx: Context) -> Result<Vec<u8>, HandlerError> {
74    ///     let age = "hello"; // this will throw an error when we try to parse it into an int
75    ///     age.parse::<u8>().handler_error()?;
76    ///
77    ///     Ok(vec!())
78    /// }
79    /// ```
80    fn handler_error(self) -> Result<OK, HandlerError>;
81
82    /// Takes the incoming result and converts it into an `Error` type from the `failure` crate
83    /// wrapped in a `Compat` object to make it implement the `Error` trait from the standard
84    /// library. This method makes it easy to write handler functions that return `Compat<Error>`
85    /// directly.
86    ///
87    /// # Example
88    ///
89    /// ```rust,no_run
90    /// use lambda_runtime_core::{Context, LambdaResultExt, lambda};
91    /// use failure::{Error, Compat};
92    /// use std::error::Error as StdError;
93    ///
94    /// fn main() -> Result<(), Box<dyn StdError>> {
95    ///     lambda!(my_handler);
96    ///     Ok(())
97    /// }
98    ///
99    /// fn my_handler(_event: Vec<u8>, _ctx: Context) -> Result<Vec<u8>, Compat<Error>> {
100    ///     let age = "hello"; // this will throw an error when we try to parse it into an int
101    ///     age.parse::<u8>().failure_compat()?;
102    ///     Ok(vec!())
103    /// }
104    /// ```
105    fn failure_compat(self) -> Result<OK, Compat<Error>>;
106}
107
108impl<OK, ERR> LambdaResultExt<OK, ERR> for Result<OK, ERR>
109where
110    ERR: Fail + LambdaErrorExt,
111{
112    fn handler_error(self) -> Result<OK, HandlerError> {
113        self.map_err(HandlerError::new)
114    }
115
116    fn failure_compat(self) -> Result<OK, Compat<Error>> {
117        self.map_err(|err| Error::from(err).compat())
118    }
119}
120
121/// The `HandlerError` struct can be use to abstract any `Err` of the handler method `Result`.
122/// The `HandlerError` object can be generated `From` any object that supports `Display`,
123/// `Send, `Sync`, and `Debug`. This allows handler functions to return any error using
124/// the `?` syntax. For example `let _age_num: u8 = e.age.parse()?;` will return the
125/// `<F as FromStr>::Err` from the handler function.
126//pub type HandlerError = failure::Error;
127#[derive(Debug)]
128pub struct HandlerError {
129    err_type: String,
130    inner: failure::Error,
131}
132impl HandlerError {
133    pub fn new<T: failure::Fail + LambdaErrorExt + Send + Sync>(e: T) -> Self {
134        let err_type = e.error_type().to_owned();
135        HandlerError {
136            err_type,
137            inner: failure::Error::from(e),
138        }
139    }
140}
141impl std::error::Error for HandlerError {}
142impl fmt::Display for HandlerError {
143    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
144        write!(f, "{}: {}", self.err_type, self.inner.find_root_cause())
145    }
146}
147impl LambdaErrorExt for HandlerError {
148    fn error_type(&self) -> &str {
149        &self.err_type
150    }
151}
152impl From<&str> for HandlerError {
153    fn from(s: &str) -> Self {
154        HandlerError {
155            err_type: "UnknownError".to_owned(),
156            inner: format_err!("{}", s),
157        }
158    }
159}
160impl From<failure::Error> for HandlerError {
161    fn from(e: failure::Error) -> Self {
162        let error_type = e.error_type();
163        HandlerError {
164            err_type: error_type.to_owned(),
165            inner: e,
166        }
167    }
168}
169impl From<serde_json::error::Error> for HandlerError {
170    fn from(e: serde_json::error::Error) -> Self {
171        HandlerError {
172            err_type: "JsonError".to_owned(),
173            inner: failure::Error::from(e),
174        }
175    }
176}
177
178#[cfg(test)]
179pub(crate) mod tests {
180    use super::*;
181    use failure::Fail;
182
183    #[derive(Fail, Debug)]
184    #[fail(display = "Custom Error")]
185    struct CustomError;
186
187    #[test]
188    fn std_error_type() {
189        let parsed_int = "hello".parse::<u8>();
190        let err = HandlerError::from(parsed_int.err().unwrap());
191        assert_eq!(err.error_type(), "std::num::ParseIntError");
192    }
193
194    #[test]
195    fn error_type_from_failure() {
196        let err = HandlerError::from(failure::Error::from(CustomError {}));
197        assert_eq!(err.error_type(), "lambda_runtime_errors::tests::CustomError");
198    }
199}