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