lambda_runtime_client/
error.rs1use 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
13pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError";
16
17#[derive(Serialize)]
22pub struct ErrorResponse {
23 #[serde(rename = "errorMessage")]
25 pub error_message: String,
26 #[serde(rename = "errorType")]
29 pub error_type: String,
30 #[serde(rename = "stackTrace")]
33 pub stack_trace: Option<Vec<String>>,
34}
35
36impl ErrorResponse {
37 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 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#[derive(Debug)]
81pub struct ApiError {
82 inner: Context<ApiErrorKind>,
83}
84
85impl ApiError {
86 pub fn is_recoverable(&self) -> bool {
88 match *self.inner.get_context() {
89 ApiErrorKind::Recoverable(_) => true,
90 _ => false,
91 }
92 }
93}
94#[derive(Clone, PartialEq, Debug, Fail)]
97pub enum ApiErrorKind {
98 #[fail(display = "Recoverable API error: {}", _0)]
101 Recoverable(String),
102 #[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}