diff --git a/.github/actions/rust-build/action.yml b/.github/actions/rust-build/action.yml
new file mode 100644
index 00000000..85b5c0e8
--- /dev/null
+++ b/.github/actions/rust-build/action.yml
@@ -0,0 +1,26 @@
+name: "Rust builds"
+description: "Builds, tests, and formats Rust code"
+inputs:
+ package:
+ required: true
+ description: "the Rust package to test"
+ toolchain:
+ required: true
+ description: "the Rust toolchain to use"
+
+runs:
+ using: "composite"
+ steps:
+ - uses: dtolnay/rust-toolchain@master
+ with:
+ toolchain: ${{ inputs.toolchain }}
+ components: clippy, rustfmt
+ - uses: Swatinem/rust-cache@v2
+
+ - name: Build
+ shell: bash
+ run: cargo build --all-features --verbose --package ${{ inputs.package }}
+
+ - name: Run tests
+ shell: bash
+ run: cargo test --all-features --verbose --package ${{ inputs.package }}
diff --git a/.github/workflows/build-events.yml b/.github/workflows/build-events.yml
new file mode 100644
index 00000000..3a56e597
--- /dev/null
+++ b/.github/workflows/build-events.yml
@@ -0,0 +1,37 @@
+name: Check Lambda Events
+
+on:
+ push:
+ paths:
+ - 'lambda-events/**'
+ pull_request:
+ paths:
+ - 'lambda-events/**'
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ toolchain:
+ - "1.62.0" # Current MSRV
+ - stable
+ env:
+ RUST_BACKTRACE: 1
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Build events
+ uses: ./.github/actions/rust-build
+ with:
+ package: aws_lambda_events
+ toolchain: ${{ matrix.toolchain}}
+ check-event-features:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: Swatinem/rust-cache@v2
+
+ - name: Test individual event features
+ run: make check-event-features
diff --git a/.github/workflows/build-extension.yml b/.github/workflows/build-extension.yml
new file mode 100644
index 00000000..0905f289
--- /dev/null
+++ b/.github/workflows/build-extension.yml
@@ -0,0 +1,39 @@
+name: Check Lambda Runtime
+
+on:
+ push:
+ paths:
+ - 'lambda-runtime-api-client/**'
+ - 'lambda-extension/**'
+
+ pull_request:
+ paths:
+ - 'lambda-runtime-api-client/**'
+ - 'lambda-extension/**'
+
+
+jobs:
+ build-runtime:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ toolchain:
+ - "1.62.0" # Current MSRV
+ - stable
+ env:
+ RUST_BACKTRACE: 1
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Build Runtime API Client
+ uses: ./.github/actions/rust-build
+ with:
+ package: lambda_runtime_api_client
+ toolchain: ${{ matrix.toolchain}}
+
+
+ - name: Build Extensions runtime
+ uses: ./.github/actions/rust-build
+ with:
+ package: lambda-extension
+ toolchain: ${{ matrix.toolchain}}
diff --git a/.github/workflows/build-runtime.yml b/.github/workflows/build-runtime.yml
new file mode 100644
index 00000000..68913c95
--- /dev/null
+++ b/.github/workflows/build-runtime.yml
@@ -0,0 +1,45 @@
+name: Check Lambda Runtime
+
+on:
+ push:
+ paths:
+ - 'lambda-runtime-api-client/**'
+ - 'lambda-runtime/**'
+ - 'lambda-http/**'
+
+ pull_request:
+ paths:
+ - 'lambda-runtime-api-client/**'
+ - 'lambda-runtime/**'
+ - 'lambda-http/**'
+
+jobs:
+ build-runtime:
+ runs-on: ubuntu-latest
+ strategy:
+ matrix:
+ toolchain:
+ - "1.62.0" # Current MSRV
+ - stable
+ env:
+ RUST_BACKTRACE: 1
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Build Runtime API Client
+ uses: ./.github/actions/rust-build
+ with:
+ package: lambda_runtime_api_client
+ toolchain: ${{ matrix.toolchain}}
+
+ - name: Build Functions runtime
+ uses: ./.github/actions/rust-build
+ with:
+ package: lambda_runtime
+ toolchain: ${{ matrix.toolchain}}
+
+ - name: Build HTTP layer
+ uses: ./.github/actions/rust-build
+ with:
+ package: lambda_http
+ toolchain: ${{ matrix.toolchain}}
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
deleted file mode 100644
index 370d8249..00000000
--- a/.github/workflows/build.yml
+++ /dev/null
@@ -1,93 +0,0 @@
-name: Rust
-
-on: [push, pull_request, workflow_dispatch]
-
-jobs:
- build:
- runs-on: ${{ matrix.os }}
- strategy:
- fail-fast: false
- matrix:
- os:
- - ubuntu-latest
- - macOS-latest
- toolchain:
- - "1.62.0" # Current MSRV
- - stable
- - beta
- - nightly
- target:
- - ""
- - x86_64-unknown-linux-musl
- include:
- - rust: nightly
- allow_failure: true
- env:
- RUST_BACKTRACE: 1
- steps:
- - uses: actions/checkout@v3
- - uses: dtolnay/rust-toolchain@master
- with:
- toolchain: ${{ matrix.toolchain }}
- - uses: Swatinem/rust-cache@v2
-
- - name: Build
- run: cargo build --all --verbose
- env:
- TARGET: ${{ matrix.target }}
- continue-on-error: ${{ matrix.allow_failure }}
- - name: Run tests
- run: cargo test --all --verbose
- env:
- TARGET: ${{ matrix.target }}
- continue-on-error: ${{ matrix.allow_failure }}
- formatting:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v2
- - uses: dtolnay/rust-toolchain@master
- with:
- toolchain: stable
- - uses: Swatinem/rust-cache@v2
-
- - name: Run fmt check
- run: cargo fmt --all -- --check
- - name: Run clippy check
- run: cargo clippy --all-features -- -D warnings
-
- check-examples:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@v3
- - uses: dtolnay/rust-toolchain@master
- with:
- toolchain: stable
- - uses: Swatinem/rust-cache@v2
-
- - name: Check examples
- working-directory: examples
- shell: bash
- run: ./check-examples.sh
-
- # publish rustdoc to a gh-pages branch on pushes to main
- # this can be helpful to those depending on the mainline branch
- publish-docs:
- if: github.ref == 'refs/heads/main'
- runs-on: ubuntu-latest
- needs: [build]
- steps:
- - uses: actions/checkout@v3
- - uses: dtolnay/rust-toolchain@master
- with:
- toolchain: stable
- - uses: Swatinem/rust-cache@v2
-
- - name: Generate Docs
- run: |
- cargo doc --no-deps
- echo "" > target/doc/index.html
- - name: Publish
- uses: peaceiris/actions-gh-pages@v3
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./target/doc
diff --git a/.github/workflows/check-examples.yml b/.github/workflows/check-examples.yml
new file mode 100644
index 00000000..ba7bc709
--- /dev/null
+++ b/.github/workflows/check-examples.yml
@@ -0,0 +1,16 @@
+name: Check examples
+
+on: [push, pull_request]
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: Swatinem/rust-cache@v2
+
+ - name: Check examples
+ working-directory: examples
+ shell: bash
+ run: ./check-examples.sh
\ No newline at end of file
diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml
new file mode 100644
index 00000000..f18d1f83
--- /dev/null
+++ b/.github/workflows/format.yml
@@ -0,0 +1,55 @@
+name: Formatting and Linting
+
+on: [push, pull_request]
+
+jobs:
+ check:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+ - uses: dtolnay/rust-toolchain@stable
+ - uses: Swatinem/rust-cache@v2
+
+ - name: Run fmt check
+ id: cargoFmt
+ shell: bash
+ run: cargo fmt --all -- --check
+ - name: Notify fmt check
+ if: failure() && steps.cargoFmt.outcome == 'failure'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const message = `👋 It looks like your code is not formatted like we expect.
+
+ Please run \`cargo fmt\` and push the code again.`;
+
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: message,
+ });
+ core.setFailed('It looks like there are formatting errors');
+
+ - name: Run clippy check
+ id: cargoClippy
+ shell: bash
+ run: cargo clippy --workspace --all-features -- -D warnings
+ - name: Notify fmt check
+ if: failure() && steps.cargoClippy.outcome == 'failure'
+ uses: actions/github-script@v6
+ with:
+ script: |
+ const message = `👋 It looks like your code has some linting issues.
+
+ Please run \`cargo clippy --fix\` and push the code again.`;
+
+ await github.rest.issues.createComment({
+ issue_number: context.issue.number,
+ owner: context.repo.owner,
+ repo: context.repo.repo,
+ body: message,
+ });
+ core.setFailed('It looks like there are linting errors');
+
+
\ No newline at end of file
diff --git a/Cargo.toml b/Cargo.toml
index 7361557e..48bcd5db 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -4,7 +4,8 @@ members = [
"lambda-integration-tests",
"lambda-runtime-api-client",
"lambda-runtime",
- "lambda-extension"
+ "lambda-extension",
+ "lambda-events"
]
exclude = ["examples"]
diff --git a/Makefile b/Makefile
index cb00545c..544d08b7 100644
--- a/Makefile
+++ b/Makefile
@@ -60,3 +60,47 @@ invoke-integration-api-%:
curl -X POST -d '{"command": "hello"}' $(API_URL)/trait/post
curl -X POST -d '{"command": "hello"}' $(API_URL)/al2/post
curl -X POST -d '{"command": "hello"}' $(API_URL)/al2-trait/post
+
+# Test individual event features to ensure optional dependencies
+# are correctly loaded when all default features are disabled.
+check-event-features:
+ cargo test --package aws_lambda_events --no-default-features --features activemq
+ cargo test --package aws_lambda_events --no-default-features --features alb
+ cargo test --package aws_lambda_events --no-default-features --features apigw
+ cargo test --package aws_lambda_events --no-default-features --features appsync
+ cargo test --package aws_lambda_events --no-default-features --features autoscaling
+ cargo test --package aws_lambda_events --no-default-features --features chime_bot
+ cargo test --package aws_lambda_events --no-default-features --features clientvpn
+ cargo test --package aws_lambda_events --no-default-features --features cloudwatch_events
+ cargo test --package aws_lambda_events --no-default-features --features cloudwatch_logs
+ cargo test --package aws_lambda_events --no-default-features --features code_commit
+ cargo test --package aws_lambda_events --no-default-features --features codebuild
+ cargo test --package aws_lambda_events --no-default-features --features codedeploy
+ cargo test --package aws_lambda_events --no-default-features --features codepipeline_cloudwatch
+ cargo test --package aws_lambda_events --no-default-features --features codepipeline_job
+ cargo test --package aws_lambda_events --no-default-features --features cognito
+ cargo test --package aws_lambda_events --no-default-features --features config
+ cargo test --package aws_lambda_events --no-default-features --features connect
+ cargo test --package aws_lambda_events --no-default-features --features dynamodb
+ cargo test --package aws_lambda_events --no-default-features --features ecr_scan
+ cargo test --package aws_lambda_events --no-default-features --features firehose
+ cargo test --package aws_lambda_events --no-default-features --features iam
+ cargo test --package aws_lambda_events --no-default-features --features iot
+ cargo test --package aws_lambda_events --no-default-features --features iot_1_click
+ cargo test --package aws_lambda_events --no-default-features --features iot_button
+ cargo test --package aws_lambda_events --no-default-features --features iot_deprecated
+ cargo test --package aws_lambda_events --no-default-features --features kafka
+ cargo test --package aws_lambda_events --no-default-features --features kinesis
+ cargo test --package aws_lambda_events --no-default-features --features kinesis_analytics
+ cargo test --package aws_lambda_events --no-default-features --features lambda_function_urls
+ cargo test --package aws_lambda_events --no-default-features --features lex
+ cargo test --package aws_lambda_events --no-default-features --features rabbitmq
+ cargo test --package aws_lambda_events --no-default-features --features s3
+ cargo test --package aws_lambda_events --no-default-features --features s3_batch_job
+ cargo test --package aws_lambda_events --no-default-features --features ses
+ cargo test --package aws_lambda_events --no-default-features --features sns
+ cargo test --package aws_lambda_events --no-default-features --features sqs
+ cargo test --package aws_lambda_events --no-default-features --features streams
+
+fmt:
+ cargo +nightly fmt --all
\ No newline at end of file
diff --git a/README.md b/README.md
index 781ef364..12dbf523 100644
--- a/README.md
+++ b/README.md
@@ -7,8 +7,11 @@ This package makes it easy to run AWS Lambda Functions written in Rust. This wor
- [](https://docs.rs/lambda_runtime) **`lambda-runtime`** is a library that provides a Lambda runtime for applications written in Rust.
- [](https://docs.rs/lambda_http) **`lambda-http`** is a library that makes it easy to write API Gateway proxy event focused Lambda functions in Rust.
- [](https://docs.rs/lambda-extension) **`lambda-extension`** is a library that makes it easy to write Lambda Runtime Extensions in Rust.
+- [](https://docs.rs/aws_lambda_events) **`lambda-events`** is a library with strongly-typed Lambda event structs in Rust.
- [](https://docs.rs/lambda_runtime_api_client) **`lambda-runtime-api-client`** is a shared library between the lambda runtime and lambda extension libraries that includes a common API client to talk with the AWS Lambda Runtime API.
+The Rust runtime client is an experimental package. It is subject to change and intended only for evaluation purposes.
+
## Getting started
The easiest way to start writing Lambda functions with Rust is by using [Cargo Lambda](https://www.cargo-lambda.info/), a related project. Cargo Lambda is a Cargo plugin, or subcommand, that provides several commands to help you in your journey with Rust on AWS Lambda.
@@ -355,7 +358,7 @@ An simpler alternative is to cURL the following endpoint based on the address an
```bash
curl -v -X POST \
- 'http://127.0.0.1:9001/lambda-url/' \
+ 'http://127.0.0.1:9001/lambda-url//' \
-H 'content-type: application/json' \
-d '{ "command": "hi" }'
```
diff --git a/examples/advanced-sqs-partial-batch-failures/Cargo.toml b/examples/advanced-sqs-partial-batch-failures/Cargo.toml
index 06e56053..0dfe49d9 100644
--- a/examples/advanced-sqs-partial-batch-failures/Cargo.toml
+++ b/examples/advanced-sqs-partial-batch-failures/Cargo.toml
@@ -8,8 +8,8 @@ serde = "^1"
serde_derive = "^1"
serde_with = { version = "^2", features = ["json"], optional = true }
serde_json = "^1"
-aws_lambda_events = "0.7.3"
-lambda_runtime = "0.7"
+aws_lambda_events = { path = "../../lambda-events" }
+lambda_runtime = { path = "../../lambda-runtime" }
tokio = { version = "1", features = ["macros"] }
futures = "0.3"
tracing = { version = "0.1", features = ["log"] }
diff --git a/examples/basic-lambda-external-runtime/Cargo.toml b/examples/basic-lambda-external-runtime/Cargo.toml
new file mode 100644
index 00000000..9c732b2f
--- /dev/null
+++ b/examples/basic-lambda-external-runtime/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "basic-lambda-external-runtime"
+version = "0.1.0"
+edition = "2021"
+
+[dependencies]
+async-channel = "1.8.0"
+futures-lite = "1.13.0"
+lambda_runtime = "0.8.0"
+lambda_runtime_api_client = "0.8.0"
+serde = "1.0.163"
+tokio = "1.28.2"
+tokio-test = "0.4.2"
+tracing = "0.1.37"
+tracing-subscriber = "0.3.17"
diff --git a/examples/basic-lambda-external-runtime/README.md b/examples/basic-lambda-external-runtime/README.md
new file mode 100644
index 00000000..498f8a50
--- /dev/null
+++ b/examples/basic-lambda-external-runtime/README.md
@@ -0,0 +1,11 @@
+# AWS Lambda Function example
+
+## Build & Deploy
+
+1. Install [cargo-lambda](https://github.com/cargo-lambda/cargo-lambda#installation)
+2. Build the function with `cargo lambda build --release`
+3. Deploy the function to AWS Lambda with `cargo lambda deploy --iam-role YOUR_ROLE`
+
+## Build for ARM 64
+
+Build the function with `cargo lambda build --release --arm64`
diff --git a/examples/basic-lambda-external-runtime/src/main.rs b/examples/basic-lambda-external-runtime/src/main.rs
new file mode 100644
index 00000000..71bd123b
--- /dev/null
+++ b/examples/basic-lambda-external-runtime/src/main.rs
@@ -0,0 +1,104 @@
+use std::{io, thread};
+
+use futures_lite::future;
+use lambda_runtime::{service_fn, Error, LambdaEvent};
+use serde::{Deserialize, Serialize};
+use tokio::runtime::Builder;
+
+/// This is also a made-up example. Requests come into the runtime as unicode
+/// strings in json format, which can map to any structure that implements `serde::Deserialize`
+/// The runtime pays no attention to the contents of the request payload.
+#[derive(Deserialize)]
+struct Request {
+ command: String,
+}
+
+/// This is a made-up example of what a response structure may look like.
+/// There is no restriction on what it can be. The runtime requires responses
+/// to be serialized into json. The runtime pays no attention
+/// to the contents of the response payload.
+#[derive(Serialize)]
+struct Response {
+ req_id: String,
+ msg: String,
+}
+
+fn main() -> Result<(), io::Error> {
+ // required to enable CloudWatch error logging by the runtime
+ tracing_subscriber::fmt()
+ .with_max_level(tracing::Level::INFO)
+ // disable printing the name of the module in every log line.
+ .with_target(false)
+ // this needs to be set to false, otherwise ANSI color codes will
+ // show up in a confusing manner in CloudWatch logs.
+ .with_ansi(false)
+ // disabling time is handy because CloudWatch will add the ingestion time.
+ .without_time()
+ .init();
+
+ // Create a channel used to send and receive outputs from our lambda handler. Realistically, this would be either an unbounded channel
+ // or a bounded channel with a higher capacity as needed.
+ let (lambda_tx, lambda_rx) = async_channel::bounded(1);
+
+ // Create a bounded channel used to communicate our shutdown signal across threads.
+ let (shutdown_tx, shutdown_rx) = async_channel::bounded(1);
+
+ // Build a single-threaded (or multi-threaded using Builder::new_multi_thread) runtime to spawn our lambda work onto.
+ let tokio_runtime = Builder::new_current_thread()
+ .thread_name("lambda-runtime")
+ .enable_all()
+ .build()
+ .expect("build lambda runtime");
+
+ // Run the lambda runtime worker thread to completion. The response is sent to the other "runtime" to be processed as needed.
+ thread::spawn(move || {
+ let func = service_fn(my_handler);
+ if let Ok(response) = tokio_runtime.block_on(lambda_runtime::run(func)) {
+ lambda_tx.send_blocking(response).expect("send lambda result");
+ };
+ });
+
+ // Run the mock runtime to completion.
+ my_runtime(move || future::block_on(app_runtime_task(lambda_rx.clone(), shutdown_tx.clone())));
+
+ // Block the main thread until a shutdown signal is received.
+ future::block_on(shutdown_rx.recv()).map_err(|err| io::Error::new(io::ErrorKind::Other, format!("{:?}", err)))
+}
+
+pub(crate) async fn my_handler(event: LambdaEvent) -> Result {
+ // extract some useful info from the request
+ let command = event.payload.command;
+
+ // prepare the response
+ let resp = Response {
+ req_id: event.context.request_id,
+ msg: format!("Command {} executed.", command),
+ };
+
+ // return `Response` (it will be serialized to JSON automatically by the runtime)
+ Ok(resp)
+}
+
+/// A task to be ran on the custom runtime. Once a response from the lambda runtime is received then a shutdown signal
+/// is sent to the main thread notifying the process to exit.
+pub(crate) async fn app_runtime_task(lambda_rx: async_channel::Receiver<()>, shutdown_tx: async_channel::Sender<()>) {
+ loop {
+ // Receive the response sent by the lambda handle and process as needed.
+ if let Ok(result) = lambda_rx.recv().await {
+ tracing::debug!(?result);
+ // We're ready to shutdown our app. Send the shutdown signal notifying the main thread to exit the process.
+ shutdown_tx.send(()).await.expect("send shutdown signal");
+ break;
+ }
+
+ // more app logic would be here...
+ }
+}
+
+/// Construct the mock runtime worker thread(s) to spawn some work onto.
+fn my_runtime(func: impl Fn() + Send + 'static) {
+ thread::Builder::new()
+ .name("my-runtime".into())
+ .spawn(func)
+ .expect("spawn my_runtime worker");
+}
diff --git a/examples/basic-s3-thumbnail/Cargo.toml b/examples/basic-s3-thumbnail/Cargo.toml
index dfa6d69b..0ef0d463 100644
--- a/examples/basic-s3-thumbnail/Cargo.toml
+++ b/examples/basic-s3-thumbnail/Cargo.toml
@@ -15,7 +15,7 @@ edition = "2021"
# and it will keep the alphabetic ordering for you.
[dependencies]
-aws_lambda_events = "0.7.2"
+aws_lambda_events = { path = "../../lambda-events" }
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1"
tokio = { version = "1", features = ["macros"] }
diff --git a/examples/basic-sqs/Cargo.toml b/examples/basic-sqs/Cargo.toml
index 086a22dc..a1b11567 100644
--- a/examples/basic-sqs/Cargo.toml
+++ b/examples/basic-sqs/Cargo.toml
@@ -15,7 +15,7 @@ edition = "2021"
# and it will keep the alphabetic ordering for you.
[dependencies]
-aws_lambda_events = "0.7.2"
+aws_lambda_events = { path = "../../lambda-events" }
lambda_runtime = { path = "../../lambda-runtime" }
serde = "1.0.136"
tokio = { version = "1", features = ["macros"] }
diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml
new file mode 100644
index 00000000..b1108c63
--- /dev/null
+++ b/lambda-events/Cargo.toml
@@ -0,0 +1,114 @@
+[package]
+name = "aws_lambda_events"
+version = "0.10.0"
+description = "AWS Lambda event definitions"
+authors = [
+ "Christian Legnitto ",
+ "Sam Rijs ",
+ "David Calavera ",
+]
+license = "MIT"
+homepage = "https://github.com/awslabs/aws-lambda-rust-runtime"
+repository = "https://github.com/awslabs/aws-lambda-rust-runtime"
+readme = "README.md"
+keywords = ["lambda", "aws", "amazon", "events", "S3"]
+categories = ["api-bindings", "encoding", "web-programming"]
+edition = "2021"
+
+[dependencies]
+base64 = "0.21"
+http = { version = "0.2", optional = true }
+http-body = { version = "0.4", optional = true }
+http-serde = { version = "^1", optional = true }
+serde = { version = "^1", features = ["derive"] }
+serde_with = { version = "^3", features = ["json"], optional = true }
+serde_json = "^1"
+serde_dynamo = { version = "^4.1", optional = true }
+bytes = { version = "1", features = ["serde"], optional = true }
+chrono = { version = "0.4.23", default-features = false, features = [
+ "clock",
+ "serde",
+ "std",
+], optional = true }
+query_map = { version = "^0.6", features = ["serde", "url-query"], optional = true }
+flate2 = { version = "1.0.24", optional = true }
+
+[features]
+default = [
+ "activemq",
+ "alb",
+ "apigw",
+ "appsync",
+ "autoscaling",
+ "chime_bot",
+ "clientvpn",
+ "cloudwatch_events",
+ "cloudwatch_logs",
+ "code_commit",
+ "codebuild",
+ "codedeploy",
+ "codepipeline_cloudwatch",
+ "codepipeline_job",
+ "cognito",
+ "config",
+ "connect",
+ "dynamodb",
+ "ecr_scan",
+ "firehose",
+ "iam",
+ "iot",
+ "iot_1_click",
+ "iot_button",
+ "iot_deprecated",
+ "kafka",
+ "kinesis",
+ "kinesis_analytics",
+ "lambda_function_urls",
+ "lex",
+ "rabbitmq",
+ "s3",
+ "s3_batch_job",
+ "ses",
+ "sns",
+ "sqs",
+ "streams",
+]
+
+activemq = []
+alb = ["bytes", "http", "http-body", "http-serde", "query_map"]
+apigw = ["bytes", "http", "http-body", "http-serde", "query_map"]
+appsync = []
+autoscaling = ["chrono"]
+chime_bot = ["chrono"]
+clientvpn = []
+cloudwatch_events = ["chrono"]
+cloudwatch_logs = ["flate2"]
+code_commit = ["chrono"]
+codebuild = ["chrono"]
+codedeploy = ["chrono"]
+codepipeline = []
+codepipeline_cloudwatch = ["chrono"]
+codepipeline_job = []
+cognito = []
+config = []
+connect = []
+dynamodb = ["chrono", "serde_dynamo", "streams"]
+ecr_scan = []
+firehose = ["chrono"]
+iam = []
+iot = ["bytes", "http", "http-body", "http-serde", "iam"]
+iot_1_click = []
+iot_button = []
+iot_deprecated = ["iot"]
+kafka = ["chrono"]
+kinesis = ["chrono"]
+kinesis_analytics = ["kinesis"]
+lambda_function_urls = ["bytes", "http", "http-body", "http-serde"]
+lex = []
+rabbitmq = []
+s3 = ["bytes", "chrono", "http", "http-body", "http-serde"]
+s3_batch_job = ["s3"]
+ses = ["chrono"]
+sns = ["chrono", "serde_with"]
+sqs = ["serde_with"]
+streams = []
diff --git a/lambda-events/LICENSE b/lambda-events/LICENSE
new file mode 100644
index 00000000..2329b573
--- /dev/null
+++ b/lambda-events/LICENSE
@@ -0,0 +1,23 @@
+MIT License
+
+Copyright (c) 2018 Sam Rijs and Christian Legnitto
+Copyright 2023 Amazon.com, Inc. or its affiliates
+
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/lambda-events/README.md b/lambda-events/README.md
new file mode 100644
index 00000000..0813c63a
--- /dev/null
+++ b/lambda-events/README.md
@@ -0,0 +1,34 @@
+# AWS Lambda Events
+
+[![crates.io][crate-image]][crate-link]
+[![Documentation][docs-image]][docs-link]
+
+This crate provides strongly-typed [AWS Lambda event structs](https://docs.aws.amazon.com/lambda/latest/dg/invoking-lambda-function.html) in Rust.
+
+## Installation
+
+Add the dependency with Cargo: `cargo add aws_lambda_events`.
+
+## Usage
+
+The crate itself has no AWS Lambda handler logic and instead exists to serialize
+and deserialize AWS Lambda events into strongly-typed Rust structs.
+
+The types
+defined in this crate are usually used with handlers / runtimes provided by the [official Rust runtime](https://github.com/awslabs/aws-lambda-rust-runtime).
+
+For a list of supported AWS Lambda events and services, see [the crate reference documentation](https://docs.rs/aws_lambda_events).
+
+## Conditional compilation of features
+
+This crate divides all Lambda Events into features named after the service that the events are generated from. By default all events are enabled when you include this crate as a dependency to your project. If you only want to import specific events from this crate, you can disable the default features, and enable only the events that you need. This will make your project to compile a little bit faster, since rustc doesn't need to compile events that you're not going to use. Here's an example on how to do that:
+
+```
+cargo add aws_lambda_events --no-default-features --features apigw,alb
+```
+
+[//]: # 'badges'
+[crate-image]: https://img.shields.io/crates/v/aws_lambda_events.svg
+[crate-link]: https://crates.io/crates/aws_lambda_events
+[docs-image]: https://docs.rs/aws_lambda_events/badge.svg
+[docs-link]: https://docs.rs/aws_lambda_events
\ No newline at end of file
diff --git a/lambda-events/src/custom_serde/codebuild_time.rs b/lambda-events/src/custom_serde/codebuild_time.rs
new file mode 100644
index 00000000..94d0e2f5
--- /dev/null
+++ b/lambda-events/src/custom_serde/codebuild_time.rs
@@ -0,0 +1,108 @@
+use chrono::{DateTime, TimeZone, Utc};
+use serde::ser::Serializer;
+use serde::{
+ de::{Deserializer, Error as DeError, Visitor},
+ Deserialize,
+};
+use std::fmt;
+
+// Jan 2, 2006 3:04:05 PM
+const CODEBUILD_TIME_FORMAT: &str = "%b %e, %Y %l:%M:%S %p";
+
+struct TimeVisitor;
+impl<'de> Visitor<'de> for TimeVisitor {
+ type Value = DateTime;
+
+ fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(formatter, "valid codebuild time: {}", CODEBUILD_TIME_FORMAT)
+ }
+
+ fn visit_str(self, val: &str) -> Result {
+ Utc.datetime_from_str(val, CODEBUILD_TIME_FORMAT)
+ .map_err(|e| DeError::custom(format!("Parse error {} for {}", e, val)))
+ }
+}
+
+pub(crate) mod str_time {
+ use super::*;
+
+ pub(crate) fn deserialize<'de, D>(d: D) -> Result, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ d.deserialize_str(TimeVisitor)
+ }
+
+ pub fn serialize(date: &DateTime, ser: S) -> Result {
+ let s = format!("{}", date.format(CODEBUILD_TIME_FORMAT));
+ ser.serialize_str(&s)
+ }
+}
+
+pub(crate) mod optional_time {
+ use super::*;
+
+ pub(crate) fn deserialize<'de, D>(deserializer: D) -> Result