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}