From a73212f477d0652a530e40824db9cb8a5b38e9cd Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 5 Jul 2024 13:40:55 -0700 Subject: [PATCH 1/5] Error handling improvements Change how we handle error types to be more ergonomic: - Replace Cow with String: Diagnostics are serialized into JSON as soon as the function returns, which means that their value is copied right away. The performance improvement of using Cow is minimal in this case, but it has some ergonomic implications because we have to handle their lifetimes. By removing the explicit lifetimes, people can return Diagnostic values with static lifetimes which was not possible before. - Add `IntoDiagnostic` trait. This is a helper trait to facilitate transforming value types into Diagnostic. It gives external crates a better mechanism to transform values into `Diagnostic`. - Add features to implement `IntoDiagnostic` for anyhow, eyre, and miette error types. This helps people that use those creates to transform their errors into `Diagnostic` without double boxing their errors. --- README.md | 57 +++- examples/basic-error-anyhow/Cargo.toml | 10 - examples/basic-error-anyhow/src/main.rs | 21 -- .../.gitignore | 0 .../Cargo.toml | 12 + .../README.md | 6 +- .../src/main.rs | 44 +++ .../.gitignore | 0 .../Cargo.toml | 2 +- .../README.md | 0 .../src/main.rs | 4 +- lambda-http/Cargo.toml | 3 + lambda-http/src/lib.rs | 2 +- lambda-http/src/streaming.rs | 2 +- lambda-runtime/Cargo.toml | 6 + lambda-runtime/src/diagnostic.rs | 252 ++++++++++++++---- lambda-runtime/src/layers/api_response.rs | 21 +- lambda-runtime/src/layers/panic.rs | 27 +- lambda-runtime/src/lib.rs | 4 +- lambda-runtime/src/requests.rs | 8 +- lambda-runtime/src/runtime.rs | 10 +- 21 files changed, 365 insertions(+), 126 deletions(-) delete mode 100644 examples/basic-error-anyhow/Cargo.toml delete mode 100644 examples/basic-error-anyhow/src/main.rs rename examples/{basic-error-anyhow => basic-error-error-crates-integration}/.gitignore (100%) create mode 100644 examples/basic-error-error-crates-integration/Cargo.toml rename examples/{basic-error-anyhow => basic-error-error-crates-integration}/README.md (54%) create mode 100644 examples/basic-error-error-crates-integration/src/main.rs rename examples/{basic-error-diagnostic => basic-error-thiserror}/.gitignore (100%) rename examples/{basic-error-diagnostic => basic-error-thiserror}/Cargo.toml (94%) rename examples/{basic-error-diagnostic => basic-error-thiserror}/README.md (100%) rename examples/{basic-error-diagnostic => basic-error-thiserror}/src/main.rs (90%) diff --git a/README.md b/README.md index 44ec1983..b879712b 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ pip3 install cargo-lambda See other installation options in [the Cargo Lambda documentation](https://www.cargo-lambda.info/guide/installation.html). -### Your first function +## Your first function To create your first function, run Cargo Lambda with the [subcommand `new`](https://www.cargo-lambda.info/commands/new.html). This command will generate a Rust package with the initial source code for your function: @@ -71,6 +71,61 @@ async fn func(event: LambdaEvent) -> Result { } ``` +## Understanding Lambda errors + +when a function invocation fails, AWS Lambda expects you to return an object that can be serialized into JSON structure with the error information. This structure is represented in the following example: + +```json +{ + "error_type": "the type of error raised", + "error_message": "a string description of the error" +} +``` + +The Rust Runtime for Lambda uses a struct called `Diagnostic` to represent function errors internally. The runtime implements the converstion of several general errors types, like `std::error::Error`, into `Diagnostic`. For these general implementations, the `error_type` is the name of the value type returned by your function. For example, if your function returns `lambda_runtime::Error`, the `error_type` will be something like `alloc::boxed::Box`, which is not very descriptive. + +### Implement your own Diagnostic + +To get more descriptive `error_type` fields, you can implement `Into` for your error type. That gives you full control on what the `error_type` is: + +```rust +use lambda_runtime::{Diagnostic, Error, LambdaEvent}; + +#[derive(Debug)] +struct ErrorResponse(&'static str); + +impl Into for ErrorResponse { + fn into(self) -> Diagnostic { + Diagnostic { + error_type: "MyErrorType".into(), + error_message: self.0.to_string(), + } + } +} + +async fn handler(_event: LambdaEvent<()>) -> Result<(), ErrorResponse> { + Err(ErrorResponse("this is an error response")) +} +``` + +We recommend you to use the [thiserror crate](https://crates.io/crates/thiserror) to declare your errors. You can see an example on how to integrate `thiserror` with the Runtime's diagnostics in our [example repository](https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples/basic-error-thiserror) + +### Anyhow, Eyre, and Miette + +Popular error crates like Anyhow, Eyre, and Miette provide their own error types that encapsulate other errors. There is no direct transformation of those errors into `Diagnostic`, but we provide feature flags for each one of those crates to help you integrate them with your Lambda functions. + +If you enable the features `anyhow`, `eyre`, or `miette` in the `lambda_runtime` dependency of your package. The error types provided by those crates can have blanket transformations into `Diagnostic` when the `lambda_runtime::IntoDiagnostic` trait is in scope. This trait exposes an `into_diagnostic` method that transforms those error types into a `Diagnostic`. This is an example that transforms an `anyhow::Error` into a `Diagnostic`: + +```rust +use lambda_runtime::{Diagnostic, IntoDiagnostic, LambdaEvent}; + +async fn handler(_event: LambdaEvent) -> Result<(), Diagnostic> { + Err(anyhow::anyhow!("this is an error").into_diagnostic()) +} +``` + +You can see more examples on how to use these error crates in our [example repository](https://github.com/awslabs/aws-lambda-rust-runtime/tree/main/examples/basic-error-error-crates-integration). + ## Building and deploying your Lambda functions If you already have Cargo Lambda installed in your machine, run the next command to build your function: diff --git a/examples/basic-error-anyhow/Cargo.toml b/examples/basic-error-anyhow/Cargo.toml deleted file mode 100644 index a0ff62db..00000000 --- a/examples/basic-error-anyhow/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "basic-error-anyhow" -version = "0.1.0" -edition = "2021" - -[dependencies] -anyhow = "1" -lambda_runtime = { path = "../../lambda-runtime" } -serde = "1" -tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-error-anyhow/src/main.rs b/examples/basic-error-anyhow/src/main.rs deleted file mode 100644 index cbca84fd..00000000 --- a/examples/basic-error-anyhow/src/main.rs +++ /dev/null @@ -1,21 +0,0 @@ -use anyhow::bail; -use lambda_runtime::{service_fn, Error, LambdaEvent}; -use serde::Deserialize; - -#[derive(Deserialize)] -struct Request {} - -/// Return anyhow::Result in the main body for the Lambda function. -async fn function_handler(_event: LambdaEvent) -> anyhow::Result<()> { - bail!("This is an error message"); -} - -#[tokio::main] -async fn main() -> Result<(), Error> { - lambda_runtime::run(service_fn(|event: LambdaEvent| async move { - function_handler(event) - .await - .map_err(Into::>::into) - })) - .await -} diff --git a/examples/basic-error-anyhow/.gitignore b/examples/basic-error-error-crates-integration/.gitignore similarity index 100% rename from examples/basic-error-anyhow/.gitignore rename to examples/basic-error-error-crates-integration/.gitignore diff --git a/examples/basic-error-error-crates-integration/Cargo.toml b/examples/basic-error-error-crates-integration/Cargo.toml new file mode 100644 index 00000000..741ec713 --- /dev/null +++ b/examples/basic-error-error-crates-integration/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "basic-error-error-crates-integration" +version = "0.1.0" +edition = "2021" + +[dependencies] +anyhow = "1" +eyre = "0.6.12" +lambda_runtime = { path = "../../lambda-runtime", features = ["anyhow", "eyre", "miette"] } +miette = "7.2.0" +serde = "1" +tokio = { version = "1", features = ["macros"] } diff --git a/examples/basic-error-anyhow/README.md b/examples/basic-error-error-crates-integration/README.md similarity index 54% rename from examples/basic-error-anyhow/README.md rename to examples/basic-error-error-crates-integration/README.md index b659c283..2e46c55d 100644 --- a/examples/basic-error-anyhow/README.md +++ b/examples/basic-error-error-crates-integration/README.md @@ -1,6 +1,8 @@ -# AWS Lambda Function Error Handling With `anyhow` Crate Example +# AWS Lambda Function Error Handling with several popular error crates. -This example shows how to use external error types like `anyhow::Error`. +This example shows how to use external error types like `anyhow::Error`, `eyre::Report`, and `miette::Report`. + +To use the integrations with these crates, you need to enable to respective feature flag in the runtime which provides the implemetation of `into_diagnostic` for specific error types provided by these crates. ## Build & Deploy diff --git a/examples/basic-error-error-crates-integration/src/main.rs b/examples/basic-error-error-crates-integration/src/main.rs new file mode 100644 index 00000000..203e65bf --- /dev/null +++ b/examples/basic-error-error-crates-integration/src/main.rs @@ -0,0 +1,44 @@ +use lambda_runtime::{run, service_fn, Diagnostic, IntoDiagnostic, Error, LambdaEvent}; +use serde::Deserialize; + +#[derive(Deserialize)] +#[serde(rename_all = "camelCase")] +enum ErrorType { + Anyhow, + Eyre, + Miette, +} + +#[derive(Deserialize)] +struct Request { + error_type: ErrorType, +} + +fn anyhow_error() -> anyhow::Result<()> { + anyhow::bail!("This is an error message from Anyhow"); +} + +fn eyre_error() -> eyre::Result<()> { + eyre::bail!("This is an error message from Eyre"); +} + +fn miette_error() -> miette::Result<()> { + miette::bail!("This is an error message from Miette"); +} + +/// Transform an anyhow::Error, eyre::Report, or miette::Report into a lambda_runtime::Diagnostic. +/// It does it by enabling the feature `anyhow`, `eyre` or `miette` in the runtime dependency, +/// and importing the `IntoDiagnostic` trait, which enables +/// the implementation of `into_diagnostic` for `anyhow::Error`, `eyre::Report`, and `miette::Report`. +async fn function_handler(event: LambdaEvent) -> Result<(), Diagnostic> { + match event.payload.error_type { + ErrorType::Anyhow => anyhow_error().map_err(|e| e.into_diagnostic()), + ErrorType::Eyre => eyre_error().map_err(|e| e.into_diagnostic()), + ErrorType::Miette => miette_error().map_err(|e| e.into_diagnostic()), + } +} + +#[tokio::main] +async fn main() -> Result<(), Error> { + run(service_fn(function_handler)).await +} diff --git a/examples/basic-error-diagnostic/.gitignore b/examples/basic-error-thiserror/.gitignore similarity index 100% rename from examples/basic-error-diagnostic/.gitignore rename to examples/basic-error-thiserror/.gitignore diff --git a/examples/basic-error-diagnostic/Cargo.toml b/examples/basic-error-thiserror/Cargo.toml similarity index 94% rename from examples/basic-error-diagnostic/Cargo.toml rename to examples/basic-error-thiserror/Cargo.toml index b81ef730..d7c7d725 100644 --- a/examples/basic-error-diagnostic/Cargo.toml +++ b/examples/basic-error-thiserror/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "basic-error-diagnostic" +name = "basic-error-thiserror" version = "0.1.0" edition = "2021" diff --git a/examples/basic-error-diagnostic/README.md b/examples/basic-error-thiserror/README.md similarity index 100% rename from examples/basic-error-diagnostic/README.md rename to examples/basic-error-thiserror/README.md diff --git a/examples/basic-error-diagnostic/src/main.rs b/examples/basic-error-thiserror/src/main.rs similarity index 90% rename from examples/basic-error-diagnostic/src/main.rs rename to examples/basic-error-thiserror/src/main.rs index 11f68d4b..403309bf 100644 --- a/examples/basic-error-diagnostic/src/main.rs +++ b/examples/basic-error-thiserror/src/main.rs @@ -13,8 +13,8 @@ pub enum ExecutionError { Unexpected(String), } -impl<'a> From for Diagnostic<'a> { - fn from(value: ExecutionError) -> Diagnostic<'a> { +impl From for Diagnostic { + fn from(value: ExecutionError) -> Diagnostic { let (error_type, error_message) = match value { ExecutionError::DatabaseError(err) => ("Retryable", err.to_string()), ExecutionError::Unexpected(err) => ("NonRetryable", err.to_string()), diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index 1165084b..ddd509fc 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -23,6 +23,9 @@ apigw_websockets = [] alb = [] pass_through = [] tracing = ["lambda_runtime/tracing"] +anyhow = ["lambda_runtime/anyhow"] +eyre = ["lambda_runtime/eyre"] +miette = ["lambda_runtime/miette"] [dependencies] base64 = { workspace = true } diff --git a/lambda-http/src/lib.rs b/lambda-http/src/lib.rs index 90e59867..233d6992 100644 --- a/lambda-http/src/lib.rs +++ b/lambda-http/src/lib.rs @@ -194,7 +194,7 @@ where S: Service, S::Future: Send + 'a, R: IntoResponse, - E: std::fmt::Debug + for<'b> Into>, + E: std::fmt::Debug + Into, { lambda_runtime::run(Adapter::from(handler)).await } diff --git a/lambda-http/src/streaming.rs b/lambda-http/src/streaming.rs index ad3471d3..a93408b4 100644 --- a/lambda-http/src/streaming.rs +++ b/lambda-http/src/streaming.rs @@ -20,7 +20,7 @@ pub async fn run_with_streaming_response<'a, S, B, E>(handler: S) -> Result<(), where S: Service, Error = E>, S::Future: Send + 'a, - E: Debug + for<'b> Into>, + E: Debug + Into, B: Body + Unpin + Send + 'static, B::Data: Into + Send, B::Error: Into + Send + Debug, diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 9e56d05b..c9965f28 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -17,11 +17,16 @@ readme = "../README.md" default = ["tracing"] tracing = ["lambda_runtime_api_client/tracing"] opentelemetry = ["opentelemetry-semantic-conventions"] +anyhow = ["dep:anyhow"] +eyre = ["dep:eyre"] +miette = ["dep:miette"] [dependencies] +anyhow = { version = "1.0.86", optional = true } async-stream = "0.3" base64 = { workspace = true } bytes = { workspace = true } +eyre = { version = "0.6.12", optional = true } futures = { workspace = true } http = { workspace = true } http-body = { workspace = true } @@ -35,6 +40,7 @@ hyper-util = { workspace = true, features = [ "tokio", ] } lambda_runtime_api_client = { version = "0.11.1", path = "../lambda-runtime-api-client", default-features = false } +miette = { version = "7.2.0", optional = true } opentelemetry-semantic-conventions = { version = "0.14", optional = true } pin-project = "1" serde = { version = "1", features = ["derive", "rc"] } diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index 9a7230a7..399d650d 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -1,5 +1,5 @@ use serde::{Deserialize, Serialize}; -use std::{any::type_name, borrow::Cow}; +use std::any::type_name; use crate::{deserializer::DeserializeError, Error}; @@ -20,108 +20,234 @@ use crate::{deserializer::DeserializeError, Error}; /// Example: /// ``` /// use lambda_runtime::{Diagnostic, Error, LambdaEvent}; -/// use std::borrow::Cow; /// /// #[derive(Debug)] -/// struct ErrorResponse(Error); +/// struct ErrorResponse(&'static str); /// -/// impl<'a> Into> for ErrorResponse { -/// fn into(self) -> Diagnostic<'a> { +/// impl Into for ErrorResponse { +/// fn into(self) -> Diagnostic { /// Diagnostic { /// error_type: "MyError".into(), -/// error_message: self.0.to_string().into(), +/// error_message: self.0.to_string(), /// } /// } /// } /// /// async fn function_handler(_event: LambdaEvent<()>) -> Result<(), ErrorResponse> { -/// // ... do something -/// Ok(()) +/// Err(ErrorResponse("this is an error response")) /// } /// ``` #[derive(Debug, Eq, PartialEq, Clone, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] -pub struct Diagnostic<'a> { - /// Error type. +pub struct Diagnostic { + /// `error_type` is the type of exception or error returned by the function. + /// Use this field to categorize the different kinds of errors that your function + /// might experience. /// - /// `error_type` is derived from the type name of the original error with - /// [`std::any::type_name`] as a fallback. - /// Please implement your own `Into` if you need more reliable - /// error types. - pub error_type: Cow<'a, str>, - /// Error message. - /// - /// `error_message` is the output from the [`Display`][std::fmt::Display] - /// implementation of the original error as a fallback. - pub error_message: Cow<'a, str>, + /// In standard implementations, `error_type` is derived from the type name of the original error with + /// [`std::any::type_name`], however this is not descriptive enough for an error type. + /// Implement your own `Into` to return a more descriptive error type. + pub error_type: String, + /// `error_message` is a string expression of the error. + /// In standard implementations, it's the output from the [`Display`][std::fmt::Display] + /// implementation of the original error. + pub error_message: String, } -impl<'a> From for Diagnostic<'a> { - fn from(value: DeserializeError) -> Self { +/// Trait that adds an `into_diagnostic` method to several types, +/// including standard errors, and error types from several well known error crates. +/// +/// This trait is automatically implemented for some common types, +/// like boxed types that implement [`Error`][std::error::Error]. +/// If you use an error type which comes from a external crate like anyhow, eyre, or miette, +/// you can enable the features `anyhow`, `eyre` or `miette` in the `lambda_runtime` crate to automatically +/// implement `IntoDiagnostic`. +/// +/// Example: +/// ``` +/// use lambda_runtime::{Diagnostic, IntoDiagnostic, LambdaEvent}; +/// +/// #[derive(Debug)] +/// struct ErrorResponse(&'static str); +/// +/// impl IntoDiagnostic for ErrorResponse { +/// fn into_diagnostic(self) -> Diagnostic { +/// Diagnostic { +/// error_type: "MyError".into(), +/// error_message: self.0.into(), +/// } +/// } +/// } +/// +/// async fn function_handler(_event: LambdaEvent<()>) -> Result<(), Diagnostic> { +/// Err(ErrorResponse("this is an error response").into_diagnostic()) +/// } +/// ``` +pub trait IntoDiagnostic { + /// Converts external error types into [`Diagnostic`] + fn into_diagnostic(self) -> Diagnostic; +} + +impl IntoDiagnostic for DeserializeError { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::().into(), - error_message: value.to_string().into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } -impl<'a> From for Diagnostic<'a> { - fn from(value: Error) -> Self { +impl IntoDiagnostic for Error { + fn into_diagnostic(self) -> Diagnostic { + Diagnostic { + error_type: type_name_of_val(&self), + error_message: self.to_string(), + } + } +} + +impl IntoDiagnostic for Box { + fn into_diagnostic(self) -> Diagnostic { + Diagnostic { + error_type: type_name_of_val(&self), + error_message: self.to_string(), + } + } +} + +impl IntoDiagnostic for std::convert::Infallible { + fn into_diagnostic(self) -> Diagnostic { + Diagnostic { + error_type: type_name_of_val(&self), + error_message: self.to_string(), + } + } +} + +impl IntoDiagnostic for String { + fn into_diagnostic(self) -> Diagnostic { + Diagnostic { + error_type: type_name_of_val(&self), + error_message: self, + } + } +} + +impl IntoDiagnostic for &'static str { + fn into_diagnostic(self) -> Diagnostic { + Diagnostic { + error_type: type_name_of_val(&self), + error_message: self.into(), + } + } +} + +impl IntoDiagnostic for std::io::Error { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::().into(), - error_message: value.to_string().into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } -impl<'a, T> From> for Diagnostic<'a> +impl IntoDiagnostic for Box where T: std::error::Error, { - fn from(value: Box) -> Self { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::().into(), - error_message: value.to_string().into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } -impl<'a> From> for Diagnostic<'a> { +impl From for Diagnostic { + fn from(value: DeserializeError) -> Self { + value.into_diagnostic() + } +} + +impl From for Diagnostic { + fn from(value: Error) -> Self { + value.into_diagnostic() + } +} + +impl From> for Diagnostic +where + T: std::error::Error, +{ + fn from(value: Box) -> Self { + value.into_diagnostic() + } +} + +impl From> for Diagnostic { fn from(value: Box) -> Self { - Diagnostic { - error_type: type_name::>().into(), - error_message: value.to_string().into(), - } + value.into_diagnostic() } } -impl<'a> From for Diagnostic<'a> { +impl From for Diagnostic { fn from(value: std::convert::Infallible) -> Self { + value.into_diagnostic() + } +} + +impl From for Diagnostic { + fn from(value: String) -> Self { + value.into_diagnostic() + } +} + +impl From<&'static str> for Diagnostic { + fn from(value: &'static str) -> Self { + value.into_diagnostic() + } +} + +impl From for Diagnostic { + fn from(value: std::io::Error) -> Self { + value.into_diagnostic() + } +} + +#[cfg(feature = "anyhow")] +impl IntoDiagnostic for anyhow::Error { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::().into(), - error_message: value.to_string().into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } -impl<'a> From for Diagnostic<'a> { - fn from(value: String) -> Self { +#[cfg(feature = "eyre")] +impl IntoDiagnostic for eyre::Report { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::().into(), - error_message: value.into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } -impl<'a> From<&'static str> for Diagnostic<'a> { - fn from(value: &'static str) -> Self { +#[cfg(feature = "miette")] +impl IntoDiagnostic for miette::Report { + fn into_diagnostic(self) -> Diagnostic { Diagnostic { - error_type: type_name::<&'static str>().into(), - error_message: value.into(), + error_type: type_name_of_val(&self), + error_message: self.to_string(), } } } +pub(crate) fn type_name_of_val(_: T) -> String { + type_name::().into() +} + #[cfg(test)] mod test { use super::*; @@ -141,4 +267,34 @@ mod test { let actual: Value = serde_json::to_value(actual).expect("failed to serialize diagnostic"); assert_eq!(expected, actual); } + + #[cfg(feature = "anyhow")] + #[test] + fn test_anyhow_integration() { + use anyhow::Error as AnyhowError; + let error: AnyhowError = anyhow::anyhow!("anyhow error"); + let diagnostic: Diagnostic = error.into_diagnostic(); + assert_eq!(diagnostic.error_type, "&anyhow::Error"); + assert_eq!(diagnostic.error_message, "anyhow error"); + } + + #[cfg(feature = "eyre")] + #[test] + fn test_eyre_integration() { + use eyre::Report; + let error: Report = eyre::eyre!("eyre error"); + let diagnostic: Diagnostic = error.into_diagnostic(); + assert_eq!(diagnostic.error_type, "&eyre::Report"); + assert_eq!(diagnostic.error_message, "eyre error"); + } + + #[cfg(feature = "miette")] + #[test] + fn test_miette_integration() { + use miette::Report; + let error: Report = miette::miette!("miette error"); + let diagnostic: Diagnostic = error.into_diagnostic(); + assert_eq!(diagnostic.error_type, "&miette::eyreish::Report"); + assert_eq!(diagnostic.error_message, "miette error"); + } } diff --git a/lambda-runtime/src/layers/api_response.rs b/lambda-runtime/src/layers/api_response.rs index 7c963f68..e744cde1 100644 --- a/lambda-runtime/src/layers/api_response.rs +++ b/lambda-runtime/src/layers/api_response.rs @@ -51,8 +51,7 @@ impl - Service +impl Service for RuntimeApiResponseService< S, EventPayload, @@ -63,7 +62,7 @@ impl<'a, S, EventPayload, Response, BufferedResponse, StreamingResponse, StreamI StreamError, > where - S: Service, Response = Response, Error = Diagnostic<'a>>, + S: Service, Response = Response, Error = Diagnostic>, EventPayload: for<'de> Deserialize<'de>, Response: IntoFunctionResponse, BufferedResponse: Serialize, @@ -74,7 +73,7 @@ where type Response = http::Request; type Error = BoxError; type Future = - RuntimeApiResponseFuture<'a, S::Future, Response, BufferedResponse, StreamingResponse, StreamItem, StreamError>; + RuntimeApiResponseFuture; fn poll_ready(&mut self, cx: &mut task::Context<'_>) -> task::Poll> { self.inner @@ -120,21 +119,21 @@ where } } -fn build_event_error_request<'a, T>(request_id: &'a str, err: T) -> Result, BoxError> +fn build_event_error_request(request_id: &str, err: T) -> Result, BoxError> where - T: Into> + Debug, + T: Into + Debug, { error!(error = ?err, "Request payload deserialization into LambdaEvent failed. The handler will not be called. Log at TRACE level to see the payload."); EventErrorRequest::new(request_id, err).into_req() } #[pin_project(project = RuntimeApiResponseFutureProj)] -pub enum RuntimeApiResponseFuture<'a, F, Response, BufferedResponse, StreamingResponse, StreamItem, StreamError> { +pub enum RuntimeApiResponseFuture { Future( #[pin] F, String, PhantomData<( - &'a (), + (), Response, BufferedResponse, StreamingResponse, @@ -145,10 +144,10 @@ pub enum RuntimeApiResponseFuture<'a, F, Response, BufferedResponse, StreamingRe Ready(Option, BoxError>>), } -impl<'a, F, Response, BufferedResponse, StreamingResponse, StreamItem, StreamError> Future - for RuntimeApiResponseFuture<'a, F, Response, BufferedResponse, StreamingResponse, StreamItem, StreamError> +impl Future + for RuntimeApiResponseFuture where - F: Future>>, + F: Future>, Response: IntoFunctionResponse, BufferedResponse: Serialize, StreamingResponse: Stream> + Unpin + Send + 'static, diff --git a/lambda-runtime/src/layers/panic.rs b/lambda-runtime/src/layers/panic.rs index 036b747b..c76348ac 100644 --- a/lambda-runtime/src/layers/panic.rs +++ b/lambda-runtime/src/layers/panic.rs @@ -1,9 +1,7 @@ -use crate::{Diagnostic, LambdaEvent}; +use crate::{diagnostic::type_name_of_val, Diagnostic, LambdaEvent}; use futures::{future::CatchUnwind, FutureExt}; use pin_project::pin_project; -use std::{ - any::Any, borrow::Cow, fmt::Debug, future::Future, marker::PhantomData, panic::AssertUnwindSafe, pin::Pin, task, -}; +use std::{any::Any, fmt::Debug, future::Future, marker::PhantomData, panic::AssertUnwindSafe, pin::Pin, task}; use tower::Service; use tracing::error; @@ -33,9 +31,9 @@ impl<'a, S, Payload> Service> for CatchPanicService<'a, S> where S: Service>, S::Future: 'a, - S::Error: Into> + Debug, + S::Error: Into + Debug, { - type Error = Diagnostic<'a>; + type Error = Diagnostic; type Response = S::Response; type Future = CatchPanicFuture<'a, S::Future>; @@ -71,9 +69,9 @@ pub enum CatchPanicFuture<'a, F> { impl<'a, F, T, E> Future for CatchPanicFuture<'a, F> where F: Future>, - E: Into> + Debug, + E: Into + Debug, { - type Output = Result>; + type Output = Result; fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> task::Poll { use task::Poll; @@ -98,20 +96,15 @@ where } impl<'a, F> CatchPanicFuture<'a, F> { - fn build_panic_diagnostic(err: &Box) -> Diagnostic<'a> { - let error_type = type_name_of_val(&err); - let msg = if let Some(msg) = err.downcast_ref::<&str>() { + fn build_panic_diagnostic(err: &Box) -> Diagnostic { + let error_message = if let Some(msg) = err.downcast_ref::<&str>() { format!("Lambda panicked: {msg}") } else { "Lambda panicked".to_string() }; Diagnostic { - error_type: Cow::Borrowed(error_type), - error_message: Cow::Owned(msg), + error_type: type_name_of_val(err), + error_message, } } } - -fn type_name_of_val(_: T) -> &'static str { - std::any::type_name::() -} diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index c3e68cc4..9870455f 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -20,7 +20,7 @@ pub use tower::{self, service_fn, Service}; /// Diagnostic utilities to convert Rust types into Lambda Error types. pub mod diagnostic; -pub use diagnostic::Diagnostic; +pub use diagnostic::{Diagnostic, IntoDiagnostic}; mod deserializer; /// Tower middleware to be applied to runtime invocations. @@ -112,7 +112,7 @@ pub async fn run(handler: F) -> Result<(), Error> where F: Service, Response = R>, F::Future: Future>, - F::Error: for<'a> Into> + fmt::Debug, + F::Error: Into + fmt::Debug, A: for<'de> Deserialize<'de>, R: IntoFunctionResponse, B: Serialize, diff --git a/lambda-runtime/src/requests.rs b/lambda-runtime/src/requests.rs index 0e2eb59a..535f1e8e 100644 --- a/lambda-runtime/src/requests.rs +++ b/lambda-runtime/src/requests.rs @@ -158,11 +158,11 @@ fn test_event_completion_request() { // /runtime/invocation/{AwsRequestId}/error pub(crate) struct EventErrorRequest<'a> { pub(crate) request_id: &'a str, - pub(crate) diagnostic: Diagnostic<'a>, + pub(crate) diagnostic: Diagnostic, } impl<'a> EventErrorRequest<'a> { - pub(crate) fn new(request_id: &'a str, diagnostic: impl Into>) -> EventErrorRequest<'a> { + pub(crate) fn new(request_id: &'a str, diagnostic: impl Into) -> EventErrorRequest<'a> { EventErrorRequest { request_id, diagnostic: diagnostic.into(), @@ -224,8 +224,8 @@ mod tests { let req = EventErrorRequest { request_id: "id", diagnostic: Diagnostic { - error_type: std::borrow::Cow::Borrowed("InvalidEventDataError"), - error_message: std::borrow::Cow::Borrowed("Error parsing event data"), + error_type: "InvalidEventDataError".into(), + error_message: "Error parsing event data".into(), }, }; let req = req.into_req().unwrap(); diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 56c8efad..1c676480 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -72,7 +72,7 @@ impl<'a, F, EventPayload, Response, BufferedResponse, StreamingResponse, StreamI where F: Service, Response = Response>, F::Future: Future>, - F::Error: Into> + Debug, + F::Error: Into + Debug, EventPayload: for<'de> Deserialize<'de>, Response: IntoFunctionResponse, BufferedResponse: Serialize, @@ -207,7 +207,7 @@ fn wrap_handler<'a, F, EventPayload, Response, BufferedResponse, StreamingRespon where F: Service, Response = Response>, F::Future: Future>, - F::Error: Into> + Debug, + F::Error: Into + Debug, EventPayload: for<'de> Deserialize<'de>, Response: IntoFunctionResponse, BufferedResponse: Serialize, @@ -257,7 +257,7 @@ mod endpoint_tests { use httpmock::prelude::*; use lambda_runtime_api_client::Client; - use std::{borrow::Cow, env, sync::Arc}; + use std::{env, sync::Arc}; use tokio_stream::StreamExt; #[tokio::test] @@ -324,8 +324,8 @@ mod endpoint_tests { #[tokio::test] async fn test_error_response() -> Result<(), Error> { let diagnostic = Diagnostic { - error_type: Cow::Borrowed("InvalidEventDataError"), - error_message: Cow::Borrowed("Error parsing event data"), + error_type: "InvalidEventDataError".into(), + error_message: "Error parsing event data".into(), }; let body = serde_json::to_string(&diagnostic)?; From 0e10bdd40d0a55c6d51fd4e160e749bc3b7f84fc Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 8 Jul 2024 08:35:21 -0700 Subject: [PATCH 2/5] Recommend to implement `From`. Signed-off-by: David Calavera --- README.md | 8 ++++---- lambda-runtime/src/diagnostic.rs | 11 ++++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index b879712b..a71c041c 100644 --- a/README.md +++ b/README.md @@ -86,7 +86,7 @@ The Rust Runtime for Lambda uses a struct called `Diagnostic` to represent funct ### Implement your own Diagnostic -To get more descriptive `error_type` fields, you can implement `Into` for your error type. That gives you full control on what the `error_type` is: +To get more descriptive `error_type` fields, you can implement `From` for your error type. That gives you full control on what the `error_type` is: ```rust use lambda_runtime::{Diagnostic, Error, LambdaEvent}; @@ -94,11 +94,11 @@ use lambda_runtime::{Diagnostic, Error, LambdaEvent}; #[derive(Debug)] struct ErrorResponse(&'static str); -impl Into for ErrorResponse { - fn into(self) -> Diagnostic { +impl From for Diagnostic { + fn from(error: ErrorResponse) -> Diagnostic { Diagnostic { error_type: "MyErrorType".into(), - error_message: self.0.to_string(), + error_message: error.0.to_string(), } } } diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index 399d650d..22aaeb67 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -14,8 +14,9 @@ use crate::{deserializer::DeserializeError, Error}; /// [`error_type`][`Diagnostic::error_type`] is derived from the type name of /// the original error with [`std::any::type_name`] as a fallback, which may /// not be reliable for conditional error handling. -/// You can define your own error container that implements `Into` -/// if you need to handle errors based on error types. +/// +/// To get more descriptive [`error_type`][`Diagnostic::error_type`] fields, you can implement `From` for your error type. +/// That gives you full control on what the `error_type` is. /// /// Example: /// ``` @@ -24,11 +25,11 @@ use crate::{deserializer::DeserializeError, Error}; /// #[derive(Debug)] /// struct ErrorResponse(&'static str); /// -/// impl Into for ErrorResponse { -/// fn into(self) -> Diagnostic { +/// impl From for Diagnostic { +/// fn from(error: ErrorResponse) -> Diagnostic { /// Diagnostic { /// error_type: "MyError".into(), -/// error_message: self.0.to_string(), +/// error_message: error.0.to_string(), /// } /// } /// } From fa3347f74d080f4fab4b29c490b05e885456b709 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Mon, 8 Jul 2024 17:20:41 -0700 Subject: [PATCH 3/5] Simplify implementation, remove `IntoDiagnostic` We can implement `From for Diagnostic` for error crates. --- README.md | 6 +- .../src/main.rs | 14 +- lambda-runtime/src/diagnostic.rs | 179 +++++------------- lambda-runtime/src/lib.rs | 2 +- 4 files changed, 58 insertions(+), 143 deletions(-) diff --git a/README.md b/README.md index a71c041c..31ba399b 100644 --- a/README.md +++ b/README.md @@ -114,13 +114,13 @@ We recommend you to use the [thiserror crate](https://crates.io/crates/thiserror Popular error crates like Anyhow, Eyre, and Miette provide their own error types that encapsulate other errors. There is no direct transformation of those errors into `Diagnostic`, but we provide feature flags for each one of those crates to help you integrate them with your Lambda functions. -If you enable the features `anyhow`, `eyre`, or `miette` in the `lambda_runtime` dependency of your package. The error types provided by those crates can have blanket transformations into `Diagnostic` when the `lambda_runtime::IntoDiagnostic` trait is in scope. This trait exposes an `into_diagnostic` method that transforms those error types into a `Diagnostic`. This is an example that transforms an `anyhow::Error` into a `Diagnostic`: +If you enable the features `anyhow`, `eyre`, or `miette` in the `lambda_runtime` dependency of your package. The error types provided by those crates can have blanket transformations into `Diagnostic`. These features expose an `From for Diagnostic` implementation that transforms those error types into a `Diagnostic`. This is an example that transforms an `anyhow::Error` into a `Diagnostic`: ```rust -use lambda_runtime::{Diagnostic, IntoDiagnostic, LambdaEvent}; +use lambda_runtime::{Diagnostic, LambdaEvent}; async fn handler(_event: LambdaEvent) -> Result<(), Diagnostic> { - Err(anyhow::anyhow!("this is an error").into_diagnostic()) + Err(anyhow::anyhow!("this is an error").into()) } ``` diff --git a/examples/basic-error-error-crates-integration/src/main.rs b/examples/basic-error-error-crates-integration/src/main.rs index 203e65bf..f4048584 100644 --- a/examples/basic-error-error-crates-integration/src/main.rs +++ b/examples/basic-error-error-crates-integration/src/main.rs @@ -1,4 +1,4 @@ -use lambda_runtime::{run, service_fn, Diagnostic, IntoDiagnostic, Error, LambdaEvent}; +use lambda_runtime::{run, service_fn, Diagnostic, Error, LambdaEvent}; use serde::Deserialize; #[derive(Deserialize)] @@ -27,14 +27,14 @@ fn miette_error() -> miette::Result<()> { } /// Transform an anyhow::Error, eyre::Report, or miette::Report into a lambda_runtime::Diagnostic. -/// It does it by enabling the feature `anyhow`, `eyre` or `miette` in the runtime dependency, -/// and importing the `IntoDiagnostic` trait, which enables -/// the implementation of `into_diagnostic` for `anyhow::Error`, `eyre::Report`, and `miette::Report`. +/// It does it by enabling the feature `anyhow`, `eyre` or `miette` in the runtime dependency. +/// Those features enable the implementation of `From for Diagnostic` +/// for `anyhow::Error`, `eyre::Report`, and `miette::Report`. async fn function_handler(event: LambdaEvent) -> Result<(), Diagnostic> { match event.payload.error_type { - ErrorType::Anyhow => anyhow_error().map_err(|e| e.into_diagnostic()), - ErrorType::Eyre => eyre_error().map_err(|e| e.into_diagnostic()), - ErrorType::Miette => miette_error().map_err(|e| e.into_diagnostic()), + ErrorType::Anyhow => anyhow_error().map_err(|e| e.into()), + ErrorType::Eyre => eyre_error().map_err(|e| e.into()), + ErrorType::Miette => miette_error().map_err(|e| e.into()), } } diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index 22aaeb67..cd35535b 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -55,124 +55,21 @@ pub struct Diagnostic { pub error_message: String, } -/// Trait that adds an `into_diagnostic` method to several types, -/// including standard errors, and error types from several well known error crates. -/// -/// This trait is automatically implemented for some common types, -/// like boxed types that implement [`Error`][std::error::Error]. -/// If you use an error type which comes from a external crate like anyhow, eyre, or miette, -/// you can enable the features `anyhow`, `eyre` or `miette` in the `lambda_runtime` crate to automatically -/// implement `IntoDiagnostic`. -/// -/// Example: -/// ``` -/// use lambda_runtime::{Diagnostic, IntoDiagnostic, LambdaEvent}; -/// -/// #[derive(Debug)] -/// struct ErrorResponse(&'static str); -/// -/// impl IntoDiagnostic for ErrorResponse { -/// fn into_diagnostic(self) -> Diagnostic { -/// Diagnostic { -/// error_type: "MyError".into(), -/// error_message: self.0.into(), -/// } -/// } -/// } -/// -/// async fn function_handler(_event: LambdaEvent<()>) -> Result<(), Diagnostic> { -/// Err(ErrorResponse("this is an error response").into_diagnostic()) -/// } -/// ``` -pub trait IntoDiagnostic { - /// Converts external error types into [`Diagnostic`] - fn into_diagnostic(self) -> Diagnostic; -} - -impl IntoDiagnostic for DeserializeError { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - -impl IntoDiagnostic for Error { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - -impl IntoDiagnostic for Box { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - -impl IntoDiagnostic for std::convert::Infallible { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - -impl IntoDiagnostic for String { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self, - } - } -} - -impl IntoDiagnostic for &'static str { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.into(), - } - } -} - -impl IntoDiagnostic for std::io::Error { - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - -impl IntoDiagnostic for Box -where - T: std::error::Error, -{ - fn into_diagnostic(self) -> Diagnostic { - Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), - } - } -} - impl From for Diagnostic { fn from(value: DeserializeError) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From for Diagnostic { fn from(value: Error) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } @@ -181,66 +78,84 @@ where T: std::error::Error, { fn from(value: Box) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From> for Diagnostic { fn from(value: Box) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From for Diagnostic { fn from(value: std::convert::Infallible) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From for Diagnostic { fn from(value: String) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From<&'static str> for Diagnostic { fn from(value: &'static str) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } impl From for Diagnostic { fn from(value: std::io::Error) -> Self { - value.into_diagnostic() + Diagnostic { + error_type: type_name_of_val(&value), + error_message: value.to_string(), + } } } #[cfg(feature = "anyhow")] -impl IntoDiagnostic for anyhow::Error { - fn into_diagnostic(self) -> Diagnostic { +impl From for Diagnostic { + fn from(value: anyhow::Error) -> Diagnostic { Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), + error_type: type_name_of_val(&value), + error_message: value.to_string(), } } } #[cfg(feature = "eyre")] -impl IntoDiagnostic for eyre::Report { - fn into_diagnostic(self) -> Diagnostic { +impl From for Diagnostic { + fn from(value: eyre::Report) -> Diagnostic { Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), + error_type: type_name_of_val(&value), + error_message: value.to_string(), } } } #[cfg(feature = "miette")] -impl IntoDiagnostic for miette::Report { - fn into_diagnostic(self) -> Diagnostic { +impl From for Diagnostic { + fn from(value: miette::Report) -> Diagnostic { Diagnostic { - error_type: type_name_of_val(&self), - error_message: self.to_string(), + error_type: type_name_of_val(&value), + error_message: value.to_string(), } } } @@ -274,7 +189,7 @@ mod test { fn test_anyhow_integration() { use anyhow::Error as AnyhowError; let error: AnyhowError = anyhow::anyhow!("anyhow error"); - let diagnostic: Diagnostic = error.into_diagnostic(); + let diagnostic: Diagnostic = error.into(); assert_eq!(diagnostic.error_type, "&anyhow::Error"); assert_eq!(diagnostic.error_message, "anyhow error"); } @@ -284,7 +199,7 @@ mod test { fn test_eyre_integration() { use eyre::Report; let error: Report = eyre::eyre!("eyre error"); - let diagnostic: Diagnostic = error.into_diagnostic(); + let diagnostic: Diagnostic = error.into(); assert_eq!(diagnostic.error_type, "&eyre::Report"); assert_eq!(diagnostic.error_message, "eyre error"); } @@ -294,7 +209,7 @@ mod test { fn test_miette_integration() { use miette::Report; let error: Report = miette::miette!("miette error"); - let diagnostic: Diagnostic = error.into_diagnostic(); + let diagnostic: Diagnostic = error.into(); assert_eq!(diagnostic.error_type, "&miette::eyreish::Report"); assert_eq!(diagnostic.error_message, "miette error"); } diff --git a/lambda-runtime/src/lib.rs b/lambda-runtime/src/lib.rs index 9870455f..76d0562a 100644 --- a/lambda-runtime/src/lib.rs +++ b/lambda-runtime/src/lib.rs @@ -20,7 +20,7 @@ pub use tower::{self, service_fn, Service}; /// Diagnostic utilities to convert Rust types into Lambda Error types. pub mod diagnostic; -pub use diagnostic::{Diagnostic, IntoDiagnostic}; +pub use diagnostic::Diagnostic; mod deserializer; /// Tower middleware to be applied to runtime invocations. From 17886da7b338d27c39e1272be8eac9b33b81ade4 Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 12 Jul 2024 12:34:20 -0700 Subject: [PATCH 4/5] Remove implementation for From> Signed-off-by: David Calavera --- lambda-runtime/src/diagnostic.rs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index cd35535b..c03ce284 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -73,18 +73,6 @@ impl From for Diagnostic { } } -impl From> for Diagnostic -where - T: std::error::Error, -{ - fn from(value: Box) -> Self { - Diagnostic { - error_type: type_name_of_val(&value), - error_message: value.to_string(), - } - } -} - impl From> for Diagnostic { fn from(value: Box) -> Self { Diagnostic { From 6c13af2cdfa74fc8283b926e40f4fe242192fffb Mon Sep 17 00:00:00 2001 From: David Calavera Date: Fri, 12 Jul 2024 12:48:05 -0700 Subject: [PATCH 5/5] Document features in Cargo.toml Signed-off-by: David Calavera --- lambda-http/Cargo.toml | 9 +++++---- lambda-runtime/Cargo.toml | 10 +++++----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index ddd509fc..fc822b95 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -22,10 +22,11 @@ apigw_http = [] apigw_websockets = [] alb = [] pass_through = [] -tracing = ["lambda_runtime/tracing"] -anyhow = ["lambda_runtime/anyhow"] -eyre = ["lambda_runtime/eyre"] -miette = ["lambda_runtime/miette"] +tracing = ["lambda_runtime/tracing"] # enables access to the Tracing utilities +opentelemetry = ["lambda_runtime/opentelemetry"] # enables access to the OpenTelemetry layers and utilities +anyhow = ["lambda_runtime/anyhow"] # enables From for Diagnostic for anyhow error types, see README.md for more info +eyre = ["lambda_runtime/eyre"] # enables From for Diagnostic for eyre error types, see README.md for more info +miette = ["lambda_runtime/miette"] # enables From for Diagnostic for miette error types, see README.md for more info [dependencies] base64 = { workspace = true } diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index c9965f28..371b32f7 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -15,11 +15,11 @@ readme = "../README.md" [features] default = ["tracing"] -tracing = ["lambda_runtime_api_client/tracing"] -opentelemetry = ["opentelemetry-semantic-conventions"] -anyhow = ["dep:anyhow"] -eyre = ["dep:eyre"] -miette = ["dep:miette"] +tracing = ["lambda_runtime_api_client/tracing"] # enables access to the Tracing utilities +opentelemetry = ["opentelemetry-semantic-conventions"] # enables access to the OpenTelemetry layers and utilities +anyhow = ["dep:anyhow"] # enables From for Diagnostic for anyhow error types, see README.md for more info +eyre = ["dep:eyre"] # enables From for Diagnostic for eyre error types, see README.md for more info +miette = ["dep:miette"] # enables From for Diagnostic for miette error types, see README.md for more info [dependencies] anyhow = { version = "1.0.86", optional = true }