diff --git a/Cargo.toml b/Cargo.toml index 867e9c0d..8c3294cc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [workspace] resolver = "2" members = [ + "json-impl", "lambda-http", "lambda-integration-tests", "lambda-runtime-api-client", diff --git a/README.md b/README.md index 31ba399b..66f29c26 100644 --- a/README.md +++ b/README.md @@ -54,7 +54,7 @@ If you'd like to manually create your first function, the code below shows you a ```rust,no_run use lambda_runtime::{service_fn, LambdaEvent, Error}; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; #[tokio::main] async fn main() -> Result<(), Error> { @@ -375,7 +375,7 @@ To serialize and deserialize events and responses, we suggest using the [`serde` ```rust,no_run use serde::{Serialize, Deserialize}; -use serde_json::json; +use aws_lambda_json_impl::json; use std::error::Error; #[derive(Serialize, Deserialize)] diff --git a/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs b/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs index 2a70dce3..e7f9414f 100644 --- a/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs +++ b/examples/advanced-sqs-multiple-functions-shared-data/producer/src/main.rs @@ -1,6 +1,6 @@ use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; use pizza_lib::Pizza; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; struct SQSManager { client: aws_sdk_sqs::Client, diff --git a/examples/basic-error-handling/src/main.rs b/examples/basic-error-handling/src/main.rs index 3bc76936..6886ef15 100644 --- a/examples/basic-error-handling/src/main.rs +++ b/examples/basic-error-handling/src/main.rs @@ -1,7 +1,7 @@ /// See https://github.com/awslabs/aws-lambda-rust-runtime for more info on Rust runtime for AWS Lambda use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; use serde::{Deserialize, Serialize}; -use serde_json::json; +use aws_lambda_json_impl::json; use std::fs::File; /// A simple Lambda request structure with just one field diff --git a/examples/basic-streaming-response/src/main.rs b/examples/basic-streaming-response/src/main.rs index 8533c8e3..938b6ba6 100644 --- a/examples/basic-streaming-response/src/main.rs +++ b/examples/basic-streaming-response/src/main.rs @@ -3,7 +3,7 @@ use lambda_runtime::{ streaming::{channel, Body, Response}, tracing, Error, LambdaEvent, }; -use serde_json::Value; +use aws_lambda_json_impl::Value; use std::{thread, time::Duration}; async fn func(_event: LambdaEvent<Value>) -> Result<Response<Body>, Error> { diff --git a/examples/http-axum-apigw-authorizer/src/main.rs b/examples/http-axum-apigw-authorizer/src/main.rs index 513a6cd8..7a254e88 100644 --- a/examples/http-axum-apigw-authorizer/src/main.rs +++ b/examples/http-axum-apigw-authorizer/src/main.rs @@ -7,7 +7,7 @@ use axum::{ Router, }; use lambda_http::{run, tracing, Error, RequestExt}; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; use std::{collections::HashMap, env::set_var}; struct AuthorizerField(String); diff --git a/examples/http-axum-middleware/src/main.rs b/examples/http-axum-middleware/src/main.rs index b1e92811..b9f8672d 100644 --- a/examples/http-axum-middleware/src/main.rs +++ b/examples/http-axum-middleware/src/main.rs @@ -12,7 +12,7 @@ use axum::{response::Json, routing::post, Router}; use lambda_http::request::RequestContext::ApiGatewayV1; use lambda_http::{run, tracing, Error}; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; // Sample middleware that logs the request id async fn mw_sample(req: axum::extract::Request, next: axum::middleware::Next) -> impl axum::response::IntoResponse { diff --git a/examples/http-axum/src/main.rs b/examples/http-axum/src/main.rs index dcd5d154..df8f7a70 100644 --- a/examples/http-axum/src/main.rs +++ b/examples/http-axum/src/main.rs @@ -16,7 +16,7 @@ use axum::{ }; use lambda_http::{run, tracing, Error}; use serde::{Deserialize, Serialize}; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; use std::env::set_var; #[derive(Deserialize, Serialize)] diff --git a/examples/lambda-rds-iam-auth/src/main.rs b/examples/lambda-rds-iam-auth/src/main.rs index 32cf3580..4e523591 100644 --- a/examples/lambda-rds-iam-auth/src/main.rs +++ b/examples/lambda-rds-iam-auth/src/main.rs @@ -5,7 +5,7 @@ use aws_sigv4::{ sign::v4, }; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; -use serde_json::{json, Value}; +use aws_lambda_json_impl::{json, Value}; use sqlx::postgres::PgConnectOptions; use std::env; use std::time::{Duration, SystemTime}; diff --git a/json-impl/Cargo.toml b/json-impl/Cargo.toml new file mode 100644 index 00000000..56ee590d --- /dev/null +++ b/json-impl/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "aws_lambda_json_impl" +version = "0.15.1" +description = "AWS Lambda JSON adapter switch between serde_json and simd_json" +authors = [ + "Martin Bartmett <martin.j.bartmett@gmail.com>", +] +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", "json"] +categories = ["api-bindings", "encoding", "web-programming"] +edition = "2021" + +[features] +default = [ ] +simd = [ "simd-json/ordered-float", "value-trait/ordered-float" ] + +[dependencies] +serde_json = { version = "^1", features = ["raw_value"] } +#simd-json = { version = "^0", optional = true } +simd-json = { git = "https://github.com/simd-lite/simd-json.git", branch = "main", optional = true } +value-trait = { version = "^0", optional = true } +bytes = { workspace = true } +serde = { version = "^1", no-default-features = true } diff --git a/json-impl/src/lib.rs b/json-impl/src/lib.rs new file mode 100644 index 00000000..bc3e09c2 --- /dev/null +++ b/json-impl/src/lib.rs @@ -0,0 +1,93 @@ +// Using serde_json as the JSON handler +#[cfg(not(feature = "simd"))] +pub use serde::*; +// Using simd_json as the JSON handler +#[cfg(feature = "simd")] +pub use simd::*; + +// Implementations + +#[cfg(not(feature = "simd"))] +mod serde { + use bytes::Bytes; + use serde::de::DeserializeOwned; + pub use serde_json::{ + self, error::Error as JsonError, from_reader, from_slice, from_str, from_value, json, to_string, + to_string_pretty, to_value, to_writer, value::RawValue, Deserializer as JsonDeserializer, Value, + to_vec, + }; + pub fn from_bytes<T>(b: Bytes) -> serde_json::Result<T> + where + T: DeserializeOwned, + { + from_slice(&b) + } + + pub fn from_string<T>(s: String) -> serde_json::Result<T> + where + T: DeserializeOwned, + { + from_str(s.as_str()) + } + + pub fn from_vec<T>(v: Vec<u8>) -> serde_json::Result<T> + where + T: DeserializeOwned, + { + from_slice(&v) + } +} + +#[cfg(feature = "simd")] +mod simd { + use bytes::Bytes; + use serde::de::DeserializeOwned; + pub use simd_json::{ + self, + json, + owned::Value, + serde::{ + from_owned_value as from_value, + from_reader, + from_str, //THIS requires a mutable string slice AND is unsafe + from_slice, //THIS requires a mutable slice! + to_owned_value as to_value, + to_string, + to_string_pretty, + to_writer, + to_vec, + }, + tape::Value as RawValue, //THIS is gonna be the fun one! + Deserializer as JsonDeserializer, + Error as JsonError, + }; + pub use value_trait::prelude::*; + + pub fn from_bytes<T>(b: Bytes) -> simd_json::Result<T> + where + T: DeserializeOwned, + { + match b.try_into_mut() { + Ok(mut b) => from_slice(&mut b), + Err(b) => { + let mut v = b.to_vec(); + from_slice(&mut v) + } + } + } + + pub fn from_string<T>(mut s: String) -> simd_json::Result<T> + where + T: DeserializeOwned, + { + unsafe{ from_str(s.as_mut_str()) } + } + + pub fn from_vec<T>(mut v: Vec<u8>) -> simd_json::Result<T> + where + T: DeserializeOwned, + { + from_slice(&mut v) + } +} + diff --git a/lambda-events/Cargo.toml b/lambda-events/Cargo.toml index d9774104..c387f9ce 100644 --- a/lambda-events/Cargo.toml +++ b/lambda-events/Cargo.toml @@ -29,11 +29,14 @@ query_map = { version = "^0.7", features = [ ], optional = true } serde = { version = "^1", features = ["derive"] } serde_with = { version = "^3", features = ["json"], optional = true } -serde_json = "^1" +aws_lambda_json_impl = { path = "../json-impl" } serde_dynamo = { version = "^4.1", optional = true } [features] default = [ + +# "simd_json", + "activemq", "alb", "apigw", @@ -123,3 +126,4 @@ sqs = ["serde_with"] streams = [] documentdb = [] eventbridge = ["chrono", "serde_with"] +simd_json = [ "aws_lambda_json_impl/simd" ] diff --git a/lambda-events/src/custom_serde/codebuild_time.rs b/lambda-events/src/custom_serde/codebuild_time.rs index 07bd0a5c..81105017 100644 --- a/lambda-events/src/custom_serde/codebuild_time.rs +++ b/lambda-events/src/custom_serde/codebuild_time.rs @@ -78,14 +78,14 @@ mod tests { #[serde(with = "str_time")] pub date: TestTime, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "date": "Sep 1, 2017 4:12:29 PM" }); let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT) .unwrap() .and_utc(); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.date); } @@ -96,14 +96,14 @@ mod tests { #[serde(with = "optional_time")] pub date: Option<TestTime>, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "date": "Sep 1, 2017 4:12:29 PM" }); let expected = NaiveDateTime::parse_from_str("Sep 1, 2017 4:12:29 PM", CODEBUILD_TIME_FORMAT) .unwrap() .and_utc(); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(Some(expected), decoded.date); } } diff --git a/lambda-events/src/custom_serde/headers.rs b/lambda-events/src/custom_serde/headers.rs index 44884649..5cbbcbf5 100644 --- a/lambda-events/src/custom_serde/headers.rs +++ b/lambda-events/src/custom_serde/headers.rs @@ -145,13 +145,13 @@ mod tests { #[serde(deserialize_with = "deserialize_headers", default)] pub headers: HeaderMap, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "not_headers": {} }); let expected = HeaderMap::new(); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.headers); } @@ -163,16 +163,16 @@ mod tests { #[serde(serialize_with = "serialize_multi_value_headers")] headers: HeaderMap, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "headers": { "Accept": ["*/*"] } }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(&"*/*", decoded.headers.get("Accept").unwrap()); - let recoded = serde_json::to_value(decoded).unwrap(); - let decoded: Test = serde_json::from_value(recoded).unwrap(); + let recoded = aws_lambda_json_impl::to_value(decoded).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(recoded).unwrap(); assert_eq!(&"*/*", decoded.headers.get("Accept").unwrap()); } @@ -183,9 +183,9 @@ mod tests { #[serde(deserialize_with = "deserialize_headers")] headers: HeaderMap, } - let data = serde_json::json!({ "headers": null }); + let data = aws_lambda_json_impl::json!({ "headers": null }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert!(decoded.headers.is_empty()); } @@ -203,7 +203,7 @@ mod tests { let content_disposition = "inline; filename=\"Schillers schönste Szenenanweisungen -Kabale und Liebe.mp4.avif\""; - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "headers": { "Content-Disposition": content_disposition }, @@ -211,15 +211,15 @@ mod tests { "Content-Disposition": content_disposition } }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(content_disposition, decoded.headers.get("Content-Disposition").unwrap()); assert_eq!( content_disposition, decoded.multi_value_headers.get("Content-Disposition").unwrap() ); - let recoded = serde_json::to_value(decoded).unwrap(); - let decoded: Test = serde_json::from_value(recoded).unwrap(); + let recoded = aws_lambda_json_impl::to_value(decoded).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(recoded).unwrap(); assert_eq!(content_disposition, decoded.headers.get("Content-Disposition").unwrap()); assert_eq!( content_disposition, diff --git a/lambda-events/src/custom_serde/http_method.rs b/lambda-events/src/custom_serde/http_method.rs index 42a94dd7..cdf5b16e 100644 --- a/lambda-events/src/custom_serde/http_method.rs +++ b/lambda-events/src/custom_serde/http_method.rs @@ -67,13 +67,13 @@ mod tests { #[serde(with = "crate::custom_serde::http_method")] pub method: http::Method, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "method": "DELETE" }); - let decoded: Test = serde_json::from_value(data.clone()).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data.clone()).unwrap(); assert_eq!(http::Method::DELETE, decoded.method); - let recoded = serde_json::to_value(decoded).unwrap(); + let recoded = aws_lambda_json_impl::to_value(decoded).unwrap(); assert_eq!(data, recoded); } @@ -86,21 +86,21 @@ mod tests { #[serde(default)] pub method: Option<http::Method>, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "method": "DELETE" }); - let decoded: Test = serde_json::from_value(data.clone()).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data.clone()).unwrap(); assert_eq!(Some(http::Method::DELETE), decoded.method); - let recoded = serde_json::to_value(decoded).unwrap(); + let recoded = aws_lambda_json_impl::to_value(decoded).unwrap(); assert_eq!(data, recoded); - let data = serde_json::json!({ "method": null }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let data = aws_lambda_json_impl::json!({ "method": null }); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(None, decoded.method); - let data = serde_json::json!({}); - let decoded: Test = serde_json::from_value(data).unwrap(); + let data = aws_lambda_json_impl::json!({}); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(None, decoded.method); } } diff --git a/lambda-events/src/custom_serde/mod.rs b/lambda-events/src/custom_serde/mod.rs index 729dee3d..98ed8a81 100644 --- a/lambda-events/src/custom_serde/mod.rs +++ b/lambda-events/src/custom_serde/mod.rs @@ -107,10 +107,10 @@ mod test { #[serde(deserialize_with = "deserialize_base64")] v: Vec<u8>, } - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": "SGVsbG8gV29ybGQ=", }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(String::from_utf8(decoded.v).unwrap(), "Hello World".to_string()); } @@ -124,7 +124,7 @@ mod test { let instance = Test { v: "Hello World".as_bytes().to_vec(), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, r#"{"v":"SGVsbG8gV29ybGQ="}"#.to_string()); } @@ -135,16 +135,16 @@ mod test { #[serde(deserialize_with = "deserialize_lambda_map")] v: HashMap<String, String>, } - let input = serde_json::json!({ + let input = aws_lambda_json_impl::json!({ "v": {}, }); - let decoded: Test = serde_json::from_value(input).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(input).unwrap(); assert_eq!(HashMap::new(), decoded.v); - let input = serde_json::json!({ + let input = aws_lambda_json_impl::json!({ "v": null, }); - let decoded: Test = serde_json::from_value(input).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(input).unwrap(); assert_eq!(HashMap::new(), decoded.v); } @@ -156,19 +156,20 @@ mod test { #[serde(deserialize_with = "deserialize_lambda_dynamodb_item")] v: serde_dynamo::Item, } - let input = serde_json::json!({ + let input = aws_lambda_json_impl::json!({ "v": {}, }); - let decoded: Test = serde_json::from_value(input).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(input).unwrap(); assert_eq!(serde_dynamo::Item::from(HashMap::new()), decoded.v); - let input = serde_json::json!({ + let input = aws_lambda_json_impl::json!({ "v": null, }); - let decoded: Test = serde_json::from_value(input).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(input).unwrap(); assert_eq!(serde_dynamo::Item::from(HashMap::new()), decoded.v); } + #[cfg(not(feature = "simd_json"))] #[test] fn test_deserialize_nullish_boolean() { #[derive(Deserialize)] @@ -178,19 +179,48 @@ mod test { } let test = r#"{"v": null}"#; - let decoded: Test = serde_json::from_str(test).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_str(test).unwrap(); assert!(!decoded.v); let test = r#"{}"#; - let decoded: Test = serde_json::from_str(test).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_str(test).unwrap(); assert!(!decoded.v); let test = r#"{"v": true}"#; - let decoded: Test = serde_json::from_str(test).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_str(test).unwrap(); assert!(decoded.v); let test = r#"{"v": false}"#; - let decoded: Test = serde_json::from_str(test).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_str(test).unwrap(); + assert!(!decoded.v); + } + #[cfg(feature = "simd_json")] + #[test] + fn test_deserialize_nullish_boolean() { + // + // Let's have some fun! This is how to do SAFE from_str with simd_json + // + + #[derive(Deserialize)] + struct Test { + #[serde(default, deserialize_with = "deserialize_nullish_boolean")] + v: bool, + } + + let test = r#"{"v": null}"#.to_owned(); + let decoded: Test = aws_lambda_json_impl::from_string(test).unwrap(); + assert!(!decoded.v); + + let test = r#"{}"#.to_owned(); + let decoded: Test = aws_lambda_json_impl::from_string(test).unwrap(); + assert!(!decoded.v); + + let test = r#"{"v": true}"#.to_owned(); + let decoded: Test = aws_lambda_json_impl::from_string(test).unwrap(); + assert!(decoded.v); + + let test = r#"{"v": false}"#.to_owned(); + let decoded: Test = aws_lambda_json_impl::from_string(test).unwrap(); assert!(!decoded.v); } } diff --git a/lambda-events/src/encodings/http.rs b/lambda-events/src/encodings/http.rs index 56cce76a..523c124d 100644 --- a/lambda-events/src/encodings/http.rs +++ b/lambda-events/src/encodings/http.rs @@ -304,21 +304,21 @@ mod tests { fn serialize_text() { let mut map = HashMap::new(); map.insert("foo", Body::from("bar")); - assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":"bar"}"#); + assert_eq!(aws_lambda_json_impl::to_string(&map).unwrap(), r#"{"foo":"bar"}"#); } #[test] fn serialize_binary() { let mut map = HashMap::new(); map.insert("foo", Body::from("bar".as_bytes())); - assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":"YmFy"}"#); + assert_eq!(aws_lambda_json_impl::to_string(&map).unwrap(), r#"{"foo":"YmFy"}"#); } #[test] fn serialize_empty() { let mut map = HashMap::new(); map.insert("foo", Body::Empty); - assert_eq!(serde_json::to_string(&map).unwrap(), r#"{"foo":null}"#); + assert_eq!(aws_lambda_json_impl::to_string(&map).unwrap(), r#"{"foo":null}"#); } #[test] diff --git a/lambda-events/src/encodings/time.rs b/lambda-events/src/encodings/time.rs index d4903360..24856416 100644 --- a/lambda-events/src/encodings/time.rs +++ b/lambda-events/src/encodings/time.rs @@ -215,6 +215,10 @@ where #[cfg(test)] #[allow(deprecated)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; use chrono::TimeZone; @@ -228,19 +232,20 @@ mod test { let expected = Utc.ymd(2017, 10, 5).and_hms_nano(15, 33, 44, 302_000_000); // Test parsing strings. - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": "1507217624302", }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); // Test parsing ints. - let decoded: Test = serde_json::from_slice(r#"{"v":1507217624302}"#.as_bytes()).unwrap(); + let mut test = r#"{"v":1507217624302}"#.as_bytes().to_vec(); + let decoded: Test = aws_lambda_json_impl::from_slice(test.as_mut_slice()).unwrap(); assert_eq!(expected, decoded.v,); // Test parsing floats. - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": 1507217624302.0, }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); } @@ -254,7 +259,7 @@ mod test { let instance = Test { v: Utc.ymd(1983, 7, 22).and_hms_nano(1, 0, 0, 99_888_777), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":"427683600099"}"#)); } @@ -270,21 +275,21 @@ mod test { let instance = Test { v: Utc.ymd(1983, 7, 22).and_hms_nano(1, 0, 0, 99), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":"427683600"}"#)); // Make sure milliseconds are included. let instance = Test { v: Utc.ymd(1983, 7, 22).and_hms_nano(1, 0, 0, 2_000_000), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":"427683600.002"}"#)); // Make sure leap seconds are included. let instance = Test { v: Utc.ymd(1983, 7, 22).and_hms_nano(23, 59, 59, 1_999_999_999), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":"427766400.999"}"#)); } @@ -298,16 +303,16 @@ mod test { let expected = TimeDelta::try_seconds(36).unwrap(); - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": 36, }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": 36.1, }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); } @@ -321,7 +326,7 @@ mod test { let instance = Test { v: TimeDelta::try_seconds(36).unwrap(), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":36}"#)); } @@ -335,16 +340,16 @@ mod test { let expected = TimeDelta::try_minutes(36).unwrap(); - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": 36, }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "v": 36.1, }); - let decoded: Test = serde_json::from_value(data).unwrap(); + let decoded: Test = aws_lambda_json_impl::from_value(data).unwrap(); assert_eq!(expected, decoded.v,); } @@ -358,7 +363,7 @@ mod test { let instance = Test { v: TimeDelta::try_minutes(36).unwrap(), }; - let encoded = serde_json::to_string(&instance).unwrap(); + let encoded = aws_lambda_json_impl::to_string(&instance).unwrap(); assert_eq!(encoded, String::from(r#"{"v":36}"#)); } } diff --git a/lambda-events/src/event/activemq/mod.rs b/lambda-events/src/event/activemq/mod.rs index 89cfda1c..de2b0752 100644 --- a/lambda-events/src/event/activemq/mod.rs +++ b/lambda-events/src/event/activemq/mod.rs @@ -52,15 +52,19 @@ pub struct ActiveMqDestination { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. use super::*; #[test] #[cfg(feature = "activemq")] fn example_activemq_event() { - let data = include_bytes!("../../fixtures/example-activemq-event.json"); - let parsed: ActiveMqEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ActiveMqEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let data = include_bytes!("../../fixtures/example-activemq-event.json").to_vec(); + let mut data = data.to_vec(); + let parsed: ActiveMqEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ActiveMqEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/alb/mod.rs b/lambda-events/src/event/alb/mod.rs index 3b4ce9d5..67644a5a 100644 --- a/lambda-events/src/event/alb/mod.rs +++ b/lambda-events/src/event/alb/mod.rs @@ -69,35 +69,40 @@ pub struct AlbTargetGroupResponse { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "alb")] fn example_alb_lambda_target_request_headers_only() { - let data = include_bytes!("../../fixtures/example-alb-lambda-target-request-headers-only.json"); - let parsed: AlbTargetGroupRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AlbTargetGroupRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-alb-lambda-target-request-headers-only.json").to_vec(); + let parsed: AlbTargetGroupRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AlbTargetGroupRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "alb")] fn example_alb_lambda_target_request_multivalue_headers() { - let data = include_bytes!("../../fixtures/example-alb-lambda-target-request-multivalue-headers.json"); - let parsed: AlbTargetGroupRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AlbTargetGroupRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-alb-lambda-target-request-multivalue-headers.json").to_vec(); + let parsed: AlbTargetGroupRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AlbTargetGroupRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "alb")] fn example_alb_lambda_target_response() { - let data = include_bytes!("../../fixtures/example-alb-lambda-target-response.json"); - let parsed: AlbTargetGroupResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AlbTargetGroupResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-alb-lambda-target-response.json").to_vec(); + let parsed: AlbTargetGroupResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AlbTargetGroupResponse = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/apigw/mod.rs b/lambda-events/src/event/apigw/mod.rs index e0aa1e8c..7e907200 100644 --- a/lambda-events/src/event/apigw/mod.rs +++ b/lambda-events/src/event/apigw/mod.rs @@ -6,10 +6,10 @@ use crate::{ encodings::Body, iam::IamPolicyStatement, }; +use aws_lambda_json_impl::Value; use http::{HeaderMap, Method}; use query_map::QueryMap; use serde::{de::DeserializeOwned, ser::SerializeMap, Deserialize, Deserializer, Serialize, Serializer}; -use serde_json::Value; use std::collections::HashMap; /// `ApiGatewayProxyRequest` contains data coming from the API Gateway proxy @@ -779,213 +779,235 @@ pub fn serialize_authorizer_fields<S: Serializer>( #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. use super::*; #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_request_type_request() { - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-request-type-request.json"); - let parsed: ApiGatewayCustomAuthorizerRequestTypeRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerRequestTypeRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-custom-auth-request-type-request.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerRequestTypeRequest = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerRequestTypeRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_request_type_request_websocket() { - let data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-websocket-request.json"); - let parsed: ApiGatewayCustomAuthorizerRequestTypeRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerRequestTypeRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-websocket-request.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerRequestTypeRequest = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerRequestTypeRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_request() { - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-request.json"); - let parsed: ApiGatewayCustomAuthorizerRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-custom-auth-request.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_response() { - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-response.json"); - let parsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-custom-auth-response.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_response_with_single_value_action() { - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-single-value-action.json"); - let parsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-single-value-action.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_custom_auth_response_with_single_value_resource() { - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-single-value-resource.json"); - let parsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-single-value-resource.json") + .to_vec(); + let parsed: ApiGatewayCustomAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_request() { - let data = include_bytes!("../../fixtures/example-apigw-request.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-request.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_response() { - let data = include_bytes!("../../fixtures/example-apigw-response.json"); - let parsed: ApiGatewayProxyResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-response.json").to_vec(); + let parsed: ApiGatewayProxyResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayProxyResponse = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_request_multi_value_parameters() { - let data = include_bytes!("../../fixtures/example-apigw-request-multi-value-parameters.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); - assert_eq!(parsed, reparsed); + let mut data = include_bytes!("../../fixtures/example-apigw-request-multi-value-parameters.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let output = aws_lambda_json_impl::to_string(&parsed).unwrap(); assert!(output.contains(r#""multiValueQueryStringParameters":{"name":["me","me2"]}"#)); assert!(output.contains(r#""queryStringParameters":{"name":"me"}"#)); assert!(output.contains(r#""headername":["headerValue","headerValue2"]"#)); assert!(output.contains(r#""headername":"headerValue2""#)); + + let mut output = output.into_bytes(); + let reparsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); + assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_restapi_openapi_request() { - let data = include_bytes!("../../fixtures/example-apigw-restapi-openapi-request.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-restapi-openapi-request.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_iam() { - let data = include_bytes!("../../fixtures/example-apigw-v2-request-iam.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-request-iam.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_jwt_authorizer() { - let data = include_bytes!("../../fixtures/example-apigw-v2-request-jwt-authorizer.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-request-jwt-authorizer.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_lambda_authorizer() { - let data = include_bytes!("../../fixtures/example-apigw-v2-request-lambda-authorizer.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-request-lambda-authorizer.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_multi_value_parameters() { - let data = include_bytes!("../../fixtures/example-apigw-v2-request-multi-value-parameters.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); - assert_eq!(parsed, reparsed); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-request-multi-value-parameters.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let output = aws_lambda_json_impl::to_string(&parsed).unwrap(); assert!(output.contains(r#""header2":"value1,value2""#)); assert!(output.contains(r#""queryStringParameters":{"Parameter1":"value1,value2"}"#)); + + let mut output = output.into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); + assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_request_no_authorizer() { - let data = include_bytes!("../../fixtures/example-apigw-v2-request-no-authorizer.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-request-no-authorizer.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_websocket_request() { - let data = include_bytes!("../../fixtures/example-apigw-websocket-request.json"); - let parsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-websocket-request.json").to_vec(); + let parsed: ApiGatewayWebsocketProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayWebsocketProxyRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_console_test_request() { - let data = include_bytes!("../../fixtures/example-apigw-console-test-request.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-console-test-request.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_websocket_request_without_method() { - let data = include_bytes!("../../fixtures/example-apigw-websocket-request-without-method.json"); - let parsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-websocket-request-without-method.json").to_vec(); + let parsed: ApiGatewayWebsocketProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayWebsocketProxyRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_websocket_request_disconnect_route() { - let data = include_bytes!("../../fixtures/example-apigw-websocket-request-disconnect-route.json"); - let parsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayWebsocketProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-websocket-request-disconnect-route.json").to_vec(); + let parsed: ApiGatewayWebsocketProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayWebsocketProxyRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_custom_authorizer_v1_request() { - let data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v1-request.json"); - let parsed: ApiGatewayV2httpRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2httpRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v1-request.json").to_vec(); + let parsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2httpRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); assert_eq!("REQUEST", parsed.kind.unwrap()); assert_eq!(Method::GET, parsed.http_method); @@ -994,51 +1016,64 @@ mod test { #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_custom_authorizer_v2_request() { - let data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request.json"); - let parsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request.json").to_vec(); + let parsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_custom_authorizer_v2_request_without_cookies() { - let data = include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request-without-cookies.json"); - let parsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request-without-cookies.json") + .to_vec(); + let parsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_v2_custom_authorizer_v2_request_without_identity_source() { - let data = - include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request-without-identity-source.json"); - let parsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayV2CustomAuthorizerV2Request = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-apigw-v2-custom-authorizer-v2-request-without-identity-source.json") + .to_vec(); + let parsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayV2CustomAuthorizerV2Request = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_console_request() { - let data = include_bytes!("../../fixtures/example-apigw-console-request.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayProxyRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-console-request.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "apigw")] fn example_apigw_request_authorizer_fields() { - let data = include_bytes!("../../fixtures/example-apigw-request.json"); - let parsed: ApiGatewayProxyRequest = serde_json::from_slice(data).unwrap(); + #[cfg(feature = "simd_json")] + use aws_lambda_json_impl::simd_json::base::ValueAsScalar; + + let mut data = include_bytes!("../../fixtures/example-apigw-request.json").to_vec(); + let parsed: ApiGatewayProxyRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); let fields = parsed.request_context.authorizer.fields; + assert_eq!(Some("admin"), fields.get("principalId").unwrap().as_str()); assert_eq!(Some(1), fields.get("clientId").unwrap().as_u64()); assert_eq!(Some("Exata"), fields.get("clientName").unwrap().as_str()); @@ -1049,10 +1084,11 @@ mod test { fn example_apigw_custom_auth_response_with_statement_condition() { use crate::iam::IamPolicyEffect; - let data = include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-condition.json"); - let parsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ApiGatewayCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-apigw-custom-auth-response-with-condition.json").to_vec(); + let parsed: ApiGatewayCustomAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ApiGatewayCustomAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); let statement = parsed.policy_document.statement.first().unwrap(); diff --git a/lambda-events/src/event/appsync/mod.rs b/lambda-events/src/event/appsync/mod.rs index 63f9ac74..52eaee26 100644 --- a/lambda-events/src/event/appsync/mod.rs +++ b/lambda-events/src/event/appsync/mod.rs @@ -1,5 +1,5 @@ +use aws_lambda_json_impl::Value; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; @@ -119,45 +119,50 @@ where #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "appsync")] fn example_appsync_identity_cognito() { - let data = include_bytes!("../../fixtures/example-appsync-identity-cognito.json"); - let parsed: AppSyncCognitoIdentity = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AppSyncCognitoIdentity = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-appsync-identity-cognito.json").to_vec(); + let parsed: AppSyncCognitoIdentity = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AppSyncCognitoIdentity = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "appsync")] fn example_appsync_identity_iam() { - let data = include_bytes!("../../fixtures/example-appsync-identity-iam.json"); - let parsed: AppSyncIamIdentity = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AppSyncIamIdentity = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-appsync-identity-iam.json").to_vec(); + let parsed: AppSyncIamIdentity = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AppSyncIamIdentity = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "appsync")] fn example_appsync_lambda_auth_request() { - let data = include_bytes!("../../fixtures/example-appsync-lambda-auth-request.json"); - let parsed: AppSyncLambdaAuthorizerRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AppSyncLambdaAuthorizerRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-appsync-lambda-auth-request.json").to_vec(); + let parsed: AppSyncLambdaAuthorizerRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AppSyncLambdaAuthorizerRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "appsync")] fn example_appsync_lambda_auth_response() { - let data = include_bytes!("../../fixtures/example-appsync-lambda-auth-response.json"); - let parsed: AppSyncLambdaAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AppSyncLambdaAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-appsync-lambda-auth-response.json").to_vec(); + let parsed: AppSyncLambdaAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AppSyncLambdaAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/autoscaling/mod.rs b/lambda-events/src/event/autoscaling/mod.rs index 601e8774..b354aa5a 100644 --- a/lambda-events/src/event/autoscaling/mod.rs +++ b/lambda-events/src/event/autoscaling/mod.rs @@ -1,6 +1,6 @@ +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; @@ -45,65 +45,69 @@ where #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_launch_successful() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-launch-successful.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-launch-successful.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_launch_unsuccessful() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-launch-unsuccessful.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-launch-unsuccessful.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_lifecycle_action() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-lifecycle-action.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-lifecycle-action.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_terminate_action() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-action.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-action.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_terminate_successful() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-successful.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-successful.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "autoscaling")] fn example_autoscaling_event_terminate_unsuccessful() { - let data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-unsuccessful.json"); - let parsed: AutoScalingEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AutoScalingEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-autoscaling-event-terminate-unsuccessful.json").to_vec(); + let parsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AutoScalingEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/bedrock_agent_runtime/mod.rs b/lambda-events/src/event/bedrock_agent_runtime/mod.rs index c1425b85..2c19b2d6 100644 --- a/lambda-events/src/event/bedrock_agent_runtime/mod.rs +++ b/lambda-events/src/event/bedrock_agent_runtime/mod.rs @@ -82,34 +82,39 @@ pub struct Agent { #[cfg(test)] mod tests { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. use crate::event::bedrock_agent_runtime::AgentEvent; #[test] #[cfg(feature = "bedrock_agent_runtime")] fn example_bedrock_agent_runtime_event() { - let data = include_bytes!("../../fixtures/example-bedrock-agent-runtime-event.json"); - let parsed: AgentEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AgentEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-bedrock-agent-runtime-event.json").to_vec(); + let parsed: AgentEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AgentEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "bedrock_agent_runtime")] fn example_bedrock_agent_runtime_event_without_parameters() { - let data = include_bytes!("../../fixtures/example-bedrock-agent-runtime-event-without-parameters.json"); - let parsed: AgentEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AgentEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-bedrock-agent-runtime-event-without-parameters.json").to_vec(); + let parsed: AgentEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AgentEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "bedrock_agent_runtime")] fn example_bedrock_agent_runtime_event_without_request_body() { - let data = include_bytes!("../../fixtures/example-bedrock-agent-runtime-event-without-request-body.json"); - let parsed: AgentEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AgentEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-bedrock-agent-runtime-event-without-request-body.json").to_vec(); + let parsed: AgentEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AgentEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/clientvpn/mod.rs b/lambda-events/src/event/clientvpn/mod.rs index 163abe72..9a1eda6a 100644 --- a/lambda-events/src/event/clientvpn/mod.rs +++ b/lambda-events/src/event/clientvpn/mod.rs @@ -47,15 +47,20 @@ pub struct ClientVpnConnectionHandlerResponse { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "clientvpn")] fn example_clientvpn_connectionhandler_request() { - let data = include_bytes!("../../fixtures/example-clientvpn-connectionhandler-request.json"); - let parsed: ClientVpnConnectionHandlerRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ClientVpnConnectionHandlerRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-clientvpn-connectionhandler-request.json").to_vec(); + let parsed: ClientVpnConnectionHandlerRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ClientVpnConnectionHandlerRequest = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cloudformation/mod.rs b/lambda-events/src/event/cloudformation/mod.rs index 44156b97..97184806 100644 --- a/lambda-events/src/event/cloudformation/mod.rs +++ b/lambda-events/src/event/cloudformation/mod.rs @@ -1,5 +1,5 @@ +use aws_lambda_json_impl::Value; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; pub mod provider; @@ -100,6 +100,10 @@ pub enum CloudFormationCustomResourceResponseStatus { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use std::collections::HashMap; use super::{CloudFormationCustomResourceRequest::*, *}; @@ -116,55 +120,60 @@ mod test { #[test] fn example_cloudformation_custom_resource_create_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-create-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-create-request.json").to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Create(_) => (), _ => panic!("expected Create request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_cloudformation_custom_resource_update_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-update-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-update-request.json").to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Update(_) => (), _ => panic!("expected Update request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_cloudformation_custom_resource_delete_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-delete-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-delete-request.json").to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Delete(_) => (), _ => panic!("expected Delete request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_cloudformation_custom_resource_response() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-response.json"); - let parsed: CloudFormationCustomResourceResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CloudFormationCustomResourceResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-response.json").to_vec(); + let parsed: CloudFormationCustomResourceResponse = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CloudFormationCustomResourceResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cloudformation/provider.rs b/lambda-events/src/event/cloudformation/provider.rs index a1594eb4..a5bd2107 100644 --- a/lambda-events/src/event/cloudformation/provider.rs +++ b/lambda-events/src/event/cloudformation/provider.rs @@ -4,8 +4,8 @@ //! //! See https://docs.aws.amazon.com/cdk/api/v2/docs/aws-cdk-lib.custom_resources-readme.html for details. +use aws_lambda_json_impl::Value; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; #[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] #[serde(tag = "RequestType")] @@ -88,6 +88,10 @@ where #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use std::collections::HashMap; use super::{CloudFormationCustomResourceRequest::*, *}; @@ -104,55 +108,64 @@ mod test { #[test] fn example_create_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-create-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-create-request.json") + .to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Create(_) => (), _ => panic!("expected Create request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_update_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-update-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-update-request.json") + .to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Update(_) => (), _ => panic!("expected Update request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_delete_request() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-delete-request.json"); - let parsed: TestRequest = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-delete-request.json") + .to_vec(); + let parsed: TestRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); match parsed { Delete(_) => (), _ => panic!("expected Delete request"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: TestRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: TestRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_response() { - let data = include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-response.json"); - let parsed: CloudFormationCustomResourceResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CloudFormationCustomResourceResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudformation-custom-resource-provider-response.json").to_vec(); + let parsed: CloudFormationCustomResourceResponse = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CloudFormationCustomResourceResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cloudwatch_alarms/mod.rs b/lambda-events/src/event/cloudwatch_alarms/mod.rs index 01174566..1e6b220d 100644 --- a/lambda-events/src/event/cloudwatch_alarms/mod.rs +++ b/lambda-events/src/event/cloudwatch_alarms/mod.rs @@ -1,12 +1,12 @@ use std::collections::HashMap; +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{ de::{DeserializeOwned, Visitor}, ser::Error as SerError, Deserialize, Serialize, }; -use serde_json::Value; /// `CloudWatchAlarm` is the generic outer structure of an event triggered by a CloudWatch Alarm. /// You probably want to use `CloudWatchMetricAlarm` or `CloudWatchCompositeAlarm` if you know which kind of alarm your function is receiving. @@ -220,9 +220,9 @@ impl Serialize for CloudWatchAlarmStateReasonData { S: serde::Serializer, { let r = match self { - Self::Metric(m) => serde_json::to_string(m), - Self::Composite(m) => serde_json::to_string(m), - Self::Generic(m) => serde_json::to_string(m), + Self::Metric(m) => aws_lambda_json_impl::to_string(m), + Self::Composite(m) => aws_lambda_json_impl::to_string(m), + Self::Generic(m) => aws_lambda_json_impl::to_string(m), }; let s = r.map_err(|e| SerError::custom(format!("failed to serialize struct as string {}", e)))?; @@ -243,10 +243,15 @@ impl<'de> Visitor<'de> for ReasonDataVisitor { where E: serde::de::Error, { - if let Ok(metric) = serde_json::from_str::<CloudWatchAlarmStateReasonDataMetric>(v) { + let mut v_mut = v.to_owned().into_bytes(); + if let Ok(metric) = + aws_lambda_json_impl::from_slice::<CloudWatchAlarmStateReasonDataMetric>(v_mut.as_mut_slice()) + { return Ok(CloudWatchAlarmStateReasonData::Metric(metric)); } - if let Ok(aggregate) = serde_json::from_str::<ClodWatchAlarmStateReasonDataComposite>(v) { + if let Ok(aggregate) = + aws_lambda_json_impl::from_slice::<ClodWatchAlarmStateReasonDataComposite>(v_mut.as_mut_slice()) + { return Ok(CloudWatchAlarmStateReasonData::Composite(aggregate)); } Ok(CloudWatchAlarmStateReasonData::Generic(Value::String(v.to_owned()))) @@ -255,13 +260,17 @@ impl<'de> Visitor<'de> for ReasonDataVisitor { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "cloudwatch_alarms")] fn example_cloudwatch_alarm_metric() { - let data = include_bytes!("../../fixtures/example-cloudwatch-alarm-metric.json"); - let parsed: CloudWatchMetricAlarm = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch-alarm-metric.json").to_vec(); + let parsed: CloudWatchMetricAlarm = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); let state = parsed.alarm_data.previous_state.clone().unwrap(); let data = state.reason_data.unwrap(); match &data { @@ -272,16 +281,16 @@ mod test { _ => panic!("unexpected reason data {data:?}"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CloudWatchMetricAlarm = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CloudWatchMetricAlarm = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cloudwatch_alarms")] fn example_cloudwatch_alarm_composite() { - let data = include_bytes!("../../fixtures/example-cloudwatch-alarm-composite.json"); - let parsed: CloudWatchCompositeAlarm = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch-alarm-composite.json").to_vec(); + let parsed: CloudWatchCompositeAlarm = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); let state = parsed.alarm_data.state.clone().unwrap(); let data = state.reason_data.unwrap(); @@ -296,16 +305,17 @@ mod test { _ => panic!("unexpected reason data {data:?}"), } - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CloudWatchCompositeAlarm = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CloudWatchCompositeAlarm = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cloudwatch_alarms")] fn example_cloudwatch_alarm_composite_with_suppressor_alarm() { - let data = include_bytes!("../../fixtures/example-cloudwatch-alarm-composite-with-suppressor-alarm.json"); - let parsed: CloudWatchCompositeAlarm = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudwatch-alarm-composite-with-suppressor-alarm.json").to_vec(); + let parsed: CloudWatchCompositeAlarm = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); let state = parsed.alarm_data.state.clone().unwrap(); assert_eq!("WaitPeriod", state.actions_suppressed_by.unwrap()); assert_eq!( @@ -313,8 +323,8 @@ mod test { state.actions_suppressed_reason.unwrap() ); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CloudWatchCompositeAlarm = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CloudWatchCompositeAlarm = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cloudwatch_events/cloudtrail.rs b/lambda-events/src/event/cloudwatch_events/cloudtrail.rs index fc332306..45c65474 100644 --- a/lambda-events/src/event/cloudwatch_events/cloudtrail.rs +++ b/lambda-events/src/event/cloudwatch_events/cloudtrail.rs @@ -1,6 +1,6 @@ +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; -use serde_json::Value; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] @@ -83,33 +83,37 @@ pub struct UserIdentity { #[cfg(test)] mod tests { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::AWSAPICall; #[test] #[cfg(feature = "cloudwatch_events")] fn example_cloudwatch_cloudtrail_unknown_assumed_role() { - let data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-assumed-role.json"); - let parsed: AWSAPICall = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AWSAPICall = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-assumed-role.json").to_vec(); + let parsed: AWSAPICall = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AWSAPICall = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cloudwatch_events")] fn example_cloudwatch_cloudtrail_unknown_federate() { - let data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-unknown-federate.json"); - let parsed: AWSAPICall = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AWSAPICall = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-unknown-federate.json").to_vec(); + let parsed: AWSAPICall = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AWSAPICall = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cloudwatch_events")] fn example_cloudwatch_cloudtrail_assumed_role() { - let data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-unknown-user-auth.json"); - let parsed: AWSAPICall = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: AWSAPICall = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch-cloudtrail-unknown-user-auth.json").to_vec(); + let parsed: AWSAPICall = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: AWSAPICall = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cloudwatch_events/mod.rs b/lambda-events/src/event/cloudwatch_events/mod.rs index 6384406e..dd88e313 100644 --- a/lambda-events/src/event/cloudwatch_events/mod.rs +++ b/lambda-events/src/event/cloudwatch_events/mod.rs @@ -1,6 +1,6 @@ +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; pub mod cloudtrail; pub mod codedeploy; diff --git a/lambda-events/src/event/cloudwatch_events/signin.rs b/lambda-events/src/event/cloudwatch_events/signin.rs index 1cd73e6e..82090e0f 100644 --- a/lambda-events/src/event/cloudwatch_events/signin.rs +++ b/lambda-events/src/event/cloudwatch_events/signin.rs @@ -1,5 +1,5 @@ +use aws_lambda_json_impl::Value; use serde::{Deserialize, Serialize}; -use serde_json::Value; #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)] #[serde(rename_all = "camelCase")] diff --git a/lambda-events/src/event/cloudwatch_logs/mod.rs b/lambda-events/src/event/cloudwatch_logs/mod.rs index 0c9ad4a8..73c22343 100644 --- a/lambda-events/src/event/cloudwatch_logs/mod.rs +++ b/lambda-events/src/event/cloudwatch_logs/mod.rs @@ -80,8 +80,7 @@ impl<'de> Deserialize<'de> for AwsLogs { })?; let bytes = flate2::read::GzDecoder::new(&bytes[..]); - let mut de = serde_json::Deserializer::from_reader(BufReader::new(bytes)); - data = Some(LogData::deserialize(&mut de).map_err(Error::custom)?); + data = aws_lambda_json_impl::from_reader(BufReader::new(bytes)).map_err(Error::custom)?; } _ => return Err(Error::unknown_field(key, FIELDS)), } @@ -105,7 +104,7 @@ impl Serialize for AwsLogs { let base = base64::write::EncoderWriter::new(Vec::new(), &base64::engine::general_purpose::STANDARD_NO_PAD); let mut gzip = flate2::write::GzEncoder::new(base, flate2::Compression::default()); - serde_json::to_writer(&mut gzip, &self.data).map_err(SeError::custom)?; + aws_lambda_json_impl::to_writer(&mut gzip, &self.data).map_err(SeError::custom)?; let mut base = gzip.finish().map_err(SeError::custom)?; let data = base.finish().map_err(SeError::custom)?; let string = std::str::from_utf8(data.as_slice()).map_err(SeError::custom)?; @@ -118,6 +117,10 @@ impl Serialize for AwsLogs { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] @@ -127,8 +130,8 @@ mod test { "awslogs": { "data": "H4sIAFETomIAA12Ry27bMBBF9/4KQuiyqsQ36Z2DqEGBGC0sdRUHAS0NExV6uCJVNw3y76Fkx03CFTH3cubwztMChRO14Jy5h+JxD9ESRZerYnW3zvJ8dZVFn4+W/tDBMImYUMaFVDrF5FVs+vuroR/3k56Yg0sa0+4qk0D50MddX8Ev98aa+wFMO3lJinWS0gTT5ObT9arI8uJWM2uUkMCpZIxiorGRtsQMiOXCgHxt5MadK4d67+u++1o3HgYXWt7M4my4nhmOw+7Kph+rg/HlQwBwM1M0W2//c2V/oPPvmzydb7OpriZqygQhFItUa6GlUkymgrNUS5EKpQhRfMpGCEzC/xgWjCpNOBMn8nM3X4fcvWmn2DDnhGNFWXiffvCdtjON3mQ/vm8KtIHfY3j6rVoiEdaxsxZizLSJd4KRWGFrYwIKqBSVMtZu/eU4mCmoJWLii2KodVt/UTcNVOiNJEMdbf0a2n54RHn9DwKYJmh9EYrmLzoJPx2EwfJY33bRmfb5mOjiefECiB5LsVgCAAA=" } -}"#; - let event: LogsEvent = serde_json::from_str(json).expect("failed to deserialize"); +}"#.to_owned(); + let event: LogsEvent = aws_lambda_json_impl::from_string(json).expect("failed to deserialize"); let data = event.clone().aws_logs.data; assert_eq!("DATA_MESSAGE", data.message_type); @@ -145,18 +148,19 @@ mod test { assert_eq!(1552518348220, data.log_events[0].timestamp); assert_eq!("REPORT RequestId: 6234bffe-149a-b642-81ff-2e8e376d8aff\tDuration: 46.84 ms\tBilled Duration: 47 ms \tMemory Size: 192 MB\tMax Memory Used: 72 MB\t\n", data.log_events[0].message); - let new_json: String = serde_json::to_string_pretty(&event).unwrap(); - let new_event: LogsEvent = serde_json::from_str(&new_json).expect("failed to deserialize"); + let mut new_json = aws_lambda_json_impl::to_string_pretty(&event).unwrap().into_bytes(); + let new_event: LogsEvent = + aws_lambda_json_impl::from_slice(new_json.as_mut_slice()).expect("failed to deserialize"); assert_eq!(new_event, event); } #[test] #[cfg(feature = "cloudwatch_logs")] fn example_cloudwatch_logs_event() { - let data = include_bytes!("../../fixtures/example-cloudwatch_logs-event.json"); - let parsed: LogsEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: LogsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cloudwatch_logs-event.json").to_vec(); + let parsed: LogsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: LogsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/code_commit/mod.rs b/lambda-events/src/event/code_commit/mod.rs index 7d5edfaa..29ad37da 100644 --- a/lambda-events/src/event/code_commit/mod.rs +++ b/lambda-events/src/event/code_commit/mod.rs @@ -67,15 +67,19 @@ pub struct CodeCommitReference { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "code_commit")] fn example_code_commit_event() { - let data = include_bytes!("../../fixtures/example-code_commit-event.json"); - let parsed: CodeCommitEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeCommitEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-code_commit-event.json").to_vec(); + let parsed: CodeCommitEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeCommitEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/codebuild/mod.rs b/lambda-events/src/event/codebuild/mod.rs index d4970f5a..8ec86456 100644 --- a/lambda-events/src/event/codebuild/mod.rs +++ b/lambda-events/src/event/codebuild/mod.rs @@ -2,9 +2,9 @@ use crate::{ custom_serde::{codebuild_time, CodeBuildNumber}, encodings::{MinuteDuration, SecondDuration}, }; +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; pub type CodeBuildPhaseStatus = String; @@ -212,25 +212,29 @@ pub type CodeBuildTime = DateTime<Utc>; #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "codebuild")] fn example_codebuild_phase_change() { - let data = include_bytes!("../../fixtures/example-codebuild-phase-change.json"); - let parsed: CodeBuildEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeBuildEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codebuild-phase-change.json").to_vec(); + let parsed: CodeBuildEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeBuildEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "codebuild")] fn example_codebuild_state_change() { - let data = include_bytes!("../../fixtures/example-codebuild-state-change.json"); - let parsed: CodeBuildEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeBuildEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codebuild-state-change.json").to_vec(); + let parsed: CodeBuildEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeBuildEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/codedeploy/mod.rs b/lambda-events/src/event/codedeploy/mod.rs index d51bf8aa..cffd5939 100644 --- a/lambda-events/src/event/codedeploy/mod.rs +++ b/lambda-events/src/event/codedeploy/mod.rs @@ -74,39 +74,43 @@ pub struct CodeDeployLifecycleEvent { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "codedeploy")] fn example_codedeploy_lifecycle_event() { - let data = include_bytes!("../../fixtures/example-codedeploy-lifecycle-event.json"); - let parsed: CodeDeployLifecycleEvent = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codedeploy-lifecycle-event.json").to_vec(); + let parsed: CodeDeployLifecycleEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(parsed.deployment_id, "d-deploymentId".to_string()); assert_eq!(parsed.lifecycle_event_hook_execution_id, "eyJlbmNyeXB0ZWREYXRhIjoiY3VHU2NjdkJXUTJQUENVd2dkYUNGRVg0dWlpME9UWVdHTVhZcDRXVW5LYUVKc21EaUFPMkNLNXMwMWFrNDlYVStlbXdRb29xS3NJTUNVQ3RYRGFZSXc1VTFwUllvMDhmMzdlbDZFeDVVdjZrNFc0eU5waGh6YTRvdkNWcmVveVR6OWdERlM2SmlIYW1TZz09IiwiaXZQYXJhbWV0ZXJTcGVjIjoiTm1ZNFR6RzZxQVhHamhhLyIsIm1hdGVyaWFsU2V0U2VyaWFsIjoxfQ==".to_string()); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeDeployLifecycleEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeDeployLifecycleEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "codedeploy")] fn example_codedeploy_deployment_event() { - let data = include_bytes!("../../fixtures/example-codedeploy-deployment-event.json"); - let parsed: CodeDeployEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeDeployEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codedeploy-deployment-event.json").to_vec(); + let parsed: CodeDeployEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeDeployEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "codedeploy")] fn example_codedeploy_instance_event() { - let data = include_bytes!("../../fixtures/example-codedeploy-instance-event.json"); - let parsed: CodeDeployEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodeDeployEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codedeploy-instance-event.json").to_vec(); + let parsed: CodeDeployEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodeDeployEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/codepipeline_cloudwatch/mod.rs b/lambda-events/src/event/codepipeline_cloudwatch/mod.rs index 22db26b1..618809fa 100644 --- a/lambda-events/src/event/codepipeline_cloudwatch/mod.rs +++ b/lambda-events/src/event/codepipeline_cloudwatch/mod.rs @@ -79,35 +79,40 @@ pub struct CodePipelineEventDetailType { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "codepipeline_cloudwatch")] fn example_codepipeline_action_execution_stage_change_event() { - let data = include_bytes!("../../fixtures/example-codepipeline-action-execution-stage-change-event.json"); - let parsed: CodePipelineCloudWatchEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodePipelineCloudWatchEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-codepipeline-action-execution-stage-change-event.json").to_vec(); + let parsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "codepipeline_cloudwatch")] fn example_codepipeline_execution_stage_change_event() { - let data = include_bytes!("../../fixtures/example-codepipeline-execution-stage-change-event.json"); - let parsed: CodePipelineCloudWatchEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodePipelineCloudWatchEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codepipeline-execution-stage-change-event.json").to_vec(); + let parsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "codepipeline_cloudwatch")] fn example_codepipeline_execution_state_change_event() { - let data = include_bytes!("../../fixtures/example-codepipeline-execution-state-change-event.json"); - let parsed: CodePipelineCloudWatchEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodePipelineCloudWatchEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codepipeline-execution-state-change-event.json").to_vec(); + let parsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodePipelineCloudWatchEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/codepipeline_job/mod.rs b/lambda-events/src/event/codepipeline_job/mod.rs index 888e77b7..644c8b8d 100644 --- a/lambda-events/src/event/codepipeline_job/mod.rs +++ b/lambda-events/src/event/codepipeline_job/mod.rs @@ -115,15 +115,19 @@ pub struct CodePipelineArtifactCredentials { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "codepipeline_job")] fn example_codepipeline_job_event() { - let data = include_bytes!("../../fixtures/example-codepipeline_job-event.json"); - let parsed: CodePipelineJobEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CodePipelineJobEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-codepipeline_job-event.json").to_vec(); + let parsed: CodePipelineJobEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CodePipelineJobEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/cognito/mod.rs b/lambda-events/src/event/cognito/mod.rs index a0ebd8d9..aae7dd9e 100644 --- a/lambda-events/src/event/cognito/mod.rs +++ b/lambda-events/src/event/cognito/mod.rs @@ -1,5 +1,5 @@ +use aws_lambda_json_impl::Value; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::{deserialize_lambda_map, deserialize_nullish_boolean}; @@ -630,233 +630,279 @@ pub struct CognitoEventUserPoolsCustomMessageResponse { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "cognito")] fn example_cognito_event() { - let data = include_bytes!("../../fixtures/example-cognito-event.json"); - let parsed: CognitoEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event.json").to_vec(); + let parsed: CognitoEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_create_auth_challenge() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-create-auth-challenge.json"); - let parsed: CognitoEventUserPoolsCreateAuthChallenge = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsCreateAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-create-auth-challenge.json").to_vec(); + let parsed: CognitoEventUserPoolsCreateAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsCreateAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_create_auth_challenge_user_not_found() { - let data = - include_bytes!("../../fixtures/example-cognito-event-userpools-create-auth-challenge-user-not-found.json"); - let parsed: CognitoEventUserPoolsCreateAuthChallenge = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-create-auth-challenge-user-not-found.json") + .to_vec(); + let parsed: CognitoEventUserPoolsCreateAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(parsed.request.user_not_found); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsCreateAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsCreateAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_custommessage() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-custommessage.json"); - let parsed: CognitoEventUserPoolsCustomMessage = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsCustomMessage = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-custommessage.json").to_vec(); + let parsed: CognitoEventUserPoolsCustomMessage = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsCustomMessage = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_define_auth_challenge() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-define-auth-challenge.json"); - let parsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-define-auth-challenge.json").to_vec(); + let parsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_define_auth_challenge_optional_response_fields() { - let data = include_bytes!( + let mut data = include_bytes!( "../../fixtures/example-cognito-event-userpools-define-auth-challenge-optional-response-fields.json" - ); - let parsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(data).unwrap(); + ) + .to_vec(); + let parsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(!parsed.response.fail_authentication); assert!(!parsed.response.issue_tokens); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_define_auth_challenge_user_not_found() { - let data = - include_bytes!("../../fixtures/example-cognito-event-userpools-define-auth-challenge-user-not-found.json"); - let parsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-define-auth-challenge-user-not-found.json") + .to_vec(); + let parsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(parsed.request.user_not_found); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsDefineAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsDefineAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_migrateuser() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-migrateuser.json"); - let parsed: CognitoEventUserPoolsMigrateUser = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsMigrateUser = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-migrateuser.json").to_vec(); + let parsed: CognitoEventUserPoolsMigrateUser = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsMigrateUser = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_postauthentication() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-postauthentication.json"); - let parsed: CognitoEventUserPoolsPostAuthentication = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPostAuthentication = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-postauthentication.json").to_vec(); + let parsed: CognitoEventUserPoolsPostAuthentication = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPostAuthentication = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_postconfirmation() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-postconfirmation.json"); - let parsed: CognitoEventUserPoolsPostConfirmation = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPostConfirmation = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-postconfirmation.json").to_vec(); + let parsed: CognitoEventUserPoolsPostConfirmation = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPostConfirmation = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_preauthentication() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-preauthentication.json"); - let parsed: CognitoEventUserPoolsPreAuthentication = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreAuthentication = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-preauthentication.json").to_vec(); + let parsed: CognitoEventUserPoolsPreAuthentication = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreAuthentication = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_presignup() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-presignup.json"); - let parsed: CognitoEventUserPoolsPreSignup = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreSignup = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-presignup.json").to_vec(); + let parsed: CognitoEventUserPoolsPreSignup = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreSignup = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_pretokengen_incoming() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-incoming.json"); - let parsed: CognitoEventUserPoolsPreTokenGen = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreTokenGen = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-incoming.json").to_vec(); + let parsed: CognitoEventUserPoolsPreTokenGen = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreTokenGen = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_pretokengen_v2_incoming() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-v2-incoming.json"); - let parsed: CognitoEventUserPoolsPreTokenGenV2 = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreTokenGenV2 = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-v2-incoming.json").to_vec(); + let parsed: CognitoEventUserPoolsPreTokenGenV2 = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreTokenGenV2 = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_pretokengen() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen.json"); - let parsed: CognitoEventUserPoolsPreTokenGen = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreTokenGen = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen.json").to_vec(); + let parsed: CognitoEventUserPoolsPreTokenGen = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreTokenGen = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_v2_pretokengen() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-v2.json"); - let parsed: CognitoEventUserPoolsPreTokenGenV2 = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsPreTokenGenV2 = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-cognito-event-userpools-pretokengen-v2.json").to_vec(); + let parsed: CognitoEventUserPoolsPreTokenGenV2 = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsPreTokenGenV2 = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_verify_auth_challenge() { - let data = include_bytes!("../../fixtures/example-cognito-event-userpools-verify-auth-challenge.json"); - let parsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-verify-auth-challenge.json").to_vec(); + let parsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_verify_auth_challenge_optional_answer_correct() { - let data = include_bytes!( + let mut data = include_bytes!( "../../fixtures/example-cognito-event-userpools-verify-auth-challenge-optional-answer-correct.json" - ); - let parsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(data).unwrap(); + ) + .to_vec(); + let parsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(!parsed.response.answer_correct); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_verify_auth_challenge_null_answer_correct() { - let data = include_bytes!( + let mut data = include_bytes!( "../../fixtures/example-cognito-event-userpools-verify-auth-challenge-null-answer-correct.json" - ); - let parsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(data).unwrap(); + ) + .to_vec(); + let parsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(!parsed.response.answer_correct); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "cognito")] fn example_cognito_event_userpools_verify_auth_challenge_user_not_found() { - let data = - include_bytes!("../../fixtures/example-cognito-event-userpools-verify-auth-challenge-user-not-found.json"); - let parsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cognito-event-userpools-verify-auth-challenge-user-not-found.json") + .to_vec(); + let parsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert!(parsed.request.user_not_found); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsVerifyAuthChallenge = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } @@ -893,9 +939,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsPreSignupTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -906,9 +953,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsPreAuthenticationTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -922,9 +970,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsPostConfirmationTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -935,9 +984,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsPostAuthenticationTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -948,9 +998,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsDefineAuthChallengeTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -962,9 +1013,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsCreateAuthChallengeTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -975,9 +1027,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsVerifyAuthChallengeTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -994,9 +1047,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsPreTokenGenTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -1007,9 +1061,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsMigrateUserTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } @@ -1028,9 +1083,10 @@ mod trigger_source_tests { possible_triggers.into_iter().for_each(|trigger| { let header = gen_header(trigger); let parsed: CognitoEventUserPoolsHeader<CognitoEventUserPoolsCustomMessageTriggerSource> = - serde_json::from_str(&header).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: CognitoEventUserPoolsHeader<_> = serde_json::from_slice(output.as_bytes()).unwrap(); + aws_lambda_json_impl::from_string(header).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: CognitoEventUserPoolsHeader<_> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); }); } diff --git a/lambda-events/src/event/config/mod.rs b/lambda-events/src/event/config/mod.rs index 7c06e13b..10ea4f3d 100644 --- a/lambda-events/src/event/config/mod.rs +++ b/lambda-events/src/event/config/mod.rs @@ -38,15 +38,19 @@ pub struct ConfigEvent { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "config")] fn example_config_event() { - let data = include_bytes!("../../fixtures/example-config-event.json"); - let parsed: ConfigEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ConfigEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-config-event.json").to_vec(); + let parsed: ConfigEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ConfigEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/connect/mod.rs b/lambda-events/src/event/connect/mod.rs index 04f26eb5..91e8e3be 100644 --- a/lambda-events/src/event/connect/mod.rs +++ b/lambda-events/src/event/connect/mod.rs @@ -92,25 +92,29 @@ pub type ConnectResponse = HashMap<String, String>; #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "connect")] fn example_connect_event() { - let data = include_bytes!("../../fixtures/example-connect-event.json"); - let parsed: ConnectEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ConnectEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-connect-event.json").to_vec(); + let parsed: ConnectEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ConnectEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "connect")] fn example_connect_event_without_queue() { - let data = include_bytes!("../../fixtures/example-connect-event-without-queue.json"); - let parsed: ConnectEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: ConnectEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-connect-event-without-queue.json").to_vec(); + let parsed: ConnectEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: ConnectEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/documentdb/events/commom_types.rs b/lambda-events/src/event/documentdb/events/commom_types.rs index 5d1bdc19..2488d333 100644 --- a/lambda-events/src/event/documentdb/events/commom_types.rs +++ b/lambda-events/src/event/documentdb/events/commom_types.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; +use aws_lambda_json_impl::Value; use serde::{Deserialize, Serialize}; -use serde_json::Value; pub type AnyDocument = HashMap<String, Value>; diff --git a/lambda-events/src/event/documentdb/mod.rs b/lambda-events/src/event/documentdb/mod.rs index 67f7c9ad..a973f984 100644 --- a/lambda-events/src/event/documentdb/mod.rs +++ b/lambda-events/src/event/documentdb/mod.rs @@ -38,14 +38,19 @@ pub struct DocumentDbEvent { #[cfg(test)] #[cfg(feature = "documentdb")] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; pub type Event = DocumentDbEvent; fn test_example(data: &[u8]) { - let parsed: Event = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: Event = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = data.to_vec(); + let parsed: Event = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: Event = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } diff --git a/lambda-events/src/event/dynamodb/attributes.rs b/lambda-events/src/event/dynamodb/attributes.rs index e1a42c83..6bae4f80 100644 --- a/lambda-events/src/event/dynamodb/attributes.rs +++ b/lambda-events/src/event/dynamodb/attributes.rs @@ -8,59 +8,59 @@ mod test { #[test] fn test_null_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "NULL": true }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::Null(true) => {} other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_string_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "S": "value" }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::S(ref s) => assert_eq!("value", s.as_str()), other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_number_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "N": "123.45" }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::N(ref n) => assert_eq!("123.45", n.as_str()), other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_binary_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "B": "dGhpcyB0ZXh0IGlzIGJhc2U2NC1lbmNvZGVk" }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::B(ref b) => { let expected = base64::engine::general_purpose::STANDARD @@ -71,33 +71,33 @@ mod test { other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_boolean_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "BOOL": true }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::Bool(b) => assert!(b), other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_string_set_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "SS": ["Giraffe", "Hippo" ,"Zebra"] }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::Ss(ref s) => { let expected = vec!["Giraffe", "Hippo", "Zebra"]; @@ -106,17 +106,17 @@ mod test { other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_number_set_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "NS": ["42.2", "-19", "7.5", "3.14"] }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::Ns(ref s) => { let expected = vec!["42.2", "-19", "7.5", "3.14"]; @@ -125,17 +125,17 @@ mod test { other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_binary_set_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "BS": ["U3Vubnk=", "UmFpbnk=", "U25vd3k="] }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::Bs(ref s) => { let expected = vec!["U3Vubnk=", "UmFpbnk=", "U25vd3k="] @@ -147,17 +147,17 @@ mod test { other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_attribute_list_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "L": [ {"S": "Cookies"} , {"S": "Coffee"}, {"N": "3.14159"}] }); - let attr: AttributeValue = serde_json::from_value(value.clone()).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value.clone()).unwrap(); match attr { AttributeValue::L(ref s) => { let expected = vec![ @@ -170,17 +170,17 @@ mod test { other => panic!("unexpected value {:?}", other), } - let reparsed = serde_json::to_value(attr).unwrap(); + let reparsed = aws_lambda_json_impl::to_value(attr).unwrap(); assert_eq!(value, reparsed); } #[test] fn test_attribute_map_attribute() { - let value = serde_json::json!({ + let value = aws_lambda_json_impl::json!({ "M": {"Name": {"S": "Joe"}, "Age": {"N": "35"}} }); - let attr: AttributeValue = serde_json::from_value(value).unwrap(); + let attr: AttributeValue = aws_lambda_json_impl::from_value(value).unwrap(); match attr { AttributeValue::M(s) => { let mut expected = HashMap::new(); diff --git a/lambda-events/src/event/dynamodb/mod.rs b/lambda-events/src/event/dynamodb/mod.rs index 2a3d7558..834ef44a 100644 --- a/lambda-events/src/event/dynamodb/mod.rs +++ b/lambda-events/src/event/dynamodb/mod.rs @@ -253,16 +253,20 @@ pub struct StreamRecord { #[cfg(test)] #[allow(deprecated)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; use chrono::TimeZone; #[test] #[cfg(feature = "dynamodb")] fn example_dynamodb_event() { - let data = include_bytes!("../../fixtures/example-dynamodb-event.json"); - let mut parsed: Event = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: Event = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-dynamodb-event.json").to_vec(); + let mut parsed: Event = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: Event = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); let event = parsed.records.pop().unwrap(); @@ -273,10 +277,11 @@ mod test { #[test] #[cfg(feature = "dynamodb")] fn example_dynamodb_event_with_optional_fields() { - let data = include_bytes!("../../fixtures/example-dynamodb-event-record-with-optional-fields.json"); - let parsed: EventRecord = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: EventRecord = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-dynamodb-event-record-with-optional-fields.json").to_vec(); + let parsed: EventRecord = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: EventRecord = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); let date = Utc.timestamp_micros(0).unwrap(); // 1970-01-01T00:00:00Z assert_eq!(date, reparsed.change.approximate_creation_date_time); diff --git a/lambda-events/src/event/ecr_scan/mod.rs b/lambda-events/src/event/ecr_scan/mod.rs index 5502e81a..0cb02937 100644 --- a/lambda-events/src/event/ecr_scan/mod.rs +++ b/lambda-events/src/event/ecr_scan/mod.rs @@ -65,25 +65,30 @@ pub struct EcrScanEventFindingSeverityCounts { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "ecr_scan")] fn example_ecr_image_scan_event() { - let data = include_bytes!("../../fixtures/example-ecr-image-scan-event.json"); - let parsed: EcrScanEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: EcrScanEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-ecr-image-scan-event.json").to_vec(); + let parsed: EcrScanEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: EcrScanEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "ecr_scan")] fn example_ecr_image_scan_event_with_missing_severities() { - let data = include_bytes!("../../fixtures/example-ecr-image-scan-event-with-missing-severities.json"); - let parsed: EcrScanEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: EcrScanEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-ecr-image-scan-event-with-missing-severities.json").to_vec(); + let parsed: EcrScanEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: EcrScanEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/eventbridge/mod.rs b/lambda-events/src/event/eventbridge/mod.rs index 7756e0e4..3287d3db 100644 --- a/lambda-events/src/event/eventbridge/mod.rs +++ b/lambda-events/src/event/eventbridge/mod.rs @@ -1,6 +1,6 @@ +use aws_lambda_json_impl::Value; use chrono::{DateTime, Utc}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; /// Parse EventBridge events. /// Deserialize the event detail into a structure that's `DeserializeOwned`. @@ -35,6 +35,10 @@ where #[cfg(test)] #[cfg(feature = "eventbridge")] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] @@ -47,27 +51,28 @@ mod test { } // Example from https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/monitoring-instance-state-changes.html - let data = include_bytes!("../../fixtures/example-eventbridge-event-obj.json"); - let parsed: EventBridgeEvent<Ec2StateChange> = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-eventbridge-event-obj.json").to_vec(); + let parsed: EventBridgeEvent<Ec2StateChange> = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!("i-abcd1111", parsed.detail.instance_id); assert_eq!("pending", parsed.detail.state); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: EventBridgeEvent<Ec2StateChange> = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: EventBridgeEvent<Ec2StateChange> = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] fn example_eventbridge_schedule_event() { - let data = include_bytes!("../../fixtures/example-eventbridge-schedule.json"); - let parsed: EventBridgeEvent = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-eventbridge-schedule.json").to_vec(); + let parsed: EventBridgeEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!("aws.events", parsed.source); assert_eq!("Scheduled Event", parsed.detail_type); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: EventBridgeEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: EventBridgeEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/firehose/mod.rs b/lambda-events/src/event/firehose/mod.rs index 6a0a13fd..c557e598 100644 --- a/lambda-events/src/event/firehose/mod.rs +++ b/lambda-events/src/event/firehose/mod.rs @@ -74,15 +74,19 @@ pub struct KinesisFirehoseRecordMetadata { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "firehose")] fn example_firehose_event() { - let data = include_bytes!("../../fixtures/example-firehose-event.json"); - let parsed: KinesisFirehoseEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: KinesisFirehoseEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-firehose-event.json").to_vec(); + let parsed: KinesisFirehoseEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: KinesisFirehoseEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/iam/mod.rs b/lambda-events/src/event/iam/mod.rs index 36f59c7b..76b11606 100644 --- a/lambda-events/src/event/iam/mod.rs +++ b/lambda-events/src/event/iam/mod.rs @@ -129,7 +129,7 @@ mod tests { #[test] fn test_deserialize_string_condition() { - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "condition": { "StringEquals": { "iam:RegisterSecurityKey": "Activate", @@ -144,7 +144,7 @@ mod tests { condition: Option<IamPolicyCondition>, } - let test: Test = serde_json::from_value(data).unwrap(); + let test: Test = aws_lambda_json_impl::from_value(data).unwrap(); let condition = test.condition.unwrap(); assert_eq!(1, condition.len()); @@ -154,7 +154,7 @@ mod tests { #[test] fn test_deserialize_slide_condition() { - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "condition": {"StringLike": {"s3:prefix": ["janedoe/*"]}} }); @@ -164,7 +164,7 @@ mod tests { condition: Option<IamPolicyCondition>, } - let test: Test = serde_json::from_value(data).unwrap(); + let test: Test = aws_lambda_json_impl::from_value(data).unwrap(); let condition = test.condition.unwrap(); assert_eq!(1, condition.len()); @@ -179,11 +179,11 @@ mod tests { resource: vec!["some:resource".into()], condition: None, }; - let policy_ser = serde_json::to_value(policy).unwrap(); + let policy_ser = aws_lambda_json_impl::to_value(policy).unwrap(); assert_eq!( policy_ser, - serde_json::json!({ + aws_lambda_json_impl::json!({ "Action": ["some:action"], "Effect": "Allow", "Resource": ["some:resource"] diff --git a/lambda-events/src/event/iot/mod.rs b/lambda-events/src/event/iot/mod.rs index 3835b515..9c9b41e7 100644 --- a/lambda-events/src/event/iot/mod.rs +++ b/lambda-events/src/event/iot/mod.rs @@ -72,25 +72,30 @@ pub struct IoTCoreCustomAuthorizerResponse { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "iot")] fn example_iot_custom_auth_request() { - let data = include_bytes!("../../fixtures/example-iot-custom-auth-request.json"); - let parsed: IoTCoreCustomAuthorizerRequest = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: IoTCoreCustomAuthorizerRequest = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-iot-custom-auth-request.json").to_vec(); + let parsed: IoTCoreCustomAuthorizerRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: IoTCoreCustomAuthorizerRequest = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "iot")] fn example_iot_custom_auth_response() { - let data = include_bytes!("../../fixtures/example-iot-custom-auth-response.json"); - let parsed: IoTCoreCustomAuthorizerResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: IoTCoreCustomAuthorizerResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-iot-custom-auth-response.json").to_vec(); + let parsed: IoTCoreCustomAuthorizerResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: IoTCoreCustomAuthorizerResponse = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/iot_1_click/mod.rs b/lambda-events/src/event/iot_1_click/mod.rs index bf010b50..e7965316 100644 --- a/lambda-events/src/event/iot_1_click/mod.rs +++ b/lambda-events/src/event/iot_1_click/mod.rs @@ -58,15 +58,19 @@ pub struct IoTOneClickPlacementInfo { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "iot_1_click")] fn example_iot_1_click_event() { - let data = include_bytes!("../../fixtures/example-iot_1_click-event.json"); - let parsed: IoTOneClickEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: IoTOneClickEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-iot_1_click-event.json").to_vec(); + let parsed: IoTOneClickEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: IoTOneClickEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/iot_button/mod.rs b/lambda-events/src/event/iot_button/mod.rs index 2d2e4627..5c80e386 100644 --- a/lambda-events/src/event/iot_button/mod.rs +++ b/lambda-events/src/event/iot_button/mod.rs @@ -13,15 +13,19 @@ pub struct IoTButtonEvent { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "iot_button")] fn example_iot_button_event() { - let data = include_bytes!("../../fixtures/example-iot_button-event.json"); - let parsed: IoTButtonEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: IoTButtonEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-iot_button-event.json").to_vec(); + let parsed: IoTButtonEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: IoTButtonEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/kafka/mod.rs b/lambda-events/src/event/kafka/mod.rs index 27a1e921..b3dc2138 100644 --- a/lambda-events/src/event/kafka/mod.rs +++ b/lambda-events/src/event/kafka/mod.rs @@ -33,15 +33,19 @@ pub struct KafkaRecord { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "kafka")] fn example_kafka_event() { - let data = include_bytes!("../../fixtures/example-kafka-event.json"); - let parsed: KafkaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: KafkaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-kafka-event.json").to_vec(); + let parsed: KafkaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: KafkaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/kinesis/event.rs b/lambda-events/src/event/kinesis/event.rs index fac80e07..080039e8 100644 --- a/lambda-events/src/event/kinesis/event.rs +++ b/lambda-events/src/event/kinesis/event.rs @@ -84,29 +84,33 @@ pub enum KinesisEncryptionType { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "kinesis")] fn example_kinesis_event() { - let data = include_bytes!("../../fixtures/example-kinesis-event.json"); - let parsed: KinesisEvent = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-kinesis-event.json").to_vec(); + let parsed: KinesisEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(KinesisEncryptionType::None, parsed.records[0].kinesis.encryption_type); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: KinesisEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: KinesisEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "kinesis")] fn example_kinesis_event_encrypted() { - let data = include_bytes!("../../fixtures/example-kinesis-event-encrypted.json"); - let parsed: KinesisEvent = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-kinesis-event-encrypted.json").to_vec(); + let parsed: KinesisEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(KinesisEncryptionType::Kms, parsed.records[0].kinesis.encryption_type); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: KinesisEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: KinesisEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/lex/mod.rs b/lambda-events/src/event/lex/mod.rs index d8f9403c..7f5e0901 100644 --- a/lambda-events/src/event/lex/mod.rs +++ b/lambda-events/src/event/lex/mod.rs @@ -106,25 +106,29 @@ pub struct Attachment { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "lex")] fn example_lex_event() { - let data = include_bytes!("../../fixtures/example-lex-event.json"); - let parsed: LexEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: LexEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-lex-event.json").to_vec(); + let parsed: LexEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: LexEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "lex")] fn example_lex_response() { - let data = include_bytes!("../../fixtures/example-lex-response.json"); - let parsed: LexEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: LexEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-lex-response.json").to_vec(); + let parsed: LexEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: LexEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/rabbitmq/mod.rs b/lambda-events/src/event/rabbitmq/mod.rs index 23327276..aea63631 100644 --- a/lambda-events/src/event/rabbitmq/mod.rs +++ b/lambda-events/src/event/rabbitmq/mod.rs @@ -1,5 +1,5 @@ +use aws_lambda_json_impl::Value; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::deserialize_lambda_map; @@ -60,15 +60,19 @@ where #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "rabbitmq")] fn example_rabbitmq_event() { - let data = include_bytes!("../../fixtures/example-rabbitmq-event.json"); - let parsed: RabbitMqEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: RabbitMqEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-rabbitmq-event.json").to_vec(); + let parsed: RabbitMqEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: RabbitMqEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/s3/event.rs b/lambda-events/src/event/s3/event.rs index e062c7d2..1aaff157 100644 --- a/lambda-events/src/event/s3/event.rs +++ b/lambda-events/src/event/s3/event.rs @@ -90,25 +90,29 @@ pub struct S3Object { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "s3")] fn example_s3_event() { - let data = include_bytes!("../../fixtures/example-s3-event.json"); - let parsed: S3Event = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3Event = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-s3-event.json").to_vec(); + let parsed: S3Event = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3Event = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "s3")] fn example_s3_event_with_decoded() { - let data = include_bytes!("../../fixtures/example-s3-event-with-decoded.json"); - let parsed: S3Event = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3Event = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-s3-event-with-decoded.json").to_vec(); + let parsed: S3Event = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3Event = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/s3/object_lambda.rs b/lambda-events/src/event/s3/object_lambda.rs index 2abcc797..04a83c4c 100644 --- a/lambda-events/src/event/s3/object_lambda.rs +++ b/lambda-events/src/event/s3/object_lambda.rs @@ -1,6 +1,6 @@ +use aws_lambda_json_impl::Value; use http::HeaderMap; use serde::{de::DeserializeOwned, Deserialize, Serialize}; -use serde_json::Value; use std::collections::HashMap; use crate::custom_serde::{deserialize_headers, serialize_headers}; @@ -117,55 +117,61 @@ pub struct SessionIssuer { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "s3")] fn example_object_lambda_event_get_object_assumed_role() { - let data = include_bytes!("../../fixtures/example-s3-object-lambda-event-get-object-assumed-role.json"); - let parsed: S3ObjectLambdaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3ObjectLambdaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-s3-object-lambda-event-get-object-assumed-role.json").to_vec(); + let parsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "s3")] fn example_object_lambda_event_get_object_iam() { - let data = include_bytes!("../../fixtures/example-s3-object-lambda-event-get-object-iam.json"); - let parsed: S3ObjectLambdaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3ObjectLambdaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-s3-object-lambda-event-get-object-iam.json").to_vec(); + let parsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "s3")] fn example_object_lambda_event_head_object_iam() { - let data = include_bytes!("../../fixtures/example-s3-object-lambda-event-head-object-iam.json"); - let parsed: S3ObjectLambdaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3ObjectLambdaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-s3-object-lambda-event-head-object-iam.json").to_vec(); + let parsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "s3")] fn example_object_lambda_event_list_objects_iam() { - let data = include_bytes!("../../fixtures/example-s3-object-lambda-event-list-objects-iam.json"); - let parsed: S3ObjectLambdaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3ObjectLambdaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-s3-object-lambda-event-list-objects-iam.json").to_vec(); + let parsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "s3")] fn example_object_lambda_event_list_objects_v2_iam() { - let data = include_bytes!("../../fixtures/example-s3-object-lambda-event-list-objects-v2-iam.json"); - let parsed: S3ObjectLambdaEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: S3ObjectLambdaEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-s3-object-lambda-event-list-objects-v2-iam.json").to_vec(); + let parsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: S3ObjectLambdaEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/secretsmanager/mod.rs b/lambda-events/src/event/secretsmanager/mod.rs index 3ed8d238..9a28abec 100644 --- a/lambda-events/src/event/secretsmanager/mod.rs +++ b/lambda-events/src/event/secretsmanager/mod.rs @@ -10,15 +10,20 @@ pub struct SecretsManagerSecretRotationEvent { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "secretsmanager")] fn example_secretsmanager_secret_rotation_event() { - let data = include_bytes!("../../fixtures/example-secretsmanager-secret-rotation-event.json"); - let parsed: SecretsManagerSecretRotationEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SecretsManagerSecretRotationEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-secretsmanager-secret-rotation-event.json").to_vec(); + let parsed: SecretsManagerSecretRotationEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SecretsManagerSecretRotationEvent = + aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/ses/mod.rs b/lambda-events/src/event/ses/mod.rs index 2a60957a..f98f149d 100644 --- a/lambda-events/src/event/ses/mod.rs +++ b/lambda-events/src/event/ses/mod.rs @@ -120,35 +120,39 @@ pub struct SimpleEmailDisposition { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "ses")] fn example_ses_lambda_event() { - let data = include_bytes!("../../fixtures/example-ses-lambda-event.json"); - let parsed: SimpleEmailEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SimpleEmailEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-ses-lambda-event.json").to_vec(); + let parsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "ses")] fn example_ses_s3_event() { - let data = include_bytes!("../../fixtures/example-ses-s3-event.json"); - let parsed: SimpleEmailEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SimpleEmailEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-ses-s3-event.json").to_vec(); + let parsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "ses")] fn example_ses_sns_event() { - let data = include_bytes!("../../fixtures/example-ses-sns-event.json"); - let parsed: SimpleEmailEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SimpleEmailEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-ses-sns-event.json").to_vec(); + let parsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SimpleEmailEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/sns/mod.rs b/lambda-events/src/event/sns/mod.rs index 0fda569d..36bc2e6b 100644 --- a/lambda-events/src/event/sns/mod.rs +++ b/lambda-events/src/event/sns/mod.rs @@ -248,41 +248,54 @@ pub struct CloudWatchDimension { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "sns")] fn my_example_sns_event() { - let data = include_bytes!("../../fixtures/example-sns-event.json"); - let parsed: SnsEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SnsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sns-event.json").to_vec(); + let parsed: SnsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SnsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "sns")] fn my_example_sns_event_pascal_case() { - let data = include_bytes!("../../fixtures/example-sns-event-pascal-case.json"); - let parsed: SnsEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SnsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sns-event-pascal-case.json").to_vec(); + let parsed: SnsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SnsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "sns")] fn my_example_sns_event_cloudwatch_single_metric() { - let data = include_bytes!("../../fixtures/example-cloudwatch-alarm-sns-payload-single-metric.json"); - let parsed: SnsEvent = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudwatch-alarm-sns-payload-single-metric.json").to_vec(); + + let parsed: SnsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(1, parsed.records.len()); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SnsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SnsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); + // + // Without this, the test fails when using simd_json - precisely because simd_json needs a + // MUTABLE reference and it WILL modify the slice! + // + let mut data = + include_bytes!("../../fixtures/example-cloudwatch-alarm-sns-payload-single-metric.json").to_vec(); + let parsed: SnsEventObj<CloudWatchAlarmPayload> = - serde_json::from_slice(data).expect("failed to parse CloudWatch Alarm payload"); + aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to parse CloudWatch Alarm payload"); let record = parsed.records.first().unwrap(); assert_eq!("EXAMPLE", record.sns.message.alarm_name); @@ -291,19 +304,20 @@ mod test { #[test] #[cfg(feature = "sns")] fn my_example_sns_event_cloudwatch_multiple_metrics() { - let data = include_bytes!("../../fixtures/example-cloudwatch-alarm-sns-payload-multiple-metrics.json"); - let parsed: SnsEvent = serde_json::from_slice(data).unwrap(); + let mut data = + include_bytes!("../../fixtures/example-cloudwatch-alarm-sns-payload-multiple-metrics.json").to_vec(); + let parsed: SnsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(2, parsed.records.len()); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SnsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SnsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } #[test] #[cfg(feature = "sns")] fn my_example_sns_obj_event() { - let data = include_bytes!("../../fixtures/example-sns-event-obj.json"); + let mut data = include_bytes!("../../fixtures/example-sns-event-obj.json").to_vec(); #[derive(Debug, Serialize, Deserialize, Eq, PartialEq)] struct CustStruct { @@ -311,14 +325,14 @@ mod test { bar: i32, } - let parsed: SnsEventObj<CustStruct> = serde_json::from_slice(data).unwrap(); + let parsed: SnsEventObj<CustStruct> = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); println!("{:?}", parsed); assert_eq!(parsed.records[0].sns.message.foo, "Hello world!"); assert_eq!(parsed.records[0].sns.message.bar, 123); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SnsEventObj<CustStruct> = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SnsEventObj<CustStruct> = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/event/sqs/mod.rs b/lambda-events/src/event/sqs/mod.rs index 9dd69f66..90929bc5 100644 --- a/lambda-events/src/event/sqs/mod.rs +++ b/lambda-events/src/event/sqs/mod.rs @@ -180,15 +180,19 @@ pub struct SqsApiMessage { #[cfg(test)] mod test { + // To save on boiler plate, JSON data is parsed from a mut byte slice rather than an &str. The slice isn't actually mutated + // when using serde-json, but it IS when using simd-json - so we also take care not to reuse the slice + // once it has been deserialized. + use super::*; #[test] #[cfg(feature = "sqs")] fn example_sqs_event() { - let data = include_bytes!("../../fixtures/example-sqs-event.json"); - let parsed: SqsEvent = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SqsEvent = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sqs-event.json").to_vec(); + let parsed: SqsEvent = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SqsEvent = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } @@ -201,14 +205,14 @@ mod test { b: u32, } - let data = include_bytes!("../../fixtures/example-sqs-event-obj.json"); - let parsed: SqsEventObj<CustStruct> = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sqs-event-obj.json").to_vec(); + let parsed: SqsEventObj<CustStruct> = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(parsed.records[0].body.a, "Test"); assert_eq!(parsed.records[0].body.b, 123); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SqsEventObj<CustStruct> = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SqsEventObj<CustStruct> = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } @@ -217,10 +221,10 @@ mod test { fn example_sqs_batch_response() { // Example sqs batch response fetched 2022-05-13, from: // https://docs.aws.amazon.com/lambda/latest/dg/with-sqs.html#services-sqs-batchfailurereporting - let data = include_bytes!("../../fixtures/example-sqs-batch-response.json"); - let parsed: SqsBatchResponse = serde_json::from_slice(data).unwrap(); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SqsBatchResponse = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sqs-batch-response.json").to_vec(); + let parsed: SqsBatchResponse = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SqsBatchResponse = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } @@ -235,14 +239,14 @@ mod test { country: String, } - let data = include_bytes!("../../fixtures/example-sqs-api-event-obj.json"); - let parsed: SqsApiEventObj<CustStruct> = serde_json::from_slice(data).unwrap(); + let mut data = include_bytes!("../../fixtures/example-sqs-api-event-obj.json").to_vec(); + let parsed: SqsApiEventObj<CustStruct> = aws_lambda_json_impl::from_slice(data.as_mut_slice()).unwrap(); assert_eq!(parsed.messages[0].body.city, "provincetown"); assert_eq!(parsed.messages[0].body.country, "usa"); - let output: String = serde_json::to_string(&parsed).unwrap(); - let reparsed: SqsApiEventObj<CustStruct> = serde_json::from_slice(output.as_bytes()).unwrap(); + let mut output = aws_lambda_json_impl::to_string(&parsed).unwrap().into_bytes(); + let reparsed: SqsApiEventObj<CustStruct> = aws_lambda_json_impl::from_slice(output.as_mut_slice()).unwrap(); assert_eq!(parsed, reparsed); } } diff --git a/lambda-events/src/time_window.rs b/lambda-events/src/time_window.rs index edc9beb5..f6e1a220 100644 --- a/lambda-events/src/time_window.rs +++ b/lambda-events/src/time_window.rs @@ -68,12 +68,12 @@ mod test { #[test] fn test_window_deserializer() { - let v = serde_json::json!({ + let v = aws_lambda_json_impl::json!({ "start": "2020-12-09T07:04:00Z", "end": "2020-12-09T07:06:00Z", }); - let parsed: Window = serde_json::from_value(v).unwrap(); + let parsed: Window = aws_lambda_json_impl::from_value(v).unwrap(); assert_eq!("2020-12-09T07:04:00+00:00", &parsed.start.to_rfc3339()); assert_eq!("2020-12-09T07:06:00+00:00", &parsed.end.to_rfc3339()); } diff --git a/lambda-extension/Cargo.toml b/lambda-extension/Cargo.toml index 16b6dace..09d7c878 100644 --- a/lambda-extension/Cargo.toml +++ b/lambda-extension/Cargo.toml @@ -16,6 +16,7 @@ readme = "README.md" [features] default = ["tracing"] tracing = ["lambda_runtime_api_client/tracing"] +simd_json = [ "aws_lambda_json_impl/simd" ] [dependencies] async-stream = "0.3" @@ -27,7 +28,7 @@ hyper = { workspace = true, features = ["http1", "client", "server"] } hyper-util = { workspace = true } lambda_runtime_api_client = { version = "0.11", path = "../lambda-runtime-api-client" } serde = { version = "1", features = ["derive"] } -serde_json = "^1" +aws_lambda_json_impl = { path = "../json-impl" } tokio = { version = "1.0", features = [ "macros", "io-util", diff --git a/lambda-extension/src/extension.rs b/lambda-extension/src/extension.rs index d9717243..9170658c 100644 --- a/lambda-extension/src/extension.rs +++ b/lambda-extension/src/extension.rs @@ -396,7 +396,7 @@ where let body = body.collect().await?.to_bytes(); trace!("{}", std::str::from_utf8(&body)?); // this may be very verbose - let event: NextEvent = serde_json::from_slice(&body)?; + let event: NextEvent = aws_lambda_json_impl::from_bytes(body)?; let is_invoke = event.is_invoke(); let event = LambdaEvent::new(event); @@ -541,7 +541,7 @@ async fn register<'a>( let (_, body) = res.into_parts(); let body = body.collect().await?.to_bytes(); - let response: RegisterResponseBody = serde_json::from_slice(&body)?; + let response: RegisterResponseBody = aws_lambda_json_impl::from_bytes(body)?; Ok(RegisterResponse { extension_id, diff --git a/lambda-extension/src/logs.rs b/lambda-extension/src/logs.rs index c3b0cda2..3c1c5025 100644 --- a/lambda-extension/src/logs.rs +++ b/lambda-extension/src/logs.rs @@ -208,7 +208,7 @@ where .unwrap()); } }; - let logs: Vec<LambdaLog> = match serde_json::from_slice(&body.to_bytes()) { + let logs: Vec<LambdaLog> = match aws_lambda_json_impl::from_bytes(body.to_bytes()) { Ok(logs) => logs, Err(e) => { error!("Error parsing logs: {}", e); @@ -237,7 +237,10 @@ mod tests { #[test] fn deserialize_full() { - let data = r#"{"time": "2020-08-20T12:31:32.123Z","type": "function", "record": "hello world"}"#; + // To avoid lots of complex boilerplate, we use a mutable bytes slice in the parse + // with serde_json the slice is not actually mutated + // with simd_json it is + let mut data = r#"{"time": "2020-08-20T12:31:32.123Z","type": "function", "record": "hello world"}"#.as_bytes().to_vec(); let expected = LambdaLog { time: Utc .with_ymd_and_hms(2020, 8, 20, 12, 31, 32) @@ -247,7 +250,7 @@ mod tests { record: LambdaLogRecord::Function("hello world".to_string()), }; - let actual = serde_json::from_str::<LambdaLog>(data).unwrap(); + let actual = aws_lambda_json_impl::from_slice::<LambdaLog>(data.as_mut_slice()).unwrap(); assert_eq!(expected, actual); } @@ -258,7 +261,11 @@ mod tests { #[test] fn $name() { let (input, expected) = $value; - let actual = serde_json::from_str::<LambdaLog>(&input).expect("unable to deserialize"); + // To avoid lots of complex boilerplate, we use a mutable bytes slice in the parse + // with serde_json the slice is not actually mutated + // with simd_json it is + let mut input = input.as_bytes().to_vec(); + let actual = aws_lambda_json_impl::from_slice::<LambdaLog>(input.as_mut_slice()).expect("unable to deserialize"); assert!(actual.record == expected); } diff --git a/lambda-extension/src/requests.rs b/lambda-extension/src/requests.rs index 522b8402..42d48d4f 100644 --- a/lambda-extension/src/requests.rs +++ b/lambda-extension/src/requests.rs @@ -24,7 +24,7 @@ pub(crate) fn next_event_request(extension_id: &str) -> Result<Request<Body>, Er } pub(crate) fn register_request(extension_name: &str, events: &[&str]) -> Result<Request<Body>, Error> { - let events = serde_json::json!({ "events": events }); + let events = aws_lambda_json_impl::json!({ "events": events }); let req = build_request() .method(Method::POST) @@ -32,7 +32,7 @@ pub(crate) fn register_request(extension_name: &str, events: &[&str]) -> Result< .header(EXTENSION_NAME_HEADER, extension_name) .header(EXTENSION_ACCEPT_FEATURE, EXTENSION_ACCEPT_FEATURE_VALUE) .header(CONTENT_TYPE_HEADER_NAME, CONTENT_TYPE_HEADER_VALUE) - .body(Body::from(serde_json::to_string(&events)?))?; + .body(Body::from(aws_lambda_json_impl::to_string(&events)?))?; Ok(req) } @@ -67,7 +67,7 @@ pub(crate) fn subscribe_request( ) -> Result<Request<Body>, Error> { let types = types.unwrap_or(&["platform", "function"]); - let data = serde_json::json!({ + let data = aws_lambda_json_impl::json!({ "schemaVersion": api.schema_version(), "types": types, "buffering": buffering.unwrap_or_default(), @@ -82,7 +82,7 @@ pub(crate) fn subscribe_request( .uri(api.uri()) .header(EXTENSION_ID_HEADER, extension_id) .header(CONTENT_TYPE_HEADER_NAME, CONTENT_TYPE_HEADER_VALUE) - .body(Body::from(serde_json::to_string(&data)?))?; + .body(Body::from(aws_lambda_json_impl::to_string(&data)?))?; Ok(req) } @@ -127,7 +127,7 @@ fn error_request( let body = match request { None => Body::empty(), - Some(err) => Body::from(serde_json::to_string(&err)?), + Some(err) => Body::from(aws_lambda_json_impl::to_string(&err)?), }; let req = build_request() diff --git a/lambda-extension/src/telemetry.rs b/lambda-extension/src/telemetry.rs index a7760892..e84c4b0b 100644 --- a/lambda-extension/src/telemetry.rs +++ b/lambda-extension/src/telemetry.rs @@ -291,7 +291,7 @@ where } }; - let telemetry: Vec<LambdaTelemetry> = match serde_json::from_slice(&body.to_bytes()) { + let telemetry: Vec<LambdaTelemetry> = match aws_lambda_json_impl::from_bytes(body.to_bytes()) { Ok(telemetry) => telemetry, Err(e) => { error!("Error parsing telemetry: {}", e); @@ -324,7 +324,11 @@ mod deserialization_tests { #[test] fn $name() { let (input, expected) = $value; - let actual = serde_json::from_str::<LambdaTelemetry>(&input).expect("unable to deserialize"); + // To avoid lots of complex boilerplate, we use a mutable bytes slice in the parse + // with serde_json the slice is not actually mutated + // with simd_json it is + let mut input = input.as_bytes().to_vec(); + let actual = aws_lambda_json_impl::from_slice::<LambdaTelemetry>(input.as_mut_slice()).expect("unable to deserialize"); assert!(actual.record == expected); } @@ -482,7 +486,7 @@ mod serialization_tests { #[test] fn $name() { let (input, expected) = $value; - let actual = serde_json::to_string(&input).expect("unable to serialize"); + let actual = aws_lambda_json_impl::to_string(&input).expect("unable to serialize"); println!("Input: {:?}\n", input); println!("Expected:\n {:?}\n", expected); println!("Actual:\n {:?}\n", actual); diff --git a/lambda-http/Cargo.toml b/lambda-http/Cargo.toml index 08b70d4e..6a2d48a0 100644 --- a/lambda-http/Cargo.toml +++ b/lambda-http/Cargo.toml @@ -27,6 +27,7 @@ opentelemetry = ["lambda_runtime/opentelemetry"] # enables access to the OpenTel anyhow = ["lambda_runtime/anyhow"] # enables From<T> for Diagnostic for anyhow error types, see README.md for more info eyre = ["lambda_runtime/eyre"] # enables From<T> for Diagnostic for eyre error types, see README.md for more info miette = ["lambda_runtime/miette"] # enables From<T> for Diagnostic for miette error types, see README.md for more info +simd_json = [ "aws_lambda_json_impl/simd", "aws_lambda_events/simd_json", "lambda_runtime/simd_json" ] [dependencies] base64 = { workspace = true } @@ -43,10 +44,11 @@ mime = "0.3" percent-encoding = "2.2" pin-project-lite = { workspace = true } serde = { version = "1.0", features = ["derive"] } -serde_json = { version = "1.0", features = ["raw_value"] } +aws_lambda_json_impl = { path = "../json-impl" } serde_urlencoded = "0.7" tokio-stream = "0.1.2" url = "2.2" +tracing = "0" [dependencies.aws_lambda_events] path = "../lambda-events" diff --git a/lambda-http/README.md b/lambda-http/README.md index 6b394964..3fbd8a5a 100644 --- a/lambda-http/README.md +++ b/lambda-http/README.md @@ -45,7 +45,7 @@ The code below creates a simple API Gateway proxy (HTTP, REST) that accepts in i ```rust use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, IntoResponse, Request, RequestPayloadExt}; use serde::{Deserialize, Serialize}; -use serde_json::json; +use aws_lambda_json_impl::json; #[tokio::main] async fn main() -> Result<(), Error> { @@ -83,7 +83,7 @@ pub struct MyPayload { ```rust use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, RequestExt, IntoResponse, Request}; -use serde_json::json; +use aws_lambda_json_impl::json; #[tokio::main] async fn main() -> Result<(), Error> { @@ -124,7 +124,7 @@ use aws_lambda_events::apigw::{ ApiGatewayCustomAuthorizerRequestTypeRequest, ApiGatewayCustomAuthorizerResponse, ApiGatewayCustomAuthorizerPolicy, IamPolicyStatement, }; use lambda_runtime::{run, service_fn, Error, LambdaEvent}; -use serde_json::json; +use aws_lambda_json_impl::json; #[tokio::main] async fn main() -> Result<(), Error> { @@ -183,7 +183,7 @@ One of the [best practices](https://docs.aws.amazon.com/lambda/latest/dg/best-pr use aws_sdk_dynamodb::model::AttributeValue; use chrono::Utc; use lambda_http::{run, http::{StatusCode, Response}, service_fn, Error, RequestExt, IntoResponse, Request}; -use serde_json::json; +use aws_lambda_json_impl::json; #[tokio::main] async fn main() -> Result<(), Error> { diff --git a/lambda-http/src/deserializer.rs b/lambda-http/src/deserializer.rs index 4c0ad519..64f0cd0a 100644 --- a/lambda-http/src/deserializer.rs +++ b/lambda-http/src/deserializer.rs @@ -1,167 +1,11 @@ -use crate::request::LambdaRequest; -#[cfg(feature = "alb")] -use aws_lambda_events::alb::AlbTargetGroupRequest; -#[cfg(feature = "apigw_rest")] -use aws_lambda_events::apigw::ApiGatewayProxyRequest; -#[cfg(feature = "apigw_http")] -use aws_lambda_events::apigw::ApiGatewayV2httpRequest; -#[cfg(feature = "apigw_websockets")] -use aws_lambda_events::apigw::ApiGatewayWebsocketProxyRequest; -use serde::{de::Error, Deserialize}; -use serde_json::value::RawValue; - -const ERROR_CONTEXT: &str = "this function expects a JSON payload from Amazon API Gateway, Amazon Elastic Load Balancer, or AWS Lambda Function URLs, but the data doesn't match any of those services' events"; - -#[cfg(feature = "pass_through")] -const PASS_THROUGH_ENABLED: bool = true; - -impl<'de> Deserialize<'de> for LambdaRequest { - fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> - where - D: serde::Deserializer<'de>, - { - let raw_value: Box<RawValue> = Box::deserialize(deserializer)?; - let data = raw_value.get(); - - #[cfg(feature = "apigw_rest")] - if let Ok(res) = serde_json::from_str::<ApiGatewayProxyRequest>(data) { - return Ok(LambdaRequest::ApiGatewayV1(res)); - } - #[cfg(feature = "apigw_http")] - if let Ok(res) = serde_json::from_str::<ApiGatewayV2httpRequest>(data) { - return Ok(LambdaRequest::ApiGatewayV2(res)); - } - #[cfg(feature = "alb")] - if let Ok(res) = serde_json::from_str::<AlbTargetGroupRequest>(data) { - return Ok(LambdaRequest::Alb(res)); - } - #[cfg(feature = "apigw_websockets")] - if let Ok(res) = serde_json::from_str::<ApiGatewayWebsocketProxyRequest>(data) { - return Ok(LambdaRequest::WebSocket(res)); - } - #[cfg(feature = "pass_through")] - if PASS_THROUGH_ENABLED { - return Ok(LambdaRequest::PassThrough(data.to_string())); - } - - Err(Error::custom(ERROR_CONTEXT)) - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_deserialize_apigw_rest() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-apigw-request.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize apigw rest data"); - match req { - LambdaRequest::ApiGatewayV1(req) => { - assert_eq!("12345678912", req.request_context.account_id.unwrap()); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - fn test_deserialize_apigw_http() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-apigw-v2-request-iam.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize apigw http data"); - match req { - LambdaRequest::ApiGatewayV2(req) => { - assert_eq!("123456789012", req.request_context.account_id.unwrap()); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - fn test_deserialize_sam_rest() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-apigw-sam-rest-request.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize SAM rest data"); - match req { - LambdaRequest::ApiGatewayV1(req) => { - assert_eq!("123456789012", req.request_context.account_id.unwrap()); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - fn test_deserialize_sam_http() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-apigw-sam-http-request.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize SAM http data"); - match req { - LambdaRequest::ApiGatewayV2(req) => { - assert_eq!("123456789012", req.request_context.account_id.unwrap()); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - fn test_deserialize_alb() { - let data = include_bytes!( - "../../lambda-events/src/fixtures/example-alb-lambda-target-request-multivalue-headers.json" - ); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize alb rest data"); - match req { - LambdaRequest::Alb(req) => { - assert_eq!( - "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/abcdefgh", - req.request_context.elb.target_group_arn.unwrap() - ); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - fn test_deserialize_apigw_websocket() { - let data = - include_bytes!("../../lambda-events/src/fixtures/example-apigw-websocket-request-without-method.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize apigw websocket data"); - match req { - LambdaRequest::WebSocket(req) => { - assert_eq!("CONNECT", req.request_context.event_type.unwrap()); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - #[cfg(feature = "pass_through")] - fn test_deserialize_bedrock_agent() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-bedrock-agent-runtime-event.json"); - - let req: LambdaRequest = - serde_json::from_slice(data).expect("failed to deserialize bedrock agent request data"); - match req { - LambdaRequest::PassThrough(req) => { - assert_eq!(String::from_utf8_lossy(data), req); - } - other => panic!("unexpected request variant: {:?}", other), - } - } - - #[test] - #[cfg(feature = "pass_through")] - fn test_deserialize_sqs() { - let data = include_bytes!("../../lambda-events/src/fixtures/example-sqs-event.json"); - - let req: LambdaRequest = serde_json::from_slice(data).expect("failed to deserialize sqs event data"); - match req { - LambdaRequest::PassThrough(req) => { - assert_eq!(String::from_utf8_lossy(data), req); - } - other => panic!("unexpected request variant: {:?}", other), - } - } -} +#[cfg(feature = "simd_json")] +mod deserializer_simd_json; +#[cfg(feature = "simd_json")] +#[allow(unused_imports)] +pub use deserializer_simd_json::*; + +#[cfg(not(feature = "simd_json"))] +mod deserializer_serde_json; +#[cfg(not(feature = "simd_json"))] +#[allow(unused_imports)] +pub use deserializer_serde_json::*; \ No newline at end of file diff --git a/lambda-http/src/deserializer/deserializer_serde_json.rs b/lambda-http/src/deserializer/deserializer_serde_json.rs new file mode 100644 index 00000000..158e2344 --- /dev/null +++ b/lambda-http/src/deserializer/deserializer_serde_json.rs @@ -0,0 +1,167 @@ +use crate::request::LambdaRequest; +#[cfg(feature = "alb")] +use aws_lambda_events::alb::AlbTargetGroupRequest; +#[cfg(feature = "apigw_rest")] +use aws_lambda_events::apigw::ApiGatewayProxyRequest; +#[cfg(feature = "apigw_http")] +use aws_lambda_events::apigw::ApiGatewayV2httpRequest; +#[cfg(feature = "apigw_websockets")] +use aws_lambda_events::apigw::ApiGatewayWebsocketProxyRequest; +use serde::{de::Error, Deserialize}; +use aws_lambda_json_impl::RawValue; + +const ERROR_CONTEXT: &str = "this function expects a JSON payload from Amazon API Gateway, Amazon Elastic Load Balancer, or AWS Lambda Function URLs, but the data doesn't match any of those services' events"; + +#[cfg(feature = "pass_through")] +const PASS_THROUGH_ENABLED: bool = true; + +impl<'de> Deserialize<'de> for LambdaRequest { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de>, + { + let raw_value: Box<RawValue> = Box::deserialize(deserializer)?; + let data = raw_value.get(); + + #[cfg(feature = "apigw_rest")] + if let Ok(res) = aws_lambda_json_impl::from_str::<ApiGatewayProxyRequest>(data) { + return Ok(LambdaRequest::ApiGatewayV1(res)); + } + #[cfg(feature = "apigw_http")] + if let Ok(res) = aws_lambda_json_impl::from_str::<ApiGatewayV2httpRequest>(data) { + return Ok(LambdaRequest::ApiGatewayV2(res)); + } + #[cfg(feature = "alb")] + if let Ok(res) = aws_lambda_json_impl::from_str::<AlbTargetGroupRequest>(data) { + return Ok(LambdaRequest::Alb(res)); + } + #[cfg(feature = "apigw_websockets")] + if let Ok(res) = aws_lambda_json_impl::from_str::<ApiGatewayWebsocketProxyRequest>(data) { + return Ok(LambdaRequest::WebSocket(res)); + } + #[cfg(feature = "pass_through")] + if PASS_THROUGH_ENABLED { + return Ok(LambdaRequest::PassThrough(data.to_string())); + } + + Err(Error::custom(ERROR_CONTEXT)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deserialize_apigw_rest() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-request.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize apigw rest data"); + match req { + LambdaRequest::ApiGatewayV1(req) => { + assert_eq!("12345678912", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_apigw_http() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-v2-request-iam.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize apigw http data"); + match req { + LambdaRequest::ApiGatewayV2(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_sam_rest() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-sam-rest-request.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize SAM rest data"); + match req { + LambdaRequest::ApiGatewayV1(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_sam_http() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-sam-http-request.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize SAM http data"); + match req { + LambdaRequest::ApiGatewayV2(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_alb() { + let data = include_bytes!( + "../../../lambda-events/src/fixtures/example-alb-lambda-target-request-multivalue-headers.json" + ); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize alb rest data"); + match req { + LambdaRequest::Alb(req) => { + assert_eq!( + "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/abcdefgh", + req.request_context.elb.target_group_arn.unwrap() + ); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_apigw_websocket() { + let data = + include_bytes!("../../../lambda-events/src/fixtures/example-apigw-websocket-request-without-method.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize apigw websocket data"); + match req { + LambdaRequest::WebSocket(req) => { + assert_eq!("CONNECT", req.request_context.event_type.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + #[cfg(feature = "pass_through")] + fn test_deserialize_bedrock_agent() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-bedrock-agent-runtime-event.json"); + + let req: LambdaRequest = + aws_lambda_json_impl::from_slice(data).expect("failed to deserialize bedrock agent request data"); + match req { + LambdaRequest::PassThrough(req) => { + assert_eq!(String::from_utf8_lossy(data), req); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + #[cfg(feature = "pass_through")] + fn test_deserialize_sqs() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-sqs-event.json"); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data).expect("failed to deserialize sqs event data"); + match req { + LambdaRequest::PassThrough(req) => { + assert_eq!(String::from_utf8_lossy(data), req); + } + other => panic!("unexpected request variant: {:?}", other), + } + } +} diff --git a/lambda-http/src/deserializer/deserializer_simd_json.rs b/lambda-http/src/deserializer/deserializer_simd_json.rs new file mode 100644 index 00000000..e15deab7 --- /dev/null +++ b/lambda-http/src/deserializer/deserializer_simd_json.rs @@ -0,0 +1,219 @@ +use crate::request::LambdaRequest; +#[cfg(feature = "alb")] +use aws_lambda_events::alb::AlbTargetGroupRequest; +#[cfg(feature = "apigw_rest")] +use aws_lambda_events::apigw::ApiGatewayProxyRequest; +#[cfg(feature = "apigw_http")] +use aws_lambda_events::apigw::ApiGatewayV2httpRequest; +#[cfg(feature = "apigw_websockets")] +use aws_lambda_events::apigw::ApiGatewayWebsocketProxyRequest; +use aws_lambda_json_impl::{JsonDeserializer,Writable}; +use serde::{de::Error, Deserialize}; +use tracing::debug; + +const ERROR_CONTEXT: &str = "this function expects a JSON payload from Amazon API Gateway, Amazon Elastic Load Balancer, or AWS Lambda Function URLs, but the data doesn't match any of those services' events"; + +#[cfg(feature = "pass_through")] +const PASS_THROUGH_ENABLED: bool = true; + + +// simd_json and LambdaRequest don't sit well together due to how the request variant is discovered +// +// To get there, we have to get a bit hacky - we panic if the deserializer we have been offered is not +// a simd_json::Deserializer (please someone show me a better way if there is one!) because we need the +// the Tape from it - THEN we can do some peeking to see what's there and try to deduce the type we are +// looking for. +// +impl<'de> Deserialize<'de> for LambdaRequest { + fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> + where + D: serde::Deserializer<'de> + { + + // We can't do this because D is not enforced to be 'static + //if TypeId::of::<D>() != TypeId::of::<JsonDeserializer<'_>>() {panic!("Deserializer must be simd_json::Deserializer")}; + + // + // There is a future feature of simd_json being considered that is very similar to serde_json's RawValue - effectively a + // Deserializer-implementation-specific way of introspecting/peeking at the parsed JSON before continuing with deserialization. + // That's what the serde_json implementation of this uses, but, for now we have to resort to this wildly unsafe cast of the + // the incoming D to &mut simd_json::Deserializer. + // + // HOWEVER... IF we are here, then someone has built this code with the simd_json feature enabled - which means that all KNOWN + // invokers have ALSO been built with simd_json enabled - which means that this is, in fact, "safe"... IF users ONLY exploit the + // standard invokers (i.e. the Lambda Runtime or the standard entry-point functions in aws_lambda_json_impl). + // + // If not ... well, expect bad things to happen! + // + let d = unsafe { &*(&deserializer as *const _ as *mut &mut JsonDeserializer<'de>) }; + + // Get a simd_json "raw" Value representing what is in the deserializer; + let v = d.as_value(); + + // Introspect for the type markers on V2 payloads + let (rc_present, http_context, alb_context, websocket_context) = if let Some(rc) = v.get("requestContext") { + (true, rc.contains_key("http"), rc.contains_key("elb"), rc.contains_key("connectedAt")) + } else { + (false, false, false, false) + }; + + #[cfg(feature = "apigw_rest")] + // If it's not a V2 payload, then we try to deserialize a V1 payload + if rc_present && !(http_context || alb_context || websocket_context) { + debug!("Parsing REST API request"); + return ApiGatewayProxyRequest::deserialize(deserializer).map_err(Error::custom).map(LambdaRequest::ApiGatewayV1); + } + + #[cfg(feature = "apigw_http")] + if http_context { + debug!("Parsing HTTP API request"); + return ApiGatewayV2httpRequest::deserialize(deserializer).map_err(Error::custom).map(LambdaRequest::ApiGatewayV2); + } + + #[cfg(feature = "alb")] + if alb_context { + debug!("Parsing ALB request"); + return AlbTargetGroupRequest::deserialize(deserializer).map_err(Error::custom).map(LambdaRequest::Alb); + } + + #[cfg(feature = "apigw_websockets")] + if websocket_context { + debug!("Parsing WebSocket request"); + return ApiGatewayWebsocketProxyRequest::deserialize(deserializer).map_err(Error::custom).map(LambdaRequest::WebSocket); + } + + #[cfg(feature = "pass_through")] + if PASS_THROUGH_ENABLED { + let mut buf = Vec::new(); + v.write(&mut buf).map_err(D::Error::custom)?; + let s = String::from_utf8_lossy(&buf); + debug!("Defaulting to pass_through"); + return Ok(LambdaRequest::PassThrough(s.into_owned())); + } + + debug!("Failed to find a candidate request type"); + Err(Error::custom(ERROR_CONTEXT)) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_deserialize_apigw_rest() { + let mut data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-request.json").to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize apigw rest data"); + match req { + LambdaRequest::ApiGatewayV1(req) => { + assert_eq!("12345678912", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_apigw_http() { + let mut data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-v2-request-iam.json").to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize apigw http data"); + match req { + LambdaRequest::ApiGatewayV2(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_sam_rest() { + let mut data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-sam-rest-request.json").to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize SAM rest data"); + match req { + LambdaRequest::ApiGatewayV1(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_sam_http() { + let mut data = include_bytes!("../../../lambda-events/src/fixtures/example-apigw-sam-http-request.json").to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize SAM http data"); + match req { + LambdaRequest::ApiGatewayV2(req) => { + assert_eq!("123456789012", req.request_context.account_id.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_alb() { + let mut data = include_bytes!( + "../../../lambda-events/src/fixtures/example-alb-lambda-target-request-multivalue-headers.json" + ).to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize alb rest data"); + match req { + LambdaRequest::Alb(req) => { + assert_eq!( + "arn:aws:elasticloadbalancing:us-east-1:123456789012:targetgroup/lambda-target/abcdefgh", + req.request_context.elb.target_group_arn.unwrap() + ); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + fn test_deserialize_apigw_websocket() { + let mut data = + include_bytes!("../../../lambda-events/src/fixtures/example-apigw-websocket-request-without-method.json").to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data.as_mut_slice()).expect("failed to deserialize apigw websocket data"); + match req { + LambdaRequest::WebSocket(req) => { + assert_eq!("CONNECT", req.request_context.event_type.unwrap()); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + #[cfg(feature = "pass_through")] + fn test_deserialize_bedrock_agent() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-bedrock-agent-runtime-event.json"); + let mut data_vec = data.to_vec(); + + let req: LambdaRequest = + aws_lambda_json_impl::from_slice(data_vec.as_mut_slice()).expect("failed to deserialize bedrock agent request data"); + match req { + LambdaRequest::PassThrough(req) => { + assert_eq!(String::from_utf8_lossy(data).replace(|c: char| c.is_ascii_whitespace(), ""), req); + } + other => panic!("unexpected request variant: {:?}", other), + } + } + + #[test] + #[cfg(feature = "pass_through")] + fn test_deserialize_sqs() { + let data = include_bytes!("../../../lambda-events/src/fixtures/example-sqs-event.json"); + let mut data_vec = data.to_vec(); + + let req: LambdaRequest = aws_lambda_json_impl::from_slice(data_vec.as_mut_slice()).expect("failed to deserialize sqs event data"); + match req { + LambdaRequest::PassThrough(req) => { + //We have to hack a bit + + assert_eq!(String::from_utf8_lossy(data).replace(|c: char| c.is_ascii_whitespace(), ""), req.replace("Message Body","MessageBody")); + } + other => panic!("unexpected request variant: {:?}", other), + } + } +} diff --git a/lambda-http/src/ext/request.rs b/lambda-http/src/ext/request.rs index c56518f6..ac27caba 100644 --- a/lambda-http/src/ext/request.rs +++ b/lambda-http/src/ext/request.rs @@ -15,7 +15,7 @@ use crate::Body; #[derive(Debug)] pub enum PayloadError { /// Returned when `application/json` bodies fail to deserialize a payload - Json(serde_json::Error), + Json(aws_lambda_json_impl::JsonError), /// Returned when `application/x-www-form-urlencoded` bodies fail to deserialize a payload WwwFormUrlEncoded(SerdeError), } @@ -24,7 +24,7 @@ pub enum PayloadError { #[derive(Debug)] pub enum JsonPayloadError { /// Problem deserializing a JSON payload. - Parsing(serde_json::Error), + Parsing(aws_lambda_json_impl::JsonError), } /// Indicates a problem processing an x-www-form-urlencoded payload. @@ -244,6 +244,7 @@ impl RequestPayloadExt for http::Request<Body> { .unwrap_or_else(|| Ok(None)) } + #[cfg(not(feature = "simd_json"))] fn json<D>(&self) -> Result<Option<D>, JsonPayloadError> where D: DeserializeOwned, @@ -251,7 +252,21 @@ impl RequestPayloadExt for http::Request<Body> { if self.body().is_empty() { return Ok(None); } - serde_json::from_slice::<D>(self.body().as_ref()) + aws_lambda_json_impl::from_slice::<D>(self.body().as_ref()) + .map(Some) + .map_err(JsonPayloadError::Parsing) + } + + #[cfg(feature = "simd_json")] + fn json<D>(&self) -> Result<Option<D>, JsonPayloadError> + where + D: DeserializeOwned, + { + if self.body().is_empty() { + return Ok(None); + } + let mut body = self.body().to_vec(); + aws_lambda_json_impl::from_slice::<D>(body.as_mut_slice()) .map(Some) .map_err(JsonPayloadError::Parsing) } @@ -512,7 +527,7 @@ mod tests { assert!(payload.is_err()); if let Err(JsonPayloadError::Parsing(err)) = payload { - assert!(err.is_syntax()) + assert!(err.is_syntax(), "Error should be syntax: {:?}", err) } else { panic!( "{}", @@ -529,7 +544,7 @@ mod tests { let result = request.json::<Payload>(); if let Err(JsonPayloadError::Parsing(err)) = result { - assert!(err.is_data()) + assert!(err.is_data(), "Error should be data: {:?}", err) } else { panic!( "{}", diff --git a/lambda-http/src/request.rs b/lambda-http/src/request.rs index a9281b46..58ca664d 100644 --- a/lambda-http/src/request.rs +++ b/lambda-http/src/request.rs @@ -27,12 +27,10 @@ use aws_lambda_events::apigw::{ApiGatewayV2httpRequest, ApiGatewayV2httpRequestC use aws_lambda_events::apigw::{ApiGatewayWebsocketProxyRequest, ApiGatewayWebsocketProxyRequestContext}; use aws_lambda_events::{encodings::Body, query_map::QueryMap}; use http::{header::HeaderName, HeaderMap, HeaderValue}; - use serde::{Deserialize, Serialize}; -use serde_json::error::Error as JsonError; - use std::{env, future::Future, io::Read, pin::Pin}; use url::Url; +use aws_lambda_json_impl::JsonError; /// Internal representation of an Lambda http event from /// ALB, API Gateway REST and HTTP API proxy event perspectives @@ -463,7 +461,7 @@ pub fn from_reader<R>(rdr: R) -> Result<crate::Request, JsonError> where R: Read, { - serde_json::from_reader(rdr).map(LambdaRequest::into) + aws_lambda_json_impl::from_reader(rdr).map(LambdaRequest::into) } /// Deserializes a `Request` from a string of JSON text. @@ -482,8 +480,55 @@ where /// Ok(println!("{:#?}", request)) /// } /// ``` +#[cfg(not(feature = "simd_json"))] pub fn from_str(s: &str) -> Result<crate::Request, JsonError> { - serde_json::from_str(s).map(LambdaRequest::into) + aws_lambda_json_impl::from_str(s).map(LambdaRequest::into) +} + +/// Deserializes a `Request` from a string of JSON text using simd_json +/// +/// # Example +/// +/// ```rust,no_run +/// use lambda_http::request::from_str; +/// use std::fs::File; +/// use std::error::Error; +/// +/// fn main() -> Result<(), Box<dyn Error>> { +/// let mut json = r#"{ ...raw json here... }"#.to_owned(); +/// let request = unsafe{ from_str(&mut json) }?; +/// Ok(println!("{:#?}", request)) +/// } +/// ``` +/// +/// # Safety +/// +/// simd_json requires mutable access to the string slice and may +/// leave the slice with invalid UTF8 data. +/// +#[cfg(feature = "simd_json")] +pub unsafe fn from_str(s: &mut str) -> Result<crate::Request, JsonError> { + aws_lambda_json_impl::from_str(s).map(LambdaRequest::into) +} + +/// Deserializes a `Request` from an owned String of JSON text. +/// +/// # Example +/// +/// ```rust,no_run +/// use lambda_http::request::from_string; +/// use std::fs::File; +/// use std::error::Error; +/// +/// fn main() -> Result<(), Box<dyn Error>> { +/// let mut json = r#"{ ...raw json here... }"#.to_owned(); +/// let request = from_string(json); +/// Ok(println!("{:#?}", request)) +/// } +/// ``` +pub fn from_string(s: String) -> Result<crate::Request, JsonError> { + let mut v = s.into_bytes(); + aws_lambda_json_impl::from_slice(v.as_mut_slice()).map(LambdaRequest::into) } fn x_forwarded_proto() -> HeaderName { @@ -529,6 +574,8 @@ mod tests { use super::*; use crate::ext::RequestExt; use std::fs::File; + #[cfg(feature = "simd_json")] + use aws_lambda_json_impl::simd_json::prelude::ValueAsScalar; #[test] fn deserializes_apigw_request_events_from_readables() { @@ -539,6 +586,7 @@ mod tests { assert!(result.is_ok(), "event was not parsed as expected {result:?}"); } + #[cfg(not(feature = "simd_json"))] #[test] fn deserializes_minimal_apigw_http_request_events() { // from the docs @@ -561,12 +609,41 @@ mod tests { ); } + #[cfg(feature = "simd_json")] + #[test] + fn deserializes_minimal_apigw_http_request_events_with_simd() { + // from the docs + // https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-api-gateway-request + let mut input = include_str!("../tests/data/apigw_v2_proxy_request_minimal.json").to_owned(); + let result = unsafe{ from_str(&mut input) }; + assert!( + result.is_ok(), + "event was not parsed as expected {result:?} given {input}" //SO not safe to do! + ); + let req = result.expect("failed to parse request"); + assert_eq!(req.method(), "GET"); + assert_eq!(req.uri(), "https://xxx.execute-api.us-east-1.amazonaws.com/"); + + // Ensure this is an APIGWv2 request + let req_context = req.request_context_ref().expect("Request is missing RequestContext"); + assert!( + matches!(req_context, &RequestContext::ApiGatewayV2(_)), + "expected ApiGatewayV2 context, got {req_context:?}" + ); + } + + // + // From here on, for testing, we use the from_string function in order top avoid + // duplicating test sources and having to deal with safety and mutability differences + // between the two JSON parsers we support + // + #[test] fn deserializes_apigw_http_request_events() { // from the docs // https://docs.aws.amazon.com/lambda/latest/dg/eventsources.html#eventsources-api-gateway-request let input = include_str!("../tests/data/apigw_v2_proxy_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -599,7 +676,7 @@ mod tests { // https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format // https://docs.aws.amazon.com/apigateway/latest/developerguide/http-api-develop-integrations-lambda.html let input = include_str!("../tests/data/apigw_proxy_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -624,7 +701,7 @@ mod tests { // from the docs // https://docs.aws.amazon.com/lambda/latest/dg/urls-invocation.html#urls-payloads let input = include_str!("../tests/data/lambda_function_url_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -657,7 +734,7 @@ mod tests { // from the docs // https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers let input = include_str!("../tests/data/alb_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -682,7 +759,7 @@ mod tests { // from the docs // https://docs.aws.amazon.com/elasticloadbalancing/latest/application/lambda-functions.html#multi-value-headers let input = include_str!("../tests/data/alb_request_encoded_query_parameters.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -707,7 +784,7 @@ mod tests { // from docs // https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format let input = include_str!("../tests/data/apigw_multi_value_proxy_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event is was not parsed as expected {result:?} given {input}" @@ -737,7 +814,7 @@ mod tests { // from docs // https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format let input = include_str!("../tests/data/alb_multi_value_request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event is was not parsed as expected {result:?} given {input}" @@ -763,7 +840,7 @@ mod tests { // from docs // https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format let input = include_str!("../tests/data/alb_multi_value_request_encoded_query_parameters.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event is was not parsed as expected {result:?} given {input}" @@ -794,7 +871,7 @@ mod tests { // * sam local start-api // * Invoke the API let input = include_str!("../tests/data/apigw_v2_sam_local.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -808,7 +885,7 @@ mod tests { fn deserialize_apigw_no_host() { // generated from the 'apigateway-aws-proxy' test event template in the Lambda console let input = include_str!("../tests/data/apigw_no_host.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -822,7 +899,7 @@ mod tests { fn deserialize_alb_no_host() { // generated from ALB health checks let input = include_str!("../tests/data/alb_no_host.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -836,7 +913,7 @@ mod tests { fn deserialize_apigw_path_with_space() { // generated from ALB health checks let input = include_str!("../tests/data/apigw_request_path_with_space.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -854,7 +931,7 @@ mod tests { #[test] fn deserializes_apigw_http_request_with_stage_in_path() { let input = include_str!("../tests/data/apigw_v2_proxy_request_with_stage_in_path.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" @@ -880,7 +957,7 @@ mod tests { // from docs // https://docs.aws.amazon.com/apigateway/latest/developerguide/set-up-lambda-proxy-integrations.html#api-gateway-simple-proxy-for-lambda-input-format let input = include_str!("../tests/data/apigw_multi_value_proxy_request.json"); - let request = from_str(input).expect("failed to parse request"); + let request = from_string(input.to_owned()).expect("failed to parse request"); let (mut parts, _) = request.into_parts(); #[derive(Deserialize)] @@ -902,7 +979,7 @@ mod tests { use axum_core::extract::FromRequestParts; use axum_extra::extract::Query; let input = include_str!("../tests/data/apigw_v2_proxy_request.json"); - let request = from_str(input).expect("failed to parse request"); + let request = from_string(input.to_owned()).expect("failed to parse request"); let (mut parts, _) = request.into_parts(); #[derive(Deserialize)] @@ -923,7 +1000,7 @@ mod tests { use axum_core::extract::FromRequestParts; use axum_extra::extract::Query; let input = include_str!("../tests/data/alb_multi_value_request.json"); - let request = from_str(input).expect("failed to parse request"); + let request = from_string(input.to_owned()).expect("failed to parse request"); let (mut parts, _) = request.into_parts(); #[derive(Deserialize)] @@ -943,7 +1020,7 @@ mod tests { #[cfg(feature = "apigw_rest")] fn deserializes_request_authorizer() { let input = include_str!("../../lambda-events/src/fixtures/example-apigw-request.json"); - let result = from_str(input); + let result = from_string(input.to_owned()); assert!( result.is_ok(), "event was not parsed as expected {result:?} given {input}" diff --git a/lambda-http/src/response.rs b/lambda-http/src/response.rs index e8528fdf..60ed5bf9 100644 --- a/lambda-http/src/response.rs +++ b/lambda-http/src/response.rs @@ -51,7 +51,7 @@ pub enum LambdaResponse { #[cfg(feature = "alb")] Alb(AlbTargetGroupResponse), #[cfg(feature = "pass_through")] - PassThrough(serde_json::Value), + PassThrough(aws_lambda_json_impl::Value), } /// Transformation from http type to internal type @@ -133,9 +133,15 @@ impl LambdaResponse { RequestOrigin::PassThrough => { match body { // text body must be a valid json string - Some(Body::Text(body)) => {LambdaResponse::PassThrough(serde_json::from_str(&body).unwrap_or_default())}, + #[cfg(not(feature = "simd_json"))] + Some(Body::Text(body)) => {LambdaResponse::PassThrough(aws_lambda_json_impl::from_str(&body).unwrap_or_default())}, + #[cfg(feature = "simd_json")] + Some(Body::Text(body)) => {LambdaResponse::PassThrough(aws_lambda_json_impl::from_string(body).unwrap_or_default())}, // binary body and other cases return Value::Null - _ => LambdaResponse::PassThrough(serde_json::Value::Null), + #[cfg(not(feature = "simd_json"))] + _ => LambdaResponse::PassThrough(aws_lambda_json_impl::Value::Null), + #[cfg(feature = "simd_json")] + _ => LambdaResponse::PassThrough(aws_lambda_json_impl::Value::Static(aws_lambda_json_impl::StaticNode::Null)), } } #[cfg(not(any( @@ -195,14 +201,14 @@ impl IntoResponse for Vec<u8> { } } -impl IntoResponse for serde_json::Value { +impl IntoResponse for aws_lambda_json_impl::Value { fn into_response(self) -> ResponseFuture { Box::pin(async move { Response::builder() .header(CONTENT_TYPE, "application/json") .body( - serde_json::to_string(&self) - .expect("unable to serialize serde_json::Value") + aws_lambda_json_impl::to_string(&self) + .expect("unable to serialize aws_lambda_json_impl::Value") .into(), ) .expect("unable to build http::Response") @@ -258,7 +264,7 @@ impl IntoResponse for (StatusCode, Vec<u8>) { } } -impl IntoResponse for (StatusCode, serde_json::Value) { +impl IntoResponse for (StatusCode, aws_lambda_json_impl::Value) { fn into_response(self) -> ResponseFuture { let (status, body) = self; Box::pin(async move { @@ -266,8 +272,8 @@ impl IntoResponse for (StatusCode, serde_json::Value) { .status(status) .header(CONTENT_TYPE, "application/json") .body( - serde_json::to_string(&body) - .expect("unable to serialize serde_json::Value") + aws_lambda_json_impl::to_string(&body) + .expect("unable to serialize aws_lambda_json_impl::Value") .into(), ) .expect("unable to build http::Response") @@ -373,12 +379,12 @@ pub type BodyFuture = Pin<Box<dyn Future<Output = Body> + Send>>; #[cfg(test)] mod tests { use super::{Body, IntoResponse, LambdaResponse, RequestOrigin, X_LAMBDA_HTTP_CONTENT_ENCODING}; + use aws_lambda_json_impl::json; use http::{ header::{CONTENT_ENCODING, CONTENT_TYPE}, Response, StatusCode, }; use lambda_runtime_api_client::body::Body as HyperBody; - use serde_json::{self, json}; const SVG_LOGO: &str = include_str!("../tests/data/svg_logo.svg"); @@ -475,7 +481,7 @@ mod tests { let response = response.into_response().await; let response = LambdaResponse::from_response(&RequestOrigin::ApiGatewayV2, response); - let json = serde_json::to_string(&response).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&response).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{"content-encoding":"gzip"},"multiValueHeaders":{},"body":"MDAwMDAw","isBase64Encoded":true,"cookies":[]}"# @@ -493,7 +499,7 @@ mod tests { let response = response.into_response().await; let response = LambdaResponse::from_response(&RequestOrigin::ApiGatewayV2, response); - let json = serde_json::to_string(&response).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&response).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{"content-type":"application/json"},"multiValueHeaders":{},"body":"000000","isBase64Encoded":false,"cookies":[]}"# @@ -511,7 +517,7 @@ mod tests { let response = response.into_response().await; let response = LambdaResponse::from_response(&RequestOrigin::ApiGatewayV2, response); - let json = serde_json::to_string(&response).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&response).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{"content-type":"application/json; charset=utf-16"},"multiValueHeaders":{},"body":"〰〰〰","isBase64Encoded":false,"cookies":[]}"# @@ -529,7 +535,7 @@ mod tests { let response = response.into_response().await; let response = LambdaResponse::from_response(&RequestOrigin::ApiGatewayV2, response); - let json = serde_json::to_string(&response).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&response).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{"content-type":"application/graphql-response+json; charset=utf-16"},"multiValueHeaders":{},"body":"〰〰〰","isBase64Encoded":false,"cookies":[]}"# @@ -546,7 +552,7 @@ mod tests { let response = response.into_response().await; let response = LambdaResponse::from_response(&RequestOrigin::ApiGatewayV2, response); - let json = serde_json::to_string(&response).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&response).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{},"multiValueHeaders":{},"body":"000000","isBase64Encoded":false,"cookies":[]}"# @@ -563,7 +569,7 @@ mod tests { .body(Body::from(())) .expect("failed to create response"), ); - let json = serde_json::to_string(&res).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&res).expect("failed to serialize to json"); assert_eq!( json, r#"{"statusCode":200,"headers":{},"multiValueHeaders":{"multi":["a","b"]},"isBase64Encoded":false}"# @@ -580,7 +586,7 @@ mod tests { .body(Body::from(())) .expect("failed to create response"), ); - let json = serde_json::to_string(&res).expect("failed to serialize to json"); + let json = aws_lambda_json_impl::to_string(&res).expect("failed to serialize to json"); assert_eq!( "{\"statusCode\":200,\"headers\":{},\"multiValueHeaders\":{},\"isBase64Encoded\":false,\"cookies\":[\"cookie1=a\",\"cookie2=b\"]}", json diff --git a/lambda-integration-tests/Cargo.toml b/lambda-integration-tests/Cargo.toml index ee44a969..b46feeff 100644 --- a/lambda-integration-tests/Cargo.toml +++ b/lambda-integration-tests/Cargo.toml @@ -13,10 +13,13 @@ readme = "../README.md" [dependencies] lambda_runtime = { path = "../lambda-runtime" } aws_lambda_events = { path = "../lambda-events" } -serde_json = "1.0.121" +aws_lambda_json_impl = { path = "../json-impl" } tokio = { version = "1", features = ["full"] } serde = { version = "1.0.204", features = ["derive"] } +[features] +simd_json = [ "aws_lambda_json_impl/simd", "lambda_runtime/simd_json", "aws_lambda_events/simd_json" ] + [dev-dependencies] reqwest = { version = "0.12.5", features = ["blocking"] } openssl = { version = "0.10", features = ["vendored"] } diff --git a/lambda-integration-tests/src/authorizer.rs b/lambda-integration-tests/src/authorizer.rs index 41ddd2d8..42de319a 100644 --- a/lambda-integration-tests/src/authorizer.rs +++ b/lambda-integration-tests/src/authorizer.rs @@ -4,9 +4,9 @@ use aws_lambda_events::{ apigw::{ApiGatewayCustomAuthorizerPolicy, ApiGatewayCustomAuthorizerResponse}, event::iam::IamPolicyStatement, }; +use aws_lambda_json_impl::json; use lambda_runtime::{service_fn, tracing, Error, LambdaEvent}; use serde::Deserialize; -use serde_json::json; #[derive(Deserialize)] #[serde(rename_all = "camelCase")] diff --git a/lambda-runtime/Cargo.toml b/lambda-runtime/Cargo.toml index 0f63cd47..e380b20c 100644 --- a/lambda-runtime/Cargo.toml +++ b/lambda-runtime/Cargo.toml @@ -20,6 +20,7 @@ opentelemetry = ["opentelemetry-semantic-conventions"] # enables access to the O anyhow = ["dep:anyhow"] # enables From<T> for Diagnostic for anyhow error types, see README.md for more info eyre = ["dep:eyre"] # enables From<T> for Diagnostic for eyre error types, see README.md for more info miette = ["dep:miette"] # enables From<T> for Diagnostic for miette error types, see README.md for more info +simd_json = [ "aws_lambda_json_impl/simd" ] [dependencies] anyhow = { version = "1.0.86", optional = true } @@ -44,7 +45,7 @@ miette = { version = "7.2.0", optional = true } opentelemetry-semantic-conventions = { version = "0.14", optional = true } pin-project = "1" serde = { version = "1", features = ["derive", "rc"] } -serde_json = "^1" +aws_lambda_json_impl = { path = "../json-impl" } serde_path_to_error = "0.1.11" tokio = { version = "1.0", features = [ "macros", diff --git a/lambda-runtime/src/deserializer.rs b/lambda-runtime/src/deserializer.rs index 1841c050..41c0e5c4 100644 --- a/lambda-runtime/src/deserializer.rs +++ b/lambda-runtime/src/deserializer.rs @@ -6,38 +6,94 @@ use crate::{Context, LambdaEvent}; const ERROR_CONTEXT: &str = "failed to deserialize the incoming data into the function's payload type"; -/// Event payload deserialization error. -/// Returned when the data sent to the function cannot be deserialized -/// into the type that the function receives. -#[derive(Debug)] -pub(crate) struct DeserializeError { - inner: serde_path_to_error::Error<serde_json::Error>, -} +use bytes::Bytes; + +#[cfg(not(feature = "simd_json"))] +mod deser_error { + use super::*; + + /// Event payload deserialization error. + /// Returned when the data sent to the function cannot be deserialized + /// into the type that the function receives. + #[derive(Debug)] + #[cfg(not(feature = "simd_json"))] + pub(crate) struct DeserializeError { + pub(super) inner: serde_path_to_error::Error<aws_lambda_json_impl::JsonError>, + } + impl fmt::Display for DeserializeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let path = self.inner.to_string(); + if path == "." { + writeln!(f, "{ERROR_CONTEXT}: {}", self.inner) + } else { + writeln!(f, "{ERROR_CONTEXT}: [{path}] {}", self.inner) + } + } + } -impl fmt::Display for DeserializeError { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - let path = self.inner.path().to_string(); - if path == "." { - writeln!(f, "{ERROR_CONTEXT}: {}", self.inner) - } else { - writeln!(f, "{ERROR_CONTEXT}: [{path}] {}", self.inner) + impl Error for DeserializeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.inner) } } } -impl Error for DeserializeError { - fn source(&self) -> Option<&(dyn Error + 'static)> { - Some(&self.inner) +#[cfg(feature = "simd_json")] +mod deser_error { + use super::*; + + /// Event payload deserialization error. + /// Returned when the data sent to the function cannot be deserialized + /// into the type that the function receives. + /// For simd_json, we can't get serde_path_to_error to work at the moment + + #[derive(Debug)] + pub(crate) struct DeserializeError { + pub(super) inner: aws_lambda_json_impl::JsonError + } + + impl fmt::Display for DeserializeError { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let path = self.inner.to_string(); + if path == "." { + writeln!(f, "{ERROR_CONTEXT}: {}", self.inner) + } else { + writeln!(f, "{ERROR_CONTEXT}: [{path}] {}", self.inner) + } + } + } + + impl Error for DeserializeError { + fn source(&self) -> Option<&(dyn Error + 'static)> { + Some(&self.inner) + } } } +pub(crate) use deser_error::*; + /// Deserialize the data sent to the function into the type that the function receives. -pub(crate) fn deserialize<T>(body: &[u8], context: Context) -> Result<LambdaEvent<T>, DeserializeError> +#[cfg(not(feature = "simd_json"))] +pub(crate) fn deserialize<T>(body: Bytes, context: Context) -> Result<LambdaEvent<T>, DeserializeError> where T: for<'de> Deserialize<'de>, { - let jd = &mut serde_json::Deserializer::from_slice(body); + use aws_lambda_json_impl::JsonDeserializer; + let jd = &mut JsonDeserializer::from_slice(&body); serde_path_to_error::deserialize(jd) .map(|payload| LambdaEvent::new(payload, context)) .map_err(|inner| DeserializeError { inner }) } + +#[cfg(feature = "simd_json")] +pub(crate) fn deserialize<T>(body: Bytes, context: Context) -> Result<LambdaEvent<T>, DeserializeError> +where + T: for<'de> Deserialize<'de>, +{ + //TODO: Find a way to make serde_path_to_json work + aws_lambda_json_impl::from_bytes(body) + .map(|payload| LambdaEvent::new(payload, context)) + .map_err(|inner| DeserializeError { inner }) +} + + diff --git a/lambda-runtime/src/diagnostic.rs b/lambda-runtime/src/diagnostic.rs index c03ce284..d4742639 100644 --- a/lambda-runtime/src/diagnostic.rs +++ b/lambda-runtime/src/diagnostic.rs @@ -158,7 +158,7 @@ mod test { #[test] fn round_trip_lambda_error() { - use serde_json::{json, Value}; + use aws_lambda_json_impl::{json, Value}; let expected = json!({ "errorType": "InvalidEventDataError", "errorMessage": "Error parsing event data.", @@ -168,7 +168,7 @@ mod test { error_type: "InvalidEventDataError".into(), error_message: "Error parsing event data.".into(), }; - let actual: Value = serde_json::to_value(actual).expect("failed to serialize diagnostic"); + let actual: Value = aws_lambda_json_impl::to_value(actual).expect("failed to serialize diagnostic"); assert_eq!(expected, actual); } diff --git a/lambda-runtime/src/layers/api_response.rs b/lambda-runtime/src/layers/api_response.rs index e744cde1..59e24fc0 100644 --- a/lambda-runtime/src/layers/api_response.rs +++ b/lambda-runtime/src/layers/api_response.rs @@ -102,7 +102,7 @@ where }; let request_id = req.context.request_id.clone(); - let lambda_event = match deserializer::deserialize::<EventPayload>(&req.body, req.context) { + let lambda_event = match deserializer::deserialize::<EventPayload>(req.body, req.context) { Ok(lambda_event) => lambda_event, Err(err) => match build_event_error_request(&request_id, err) { Ok(request) => return RuntimeApiResponseFuture::Ready(Some(Ok(request))), diff --git a/lambda-runtime/src/requests.rs b/lambda-runtime/src/requests.rs index 729272f2..96e8ad8e 100644 --- a/lambda-runtime/src/requests.rs +++ b/lambda-runtime/src/requests.rs @@ -86,7 +86,7 @@ where let uri = format!("/2018-06-01/runtime/invocation/{}/response", self.request_id); let uri = Uri::from_str(&uri)?; - let body = serde_json::to_vec(&body)?; + let body = aws_lambda_json_impl::to_vec(&body)?; let body = Body::from(body); let req = build_request().method(Method::POST).uri(uri).body(body)?; @@ -116,7 +116,7 @@ where .entry(CONTENT_TYPE) .or_insert("application/octet-stream".parse()?); - let metadata_prelude = serde_json::to_string(&response.metadata_prelude)?; + let metadata_prelude = aws_lambda_json_impl::to_string(&response.metadata_prelude)?; tracing::trace!(?metadata_prelude); @@ -174,7 +174,7 @@ impl<'a> IntoRequest for EventErrorRequest<'a> { fn into_req(self) -> Result<Request<Body>, Error> { let uri = format!("/2018-06-01/runtime/invocation/{}/error", self.request_id); let uri = Uri::from_str(&uri)?; - let body = serde_json::to_vec(&self.diagnostic)?; + let body = aws_lambda_json_impl::to_vec(&self.diagnostic)?; let body = Body::from(body); let req = build_request() diff --git a/lambda-runtime/src/runtime.rs b/lambda-runtime/src/runtime.rs index 1c676480..818b82c1 100644 --- a/lambda-runtime/src/runtime.rs +++ b/lambda-runtime/src/runtime.rs @@ -327,7 +327,7 @@ mod endpoint_tests { error_type: "InvalidEventDataError".into(), error_message: "Error parsing event data".into(), }; - let body = serde_json::to_string(&diagnostic)?; + let body = aws_lambda_json_impl::to_string(&diagnostic)?; let server = MockServer::start(); let mock = server.mock(|when, then| { @@ -377,7 +377,7 @@ mod endpoint_tests { let base = server.base_url().parse().expect("Invalid mock server Uri"); let client = Client::builder().with_endpoint(base).build()?; - async fn func(event: crate::LambdaEvent<serde_json::Value>) -> Result<serde_json::Value, Error> { + async fn func(event: crate::LambdaEvent<aws_lambda_json_impl::Value>) -> Result<aws_lambda_json_impl::Value, Error> { let (event, _) = event.into_parts(); Ok(event) } @@ -421,7 +421,7 @@ mod endpoint_tests { async fn run_panicking_handler<F>(func: F) -> Result<(), Error> where - F: FnMut(crate::LambdaEvent<serde_json::Value>) -> BoxFuture<'static, Result<serde_json::Value, Error>> + F: FnMut(crate::LambdaEvent<aws_lambda_json_impl::Value>) -> BoxFuture<'static, Result<aws_lambda_json_impl::Value, Error>> + Send + 'static, { diff --git a/lambda-runtime/src/types.rs b/lambda-runtime/src/types.rs index ee09978f..f01c412a 100644 --- a/lambda-runtime/src/types.rs +++ b/lambda-runtime/src/types.rs @@ -4,12 +4,16 @@ use bytes::Bytes; use http::{header::ToStrError, HeaderMap, HeaderValue, StatusCode}; use lambda_runtime_api_client::body::Body; use serde::{Deserialize, Serialize}; +#[cfg(feature = "simd_json")] +use serde::de::DeserializeOwned; + use std::{ collections::HashMap, fmt::Debug, time::{Duration, SystemTime}, }; use tokio_stream::Stream; +use aws_lambda_json_impl::JsonError; /// Client context sent by the AWS Mobile SDK. #[derive(Serialize, Deserialize, Clone, Debug, Eq, PartialEq)] @@ -99,18 +103,31 @@ impl Default for Context { } } +#[cfg(not(feature = "simd_json"))] +fn parse_header<'a,T>(h: &'a HeaderValue) -> Result<T, JsonError> +where T: Deserialize<'a> { + aws_lambda_json_impl::from_slice(h.as_bytes()) +} + +#[cfg(feature = "simd_json")] +fn parse_header<T>(h: &HeaderValue) -> Result<T, JsonError> +where T: DeserializeOwned { + let v = h.as_bytes().to_vec(); + aws_lambda_json_impl::from_vec(v) +} + impl Context { /// Create a new [Context] struct based on the function configuration /// and the incoming request data. pub fn new(request_id: &str, env_config: RefConfig, headers: &HeaderMap) -> Result<Self, Error> { let client_context: Option<ClientContext> = if let Some(value) = headers.get("lambda-runtime-client-context") { - serde_json::from_str(value.to_str()?)? + parse_header(value)? } else { None }; let identity: Option<CognitoIdentity> = if let Some(value) = headers.get("lambda-runtime-cognito-identity") { - serde_json::from_str(value.to_str()?)? + parse_header(value)? } else { None }; @@ -323,7 +340,7 @@ mod test { custom, environment, }; - let client_context_str = serde_json::to_string(&client_context).unwrap(); + let client_context_str = aws_lambda_json_impl::to_string(&client_context).unwrap(); let mut headers = HeaderMap::new(); headers.insert("lambda-runtime-aws-request-id", HeaderValue::from_static("my-id")); headers.insert("lambda-runtime-deadline-ms", HeaderValue::from_static("123")); @@ -360,7 +377,7 @@ mod test { identity_id: String::new(), identity_pool_id: String::new(), }; - let cognito_identity_str = serde_json::to_string(&cognito_identity).unwrap(); + let cognito_identity_str = aws_lambda_json_impl::to_string(&cognito_identity).unwrap(); let mut headers = HeaderMap::new(); headers.insert("lambda-runtime-aws-request-id", HeaderValue::from_static("my-id")); headers.insert("lambda-runtime-deadline-ms", HeaderValue::from_static("123"));