diff --git a/Cargo.lock b/Cargo.lock index 2bb0f9f3..7c3ed972 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -411,6 +411,7 @@ version = "0.1.0" dependencies = [ "backtrace 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "chrono 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "failure 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "hyper 0.12.12 (registry+https://github.com/rust-lang/crates.io-index)", "hyper-tls 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "lambda_runtime_client 0.1.0", @@ -420,6 +421,7 @@ dependencies = [ "serde 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)", "serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)", + "simple-error 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)", "simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "tokio 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -909,6 +911,11 @@ dependencies = [ "fake-simd 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "simple-error" +version = "0.1.12" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "simple_logger" version = "1.0.1" @@ -1377,6 +1384,7 @@ dependencies = [ "checksum serde_derive 1.0.79 (registry+https://github.com/rust-lang/crates.io-index)" = "31569d901045afbff7a9479f793177fe9259819aff10ab4f89ef69bbc5f567fe" "checksum serde_json 1.0.31 (registry+https://github.com/rust-lang/crates.io-index)" = "bb47a3d5c84320222f66d7db21157c4a7407755de41798f9b4c1c40593397b1a" "checksum sha2 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9eb6be24e4c23a84d7184280d2722f7f2731fcdd4a9d886efbfe4413e4847ea0" +"checksum simple-error 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "01c1c2ededd95f93b1d65e7f8b5b17670e926bf9cbb55f8b91b26b0bd40d3259" "checksum simple_logger 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "25111f1d77db1ac3ee11b62ba4b7a162e6bb3be43e28273f0d3935cc8d3ff7fb" "checksum slab 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "5f9776d6b986f77b35c6cf846c11ad986ff128fe0b2b63a3628e3755e8d3102d" "checksum smallvec 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "153ffa32fd170e9944f7e0838edf824a754ec4c1fc64746fcc9fe1f8fa602e5d" diff --git a/lambda-runtime-client/src/client.rs b/lambda-runtime-client/src/client.rs index 95471dc2..30f1673e 100644 --- a/lambda-runtime-client/src/client.rs +++ b/lambda-runtime-client/src/client.rs @@ -1,4 +1,4 @@ -use error::{ApiError, ErrorResponse, RuntimeApiError}; +use error::{ApiError, ErrorResponse, ERROR_TYPE_UNHANDLED}; use hyper::{ client::HttpConnector, header::{self, HeaderMap, HeaderValue}, @@ -250,7 +250,7 @@ impl RuntimeClient { /// /// # Returns /// A `Result` object containing a bool return value for the call or an `error::ApiError` instance. - pub fn event_error(&self, request_id: &str, e: &RuntimeApiError) -> Result<(), ApiError> { + pub fn event_error(&self, request_id: &str, e: ErrorResponse) -> Result<(), ApiError> { let uri: Uri = format!( "http://{}/{}/runtime/invocation/{}/error", self.endpoint, RUNTIME_API_VERSION, request_id @@ -259,9 +259,9 @@ impl RuntimeClient { trace!( "Posting error to runtime API for request {}: {}", request_id, - e.to_response().error_message + e.error_message ); - let req = self.get_runtime_error_request(&uri, &e.to_response()); + let req = self.get_runtime_error_request(&uri, e); match self.http_client.request(req).wait() { Ok(resp) => { @@ -297,12 +297,12 @@ impl RuntimeClient { /// # Panics /// If it cannot send the init error. In this case we panic to force the runtime /// to restart. - pub fn fail_init(&self, e: &RuntimeApiError) { + pub fn fail_init(&self, e: ErrorResponse) { let uri: Uri = format!("http://{}/{}/runtime/init/error", self.endpoint, RUNTIME_API_VERSION) .parse() .expect("Could not generate Runtime URI"); - error!("Calling fail_init Runtime API: {}", e.to_response().error_message); - let req = self.get_runtime_error_request(&uri, &e.to_response()); + error!("Calling fail_init Runtime API: {}", e.error_message); + let req = self.get_runtime_error_request(&uri, e); self.http_client .request(req) @@ -343,8 +343,8 @@ impl RuntimeClient { .unwrap() } - fn get_runtime_error_request(&self, uri: &Uri, e: &ErrorResponse) -> Request { - let body = serde_json::to_vec(e).expect("Could not turn error object into response JSON"); + fn get_runtime_error_request(&self, uri: &Uri, e: ErrorResponse) -> Request { + let body = serde_json::to_vec(&e).expect("Could not turn error object into response JSON"); Request::builder() .method(Method::POST) .uri(uri.clone()) @@ -352,7 +352,7 @@ impl RuntimeClient { header::CONTENT_TYPE, header::HeaderValue::from_static(API_ERROR_CONTENT_TYPE), ) - .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static("RuntimeError")) // TODO: We should add this code to the error object. + .header(RUNTIME_ERROR_HEADER, HeaderValue::from_static(ERROR_TYPE_UNHANDLED)) .body(Body::from(body)) .unwrap() } diff --git a/lambda-runtime-client/src/error.rs b/lambda-runtime-client/src/error.rs index 5a2bc573..3a85bbb1 100644 --- a/lambda-runtime-client/src/error.rs +++ b/lambda-runtime-client/src/error.rs @@ -1,7 +1,14 @@ //! This module defines the `RuntimeApiError` trait that developers should implement //! to send their custom errors to the AWS Lambda Runtime Client SDK. The module also //! defines the `ApiError` type returned by the `RuntimeClient` implementations. -use std::{env, error::Error, fmt, io, num::ParseIntError, option::Option}; +use std::{ + env, + error::Error, + fmt::{self, Display}, + io, + num::ParseIntError, + option::Option, +}; use backtrace; use http::{header::ToStrError, uri::InvalidUri}; @@ -10,10 +17,14 @@ use serde_json; /// Error type description for the `ErrorResponse` event. This type should be returned /// for errors that were handled by the function code or framework. -pub const ERROR_TYPE_HANDLED: &str = "Handled"; +#[allow(dead_code)] +pub(crate) const ERROR_TYPE_HANDLED: &str = "Handled"; /// Error type description for the `ErrorResponse` event. This type is used for unhandled, /// unexpcted errors. -pub const ERROR_TYPE_UNHANDLED: &str = "Handled"; +pub(crate) const ERROR_TYPE_UNHANDLED: &str = "Unhandled"; +/// Error type for the error responses to the Runtime APIs. In the future, this library +/// should use a customer-generated error code +pub const RUNTIME_ERROR_TYPE: &str = "RustRuntimeError"; /// This object is used to generate requests to the Lambda Runtime APIs. /// It is used for both the error response APIs and fail init calls. @@ -36,6 +47,38 @@ pub struct ErrorResponse { } impl ErrorResponse { + /// Creates a new instance of the `ErrorResponse` object with the given parameters. If the + /// `RUST_BACKTRACE` env variable is `1` the `ErrorResponse` is populated with the backtrace + /// collected through the [`backtrace` craete](https://crates.io/crates/backtrace). + /// + /// # Arguments + /// + /// * `message` The error message to be returned to the APIs. Normally the error description() + /// * `err_type` The error type. Use the `ERROR_TYPE_HANDLED` and `ERROR_TYPE_UNHANDLED`. + /// * `code` A custom error code + /// + /// # Return + /// A new instance of the `ErrorResponse` object. + fn new(message: String, err_type: String) -> ErrorResponse { + let mut err = ErrorResponse { + error_message: message, + error_type: err_type, + stack_trace: Option::default(), + }; + let is_backtrace = env::var("RUST_BACKTRACE"); + if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { + trace!("Begin backtrace collection"); + let trace = Option::from(backtrace::Backtrace::new()); + let trace_string = format!("{:?}", trace) + .lines() + .map(|s| s.to_string()) + .collect::>(); + trace!("Completed backtrace collection"); + err.stack_trace = Option::from(trace_string); + } + err + } + /// Creates a new `RuntimeError` object with the handled error type. /// /// # Arguments @@ -45,11 +88,7 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn handled(message: String) -> ErrorResponse { - ErrorResponse { - error_message: message, - error_type: String::from(ERROR_TYPE_HANDLED), - stack_trace: Option::default(), - } + ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) } /// Creates a new `RuntimeError` object with the unhandled error type. @@ -61,33 +100,20 @@ impl ErrorResponse { /// # Return /// A populated `RuntimeError` object that can be used with the Lambda Runtime API. pub fn unhandled(message: String) -> ErrorResponse { - ErrorResponse { - error_message: message, - error_type: String::from(ERROR_TYPE_UNHANDLED), - stack_trace: Option::default(), - } + ErrorResponse::new(message, RUNTIME_ERROR_TYPE.to_owned()) } } -/// Custom errors for the framework should implement this trait. The client calls -/// the `to_response()` method automatically to produce an object that can be serialized -/// and sent to the Lambda Runtime APIs. -pub trait RuntimeApiError { - /// Creates a `RuntimeError` object for the current error. This is - /// then serialized and sent to the Lambda runtime APIs. - /// - /// # Returns - /// A populated `RuntimeError` object. - fn to_response(&self) -> ErrorResponse; +impl From> for ErrorResponse { + fn from(e: Box) -> Self { + Self::handled(format!("{}", e)) + } } /// Represents an error generated by the Lambda Runtime API client. #[derive(Debug, Clone)] pub struct ApiError { msg: String, - /// The `Backtrace` object from the `backtrace` crate used to store - /// the stack trace of the error. - pub backtrace: Option, /// Whether the current error is recoverable. If the error is not /// recoverable a runtime should panic to force the Lambda service /// to restart the execution environment. @@ -96,16 +122,8 @@ pub struct ApiError { impl ApiError { pub(crate) fn new(description: &str) -> ApiError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } ApiError { msg: String::from(description), - backtrace: trace, recoverable: true, } } @@ -134,6 +152,8 @@ impl Error for ApiError { None } } +unsafe impl Send for ApiError {} +unsafe impl Sync for ApiError {} impl From for ApiError { fn from(e: serde_json::Error) -> Self { @@ -170,14 +190,3 @@ impl From for ApiError { ApiError::new(e.description()) } } - -impl RuntimeApiError for ApiError { - fn to_response(&self) -> ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - let trace_vec = backtrace.lines().map(|s| s.to_string()).collect::>(); - let mut err = ErrorResponse::unhandled(self.msg.clone()); - err.stack_trace = Option::from(trace_vec); - - err - } -} diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 5acd7778..d2fb6e72 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -23,5 +23,7 @@ simple_logger = "^1" [dev-dependencies] hyper-tls = "^0.3" +simple-error = "^0.1" +failure = "^0.1" rusoto_core = "^0.35" -rusoto_dynamodb = "^0.35" +rusoto_dynamodb = "^0.35" \ No newline at end of file diff --git a/lambda-runtime/examples/basic.rs b/lambda-runtime/examples/basic.rs index 83736a45..c1fbc29f 100644 --- a/lambda-runtime/examples/basic.rs +++ b/lambda-runtime/examples/basic.rs @@ -1,11 +1,13 @@ extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; +extern crate simple_error; extern crate simple_logger; -use lambda::{error::HandlerError, lambda}; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; +use simple_error::bail; use std::error::Error; #[derive(Deserialize)] @@ -29,7 +31,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { diff --git a/lambda-runtime/examples/custom_error.rs b/lambda-runtime/examples/custom_error.rs new file mode 100644 index 00000000..d9e18783 --- /dev/null +++ b/lambda-runtime/examples/custom_error.rs @@ -0,0 +1,66 @@ +extern crate lambda_runtime as lambda; +extern crate log; +extern crate serde_derive; +extern crate simple_logger; + +use lambda::{lambda, HandlerError}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::{error::Error, fmt}; + +#[derive(Debug)] +struct CustomError { + msg: String, +} + +impl fmt::Display for CustomError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} + +impl Error for CustomError {} + +impl CustomError { + fn new(message: &str) -> CustomError { + CustomError { + msg: message.to_owned(), + } + } +} + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + // in this case, we explicitly initialize and box our custom error type. + // the HandlerError type is an alias to Box/ + return Err(CustomError::new("Empty first name").into()); + } + + // For errors simply want to return, because the HandlerError is an alias to any + // generic error type, we can propapgate with the standard "?" syntax. + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/custom_error_failure.rs b/lambda-runtime/examples/custom_error_failure.rs new file mode 100644 index 00000000..cfa9a47a --- /dev/null +++ b/lambda-runtime/examples/custom_error_failure.rs @@ -0,0 +1,47 @@ +extern crate failure; +extern crate lambda_runtime as lambda; +extern crate log; +extern crate serde_derive; +extern crate simple_logger; + +use failure::Fail; +use lambda::{lambda, HandlerError}; +use log::error; +use serde_derive::{Deserialize, Serialize}; +use std::error::Error as StdError; + +#[derive(Fail, Debug)] +#[fail(display = "Custom Error")] +struct CustomError; + +#[derive(Deserialize)] +struct CustomEvent { + #[serde(rename = "firstName")] + first_name: String, + age: String, +} + +#[derive(Serialize)] +struct CustomOutput { + message: String, +} + +fn main() -> Result<(), Box> { + simple_logger::init_with_level(log::Level::Debug).unwrap(); + lambda!(my_handler); + + Ok(()) +} + +fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { + if e.first_name == "" { + error!("Empty first name in request {}", c.aws_request_id); + return Err((CustomError {}).into()); + } + + let _age_num: u8 = e.age.parse()?; + + Ok(CustomOutput { + message: format!("Hello, {}!", e.first_name), + }) +} diff --git a/lambda-runtime/examples/with_custom_runtime.rs b/lambda-runtime/examples/with_custom_runtime.rs index 599e0556..666f56ec 100644 --- a/lambda-runtime/examples/with_custom_runtime.rs +++ b/lambda-runtime/examples/with_custom_runtime.rs @@ -2,9 +2,11 @@ extern crate lambda_runtime as lambda; extern crate log; extern crate serde_derive; extern crate simple_logger; +#[macro_use] +extern crate simple_error; extern crate tokio; -use lambda::{error::HandlerError, lambda}; +use lambda::{lambda, HandlerError}; use log::error; use serde_derive::{Deserialize, Serialize}; use std::error::Error; @@ -33,7 +35,7 @@ fn main() -> Result<(), Box> { fn my_handler(e: CustomEvent, c: lambda::Context) -> Result { if e.first_name == "" { error!("Empty first name in request {}", c.aws_request_id); - return Err(c.new_error("Empty first name")); + bail!("Empty first name"); } Ok(CustomOutput { diff --git a/lambda-runtime/src/context.rs b/lambda-runtime/src/context.rs index 88e699a6..75ea16e3 100644 --- a/lambda-runtime/src/context.rs +++ b/lambda-runtime/src/context.rs @@ -1,10 +1,6 @@ -use std::env; - use chrono::Utc; -use backtrace; use env as lambda_env; -use error::HandlerError; use lambda_runtime_client; /// The Lambda function execution context. The values in this struct @@ -86,20 +82,6 @@ impl Context { } } - /// We use the context for each event to store the stack trace. This is the methods - /// clients should use to retrieve an initialized `RuntimeError` with the populated - /// stack trace. - pub fn new_error(&self, msg: &str) -> HandlerError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } - HandlerError::new(msg, trace) - } - /// Returns the remaining time in the execution in milliseconds. This is based on the /// deadline header passed by Lambda's Runtime APIs. pub fn get_time_remaining_millis(&self) -> u128 { diff --git a/lambda-runtime/src/error.rs b/lambda-runtime/src/error.rs index 2e00dcf4..ebbcd504 100644 --- a/lambda-runtime/src/error.rs +++ b/lambda-runtime/src/error.rs @@ -1,11 +1,35 @@ //! The error module defines the error types that can be returned //! by custom handlers as well as the runtime itself. -use std::{env, error::Error, fmt}; +use std::{ + env, + error::Error, + fmt::{self, Debug, Display}, +}; -use backtrace; -use lambda_runtime_client::error; +use lambda_runtime_client::error::ApiError; use serde_json; +/// 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 +/// `::Err` from the handler function. +pub struct HandlerError { + msg: String, +} + +impl From for HandlerError { + fn from(e: E) -> HandlerError { + HandlerError { msg: format!("{}", e) } + } +} + +impl Display for HandlerError { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.msg) + } +} + /// The `RuntimeError` object is returned by the custom runtime as it polls /// for new events and tries to execute the handler function. The error /// is primarily used by other methods within this crate and should not be relevant @@ -14,7 +38,6 @@ use serde_json; #[derive(Debug, Clone)] pub struct RuntimeError { msg: String, - stack_trace: Option, /// The request id that generated this error pub(crate) request_id: Option, /// Whether the error is recoverable or not. @@ -49,33 +72,14 @@ impl RuntimeError { /// # Returns /// A new `RuntimeError` instance. pub(crate) fn new(msg: &str) -> RuntimeError { - let mut trace: Option = None; - let is_backtrace = env::var("RUST_BACKTRACE"); - if is_backtrace.is_ok() && is_backtrace.unwrap() == "1" { - trace!("Begin backtrace collection"); - trace = Option::from(backtrace::Backtrace::new()); - trace!("Completed backtrace collection"); - } RuntimeError { msg: String::from(msg), - stack_trace: trace, recoverable: true, request_id: None, } } } -impl error::RuntimeApiError for RuntimeError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.stack_trace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), - } - } -} - impl fmt::Display for RuntimeError { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.msg) @@ -106,66 +110,10 @@ impl From for RuntimeError { } } -impl From for RuntimeError { - fn from(e: error::ApiError) -> Self { +impl From for RuntimeError { + fn from(e: ApiError) -> Self { let mut err = RuntimeError::new(e.description()); err.recoverable = e.recoverable; - err.stack_trace = e.backtrace; err } } - -/// The error type for functions that are used as the `Handler` type. New errors -/// should be instantiated using the `new_error()` method of the `runtime::Context` -/// object passed to the handler function. -#[derive(Debug, Clone)] -pub struct HandlerError { - msg: String, - backtrace: Option, -} - -impl fmt::Display for HandlerError { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.msg) - } -} - -// This is important for other errors to wrap this one. -impl Error for HandlerError { - fn description(&self) -> &str { - &self.msg - } - - fn cause(&self) -> Option<&Error> { - // Generic error, underlying cause isn't tracked. - None - } -} - -impl HandlerError { - /// Creates a new handler error. This method is used by the `new_error()` method - /// of the `runtime::Context` object. - /// - /// # Arguments - /// - /// * `msg` The error message for the new error - /// * `trace` A `Backtrace` object to generate the stack trace for the error - /// response. This is provided by the `Context` object. - pub(crate) fn new(msg: &str, trace: Option) -> HandlerError { - HandlerError { - msg: msg.to_string(), - backtrace: trace, - } - } -} - -impl error::RuntimeApiError for HandlerError { - fn to_response(&self) -> error::ErrorResponse { - let backtrace = format!("{:?}", self.backtrace); - error::ErrorResponse { - error_message: String::from(self.description()), - error_type: String::from(error::ERROR_TYPE_HANDLED), - stack_trace: Option::from(backtrace.lines().map(|s| s.to_string()).collect::>()), - } - } -} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 36448d00..3b776f7c 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -7,13 +7,13 @@ //! package must be called `bootstrap`. //! //! ```rust,no_run -//! #[macro_use] //! extern crate serde_derive; -//! #[macro_use] //! extern crate lambda_runtime; +//! extern crate simple_error; //! -//! use lambda_runtime::error::HandlerError; -//! +//! use lambda_runtime::{HandlerError, lambda}; +//! use simple_error::bail; +//! use serde_derive::{Serialize, Deserialize}; //! //! #[derive(Deserialize, Clone)] //! struct CustomEvent { @@ -32,7 +32,7 @@ //! //! fn my_handler(e: CustomEvent, ctx: lambda_runtime::Context) -> Result { //! if e.first_name == "" { -//! return Err(ctx.new_error("Missing first name!")); +//! bail!("Empty first name"); //! } //! Ok(CustomOutput{ //! message: format!("Hello, {}!", e.first_name), @@ -51,8 +51,9 @@ extern crate tokio; mod context; mod env; -pub mod error; +mod error; mod runtime; pub use context::*; +pub use error::HandlerError; pub use runtime::*; diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 770788f4..6b56a690 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -1,4 +1,4 @@ -use std::{error::Error, marker::PhantomData, result}; +use std::{marker::PhantomData, result}; use serde; use serde_json; @@ -6,7 +6,7 @@ use serde_json; use context::Context; use env::{ConfigProvider, EnvConfigProvider, FunctionSettings}; use error::{HandlerError, RuntimeError}; -use lambda_runtime_client::RuntimeClient; +use lambda_runtime_client::{error::ErrorResponse, RuntimeClient}; use tokio::runtime::Runtime as TokioRuntime; const MAX_RETRIES: i8 = 3; @@ -215,7 +215,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking.", request_id ); - self.runtime_client.fail_init(&e); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Could not send response"); } } @@ -226,8 +226,7 @@ where "Could not marshal output object to Vec JSON represnetation for request {}: {}", request_id, e ); - self.runtime_client - .fail_init(&RuntimeError::unrecoverable(e.description())); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Failed to marshal handler output, panic"); } } @@ -235,7 +234,10 @@ where Err(e) => { debug!("Handler returned an error for {}: {}", request_id, e); debug!("Attempting to send error response to Runtime API for {}", request_id); - match self.runtime_client.event_error(&request_id, &e) { + match self + .runtime_client + .event_error(&request_id, ErrorResponse::from(Box::new(e))) + { Ok(_) => info!("Error response for {} accepted by Runtime API", request_id), Err(e) => { error!("Unable to send error response for {} to Runtime API: {}", request_id, e); @@ -244,7 +246,7 @@ where "Error for {} is not recoverable, sending fail_init signal and panicking", request_id ); - self.runtime_client.fail_init(&e); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(e))); panic!("Could not send error response"); } } @@ -272,11 +274,11 @@ where match err.request_id.clone() { Some(req_id) => { self.runtime_client - .event_error(&req_id, &err) + .event_error(&req_id, ErrorResponse::from(Box::new(err))) .expect("Could not send event error response"); } None => { - self.runtime_client.fail_init(&err); + self.runtime_client.fail_init(ErrorResponse::from(Box::new(err))); } } @@ -356,7 +358,7 @@ pub(crate) mod tests { "Handler threw an unexpected error: {}", output.err().unwrap() ); - let output_string = output.unwrap(); + let output_string = output.ok().unwrap(); assert_eq!(output_string, "hello", "Unexpected output message: {}", output_string); } }