lambda_runtime_client/
error.rs

1//! This module defines the `RuntimeApiError` trait that developers should implement
2//! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also
3//! defines the `ApiError` type returned by the `RuntimeClient` implementations.
4use failure::{AsFail, Backtrace, Context, Fail};
5use lambda_runtime_errors::LambdaErrorExt;
6use log::*;
7use serde_derive::*;
8use std::{
9    fmt::{self, Display},
10    option::Option,
11};
12
13/// Error type for the error responses to the Runtime APIs. In the future, this library
14/// should use a customer-generated error code
15pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError";
16
17/// This object is used to generate requests to the Lambda Runtime APIs.
18/// It is used for both the error response APIs and fail init calls.
19/// custom error types should implement the `RuntimeError` trait and return
20/// this object to be compatible with the APIs.
21#[derive(Serialize)]
22pub struct ErrorResponse {
23    /// The error message generated by the application.
24    #[serde(rename = "errorMessage")]
25    pub error_message: String,
26    /// The error type for Lambda. Normally, this value is populated using the
27    /// `error_type()` method from the `LambdaErrorExt` trait.
28    #[serde(rename = "errorType")]
29    pub error_type: String,
30    /// The stack trace for the exception as vector of strings. In the framework,
31    /// this value is automatically populated using the `backtrace` crate.
32    #[serde(rename = "stackTrace")]
33    pub stack_trace: Option<Vec<String>>,
34}
35
36impl ErrorResponse {
37    /// Creates a new instance of the `ErrorResponse` object with the given parameters. If the
38    /// `RUST_BACKTRACE` env variable is `1` the `ErrorResponse` is populated with the backtrace
39    /// collected through the [`backtrace` craete](https://crates.io/crates/backtrace).
40    ///
41    /// # Arguments
42    ///
43    /// * `message` The error message to be returned to the APIs. Normally the error description()
44    /// * `err_type` An error type that identifies the root cause. Normally populated by the
45    ///   `error_type()` method in the `LambdaErrorExt` trait.
46    /// * `backtrace` The stack trace for the error
47    ///
48    /// # Return
49    /// A new instance of the `ErrorResponse` object.
50    fn new(message: String, err_type: String, backtrace: Option<&Backtrace>) -> Self {
51        let mut err = ErrorResponse {
52            error_message: message,
53            error_type: err_type,
54            stack_trace: Option::default(),
55        };
56        // assume that failure is smart enough to only collect a backtrace
57        // if the env variable is enabled
58        if let Some(stack) = backtrace {
59            trace!("Begin backtrace collection");
60            err.stack_trace = Some(
61                format!("{:?}", stack)
62                    .lines()
63                    .map(std::string::ToString::to_string)
64                    .collect::<Vec<String>>(),
65            );
66            trace!("Completed backtrace collection");
67        }
68
69        err
70    }
71}
72
73impl<T: AsFail + LambdaErrorExt + Display> From<T> for ErrorResponse {
74    fn from(e: T) -> Self {
75        ErrorResponse::new(format!("{}", e), e.error_type().to_owned(), e.as_fail().backtrace())
76    }
77}
78
79/// Represents an error generated by the Lambda Runtime API client.
80#[derive(Debug)]
81pub struct ApiError {
82    inner: Context<ApiErrorKind>,
83}
84
85impl ApiError {
86    /// Returns `true` if the API error is recoverable and should be retried
87    pub fn is_recoverable(&self) -> bool {
88        match *self.inner.get_context() {
89            ApiErrorKind::Recoverable(_) => true,
90            _ => false,
91        }
92    }
93}
94/// Failure context for the `ApiError` type. The kind is used to indicate whether the
95/// error is recoverable and should be retried or not.
96#[derive(Clone, PartialEq, Debug, Fail)]
97pub enum ApiErrorKind {
98    /// Runtime implementations that receive recoverable errors should automatically
99    /// retry requests
100    #[fail(display = "Recoverable API error: {}", _0)]
101    Recoverable(String),
102    /// Unrecoverable error should cause the runtime implementation to call the `fail_init`
103    /// method of the Runtime APIs if it is appropriate and then shutdown gracefully
104    #[fail(display = "Unrecoverable API error: {}", _0)]
105    Unrecoverable(String),
106}
107
108impl Fail for ApiError {
109    fn cause(&self) -> Option<&Fail> {
110        self.inner.cause()
111    }
112
113    fn backtrace(&self) -> Option<&Backtrace> {
114        self.inner.backtrace()
115    }
116}
117
118impl Display for ApiError {
119    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
120        Display::fmt(&self.inner, f)
121    }
122}
123
124impl LambdaErrorExt for ApiError {
125    fn error_type(&self) -> &str {
126        "RuntimeApiError"
127    }
128}
129
130impl From<ApiErrorKind> for ApiError {
131    fn from(kind: ApiErrorKind) -> Self {
132        Self {
133            inner: Context::new(kind),
134        }
135    }
136}
137
138impl From<Context<ApiErrorKind>> for ApiError {
139    fn from(inner: Context<ApiErrorKind>) -> Self {
140        Self { inner }
141    }
142}
143
144#[cfg(test)]
145pub(crate) mod tests {
146    use super::*;
147    use failure::format_err;
148    use std::env;
149
150    #[test]
151    fn does_not_produce_stack_trace() {
152        env::remove_var("RUST_BACKTRACE");
153        let err = format_err!("Test error").compat();
154        let resp_err = ErrorResponse::from(err);
155        assert_eq!(resp_err.stack_trace, None);
156    }
157
158    #[test]
159    fn is_recoverable_eq_correctly() {
160        let rec_err = ApiError::from(ApiErrorKind::Recoverable("Some recoverable kind".to_owned()));
161        assert_eq!(true, rec_err.is_recoverable());
162        let unrec_err = ApiError::from(ApiErrorKind::Unrecoverable("Some unrecovrable kind".to_owned()));
163        assert_eq!(false, unrec_err.is_recoverable());
164    }
165}