Skip to content

Commit bfc73a6

Browse files
mpindaruMara Pindaru
andauthored
feat(lambda-events, lambda-http): mark all public structs/enums as #[non_exhaustive] (#1046)
Co-authored-by: Mara Pindaru <[email protected]>
1 parent b40c011 commit bfc73a6

File tree

11 files changed

+108
-58
lines changed

11 files changed

+108
-58
lines changed

lambda-events/src/event/alb/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ pub struct ElbContext {
7777
}
7878

7979
/// `AlbTargetGroupResponse` configures the response to be returned by the ALB Lambda target group for the request
80+
#[non_exhaustive]
8081
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
8182
#[serde(rename_all = "camelCase")]
8283
pub struct AlbTargetGroupResponse {

lambda-events/src/event/apigw/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ pub struct ApiGatewayProxyRequest {
5858
}
5959

6060
/// `ApiGatewayProxyResponse` configures the response to be returned by API Gateway for the request
61+
#[non_exhaustive]
6162
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
6263
#[serde(rename_all = "camelCase")]
6364
pub struct ApiGatewayProxyResponse {
@@ -349,6 +350,7 @@ pub struct ApiGatewayV2httpRequestContextHttpDescription {
349350
}
350351

351352
/// `ApiGatewayV2httpResponse` configures the response to be returned by API Gateway V2 for the request
353+
#[non_exhaustive]
352354
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
353355
#[serde(rename_all = "camelCase")]
354356
pub struct ApiGatewayV2httpResponse {
@@ -897,6 +899,7 @@ pub struct ApiGatewayCustomAuthorizerRequestTypeRequest {
897899
}
898900

899901
/// `ApiGatewayCustomAuthorizerResponse` represents the expected format of an API Gateway authorization response.
902+
#[non_exhaustive]
900903
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
901904
#[serde(rename_all = "camelCase")]
902905
pub struct ApiGatewayCustomAuthorizerResponse<T1 = Value>
@@ -963,6 +966,7 @@ where
963966
}
964967

965968
/// `ApiGatewayCustomAuthorizerPolicy` represents an IAM policy
969+
#[non_exhaustive]
966970
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
967971
#[serde(rename_all = "PascalCase")]
968972
pub struct ApiGatewayCustomAuthorizerPolicy {

lambda-events/src/event/iam/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ pub struct IamPolicyDocument {
2525
}
2626

2727
/// `IamPolicyStatement` represents one statement from IAM policy with action, effect and resource
28+
#[non_exhaustive]
2829
#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
2930
#[serde(rename_all = "PascalCase")]
3031
pub struct IamPolicyStatement {

lambda-http/src/ext/extensions.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,27 @@ use lambda_runtime::Context;
77
use crate::request::RequestContext;
88

99
/// ALB/API gateway pre-parsed http query string parameters
10+
#[non_exhaustive]
1011
#[derive(Clone)]
1112
pub(crate) struct QueryStringParameters(pub(crate) QueryMap);
1213

1314
/// API gateway pre-extracted url path parameters
1415
///
1516
/// These will always be empty for ALB requests
17+
#[non_exhaustive]
1618
#[derive(Clone)]
1719
pub(crate) struct PathParameters(pub(crate) QueryMap);
1820

1921
/// API gateway configured
2022
/// [stage variables](https://docs.aws.amazon.com/apigateway/latest/developerguide/stage-variables.html)
2123
///
2224
/// These will always be empty for ALB requests
25+
#[non_exhaustive]
2326
#[derive(Clone)]
2427
pub(crate) struct StageVariables(pub(crate) QueryMap);
2528

2629
/// ALB/API gateway raw http path without any stage information
30+
#[non_exhaustive]
2731
#[derive(Clone)]
2832
pub(crate) struct RawHttpPath(pub(crate) String);
2933

lambda-http/src/ext/request.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@ use crate::Body;
1212
/// Request payload deserialization errors
1313
///
1414
/// Returned by [`RequestPayloadExt::payload()`]
15-
#[derive(Debug)]
1615
#[non_exhaustive]
16+
#[derive(Debug)]
1717
pub enum PayloadError {
1818
/// Returned when `application/json` bodies fail to deserialize a payload
1919
Json(serde_json::Error),
@@ -22,16 +22,16 @@ pub enum PayloadError {
2222
}
2323

2424
/// Indicates a problem processing a JSON payload.
25-
#[derive(Debug)]
2625
#[non_exhaustive]
26+
#[derive(Debug)]
2727
pub enum JsonPayloadError {
2828
/// Problem deserializing a JSON payload.
2929
Parsing(serde_json::Error),
3030
}
3131

3232
/// Indicates a problem processing an x-www-form-urlencoded payload.
33-
#[derive(Debug)]
3433
#[non_exhaustive]
34+
#[derive(Debug)]
3535
pub enum FormUrlEncodedPayloadError {
3636
/// Problem deserializing an x-www-form-urlencoded payload.
3737
Parsing(SerdeError),

lambda-http/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ pub type Request = http::Request<Body>;
110110
/// Future that will convert an [`IntoResponse`] into an actual [`LambdaResponse`]
111111
///
112112
/// This is used by the `Adapter` wrapper and is completely internal to the `lambda_http::run` function.
113+
#[non_exhaustive]
113114
#[doc(hidden)]
114115
pub enum TransformResponse<'a, R, E> {
115116
Request(RequestOrigin, RequestFuture<'a, R, E>),
@@ -143,6 +144,7 @@ where
143144
/// Wraps a `Service<Request>` in a `Service<LambdaEvent<Request>>`
144145
///
145146
/// This is completely internal to the `lambda_http::run` function.
147+
#[non_exhaustive]
146148
#[doc(hidden)]
147149
pub struct Adapter<'a, R, S> {
148150
service: S,

lambda-http/src/request.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ use url::Url;
3939
///
4040
/// This is not intended to be a type consumed by crate users directly. The order
4141
/// of the variants are notable. Serde will try to deserialize in this order.
42+
#[non_exhaustive]
4243
#[doc(hidden)]
4344
#[derive(Debug)]
4445
pub enum LambdaRequest {
@@ -85,6 +86,7 @@ impl LambdaRequest {
8586
pub type RequestFuture<'a, R, E> = Pin<Box<dyn Future<Output = Result<R, E>> + Send + 'a>>;
8687

8788
/// Represents the origin from which the lambda was requested from.
89+
#[non_exhaustive]
8890
#[doc(hidden)]
8991
#[derive(Debug, Clone)]
9092
pub enum RequestOrigin {
@@ -388,6 +390,7 @@ fn apigw_path_with_stage(stage: &Option<String>, path: &str) -> String {
388390

389391
/// Event request context as an enumeration of request contexts
390392
/// for both ALB and API Gateway and HTTP API events
393+
#[non_exhaustive]
391394
#[derive(Deserialize, Debug, Clone, Serialize)]
392395
#[serde(untagged)]
393396
pub enum RequestContext {

lambda-http/src/response.rs

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const TEXT_ENCODING_PREFIXES: [&str; 5] = [
4040
const TEXT_ENCODING_SUFFIXES: [&str; 3] = ["+xml", "+yaml", "+json"];
4141

4242
/// Representation of Lambda response
43+
#[non_exhaustive]
4344
#[doc(hidden)]
4445
#[derive(Serialize, Debug)]
4546
#[serde(untagged)]
@@ -70,17 +71,22 @@ impl LambdaResponse {
7071

7172
match request_origin {
7273
#[cfg(feature = "apigw_rest")]
73-
RequestOrigin::ApiGatewayV1 => LambdaResponse::ApiGatewayV1(ApiGatewayProxyResponse {
74-
body,
75-
is_base64_encoded,
76-
status_code: status_code as i64,
74+
RequestOrigin::ApiGatewayV1 => LambdaResponse::ApiGatewayV1({
75+
let mut response = ApiGatewayProxyResponse::default();
76+
77+
response.body = body;
78+
response.is_base64_encoded = is_base64_encoded;
79+
response.status_code = status_code as i64;
7780
// Explicitly empty, as API gateway v1 will merge "headers" and
7881
// "multi_value_headers" fields together resulting in duplicate response headers.
79-
headers: HeaderMap::new(),
80-
multi_value_headers: headers,
82+
response.headers = HeaderMap::new();
83+
response.multi_value_headers = headers;
8184
// Today, this implementation doesn't provide any additional fields
8285
#[cfg(feature = "catch-all-fields")]
83-
other: Default::default()
86+
{
87+
response.other = Default::default();
88+
}
89+
response
8490
}),
8591
#[cfg(feature = "apigw_http")]
8692
RequestOrigin::ApiGatewayV2 => {
@@ -96,51 +102,64 @@ impl LambdaResponse {
96102
.collect();
97103
headers.remove(SET_COOKIE);
98104

99-
LambdaResponse::ApiGatewayV2(ApiGatewayV2httpResponse {
100-
body,
101-
is_base64_encoded,
102-
status_code: status_code as i64,
103-
cookies,
105+
LambdaResponse::ApiGatewayV2({
106+
let mut response = ApiGatewayV2httpResponse::default();
107+
response.body = body;
108+
response.is_base64_encoded = is_base64_encoded;
109+
response.status_code = status_code as i64;
110+
response.cookies = cookies;
104111
// API gateway v2 doesn't have "multi_value_headers" field. Duplicate headers
105112
// are combined with commas and included in the headers field.
106-
headers,
107-
multi_value_headers: HeaderMap::new(),
113+
response.headers = headers;
114+
response.multi_value_headers = HeaderMap::new();
108115
// Today, this implementation doesn't provide any additional fields
109116
#[cfg(feature = "catch-all-fields")]
110-
other: Default::default(),
117+
{
118+
response.other = Default::default();
119+
}
120+
response
111121
})
112122
}
113123
#[cfg(feature = "alb")]
114-
RequestOrigin::Alb => LambdaResponse::Alb(AlbTargetGroupResponse {
115-
body,
116-
status_code: status_code as i64,
117-
is_base64_encoded,
124+
RequestOrigin::Alb => LambdaResponse::Alb({
125+
let mut response = AlbTargetGroupResponse::default();
126+
127+
response.body = body;
128+
response.is_base64_encoded = is_base64_encoded;
129+
response.status_code = status_code as i64;
118130
// ALB responses are used for ALB integration, which can be configured to use
119131
// either "headers" or "multi_value_headers" field. We need to return both fields
120132
// to ensure both configuration work correctly.
121-
headers: headers.clone(),
122-
multi_value_headers: headers,
123-
status_description: Some(format!(
133+
response.headers = headers.clone();
134+
response.multi_value_headers = headers;
135+
response.status_description = Some(format!(
124136
"{} {}",
125137
status_code,
126138
parts.status.canonical_reason().unwrap_or_default()
127-
)),
139+
));
128140
// Today, this implementation doesn't provide any additional fields
129141
#[cfg(feature = "catch-all-fields")]
130-
other: Default::default(),
142+
{
143+
response.other = Default::default();
144+
}
145+
response
131146
}),
132147
#[cfg(feature = "apigw_websockets")]
133-
RequestOrigin::WebSocket => LambdaResponse::ApiGatewayV1(ApiGatewayProxyResponse {
134-
body,
135-
is_base64_encoded,
136-
status_code: status_code as i64,
148+
RequestOrigin::WebSocket => LambdaResponse::ApiGatewayV1({
149+
let mut response = ApiGatewayProxyResponse::default();
150+
response.body = body;
151+
response.is_base64_encoded = is_base64_encoded;
152+
response.status_code = status_code as i64;
137153
// Explicitly empty, as API gateway v1 will merge "headers" and
138154
// "multi_value_headers" fields together resulting in duplicate response headers.
139-
headers: HeaderMap::new(),
140-
multi_value_headers: headers,
155+
response.headers = HeaderMap::new();
156+
response.multi_value_headers = headers;
141157
// Today, this implementation doesn't provide any additional fields
142158
#[cfg(feature = "catch-all-fields")]
143-
other: Default::default(),
159+
{
160+
response.other = Default::default();
161+
}
162+
response
144163
}),
145164
#[cfg(feature = "pass_through")]
146165
RequestOrigin::PassThrough => {

lambda-http/src/streaming.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ use std::{future::Future, marker::PhantomData};
2121
/// An adapter that lifts a standard [`Service<Request>`] into a
2222
/// [`Service<LambdaEvent<LambdaRequest>>`] which produces streaming Lambda HTTP
2323
/// responses.
24+
#[non_exhaustive]
2425
pub struct StreamAdapter<'a, S, B> {
2526
service: S,
2627
_phantom_data: PhantomData<&'a B>,
@@ -147,6 +148,7 @@ where
147148
}
148149

149150
pin_project_lite::pin_project! {
151+
#[non_exhaustive]
150152
pub struct BodyStream<B> {
151153
#[pin]
152154
pub(crate) body: B,

lambda-integration-tests/src/authorizer.rs

Lines changed: 27 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -34,26 +34,36 @@ async fn func(
3434
}
3535

3636
fn allow(method_arn: &str) -> ApiGatewayCustomAuthorizerResponse {
37-
let stmt = IamPolicyStatement {
38-
action: vec!["execute-api:Invoke".to_string()],
39-
resource: vec![method_arn.to_owned()],
40-
effect: aws_lambda_events::iam::IamPolicyEffect::Allow,
41-
condition: None,
37+
let stmt = {
38+
let mut statement = IamPolicyStatement::default();
39+
statement.action = vec!["execute-api:Invoke".to_string()];
40+
statement.resource = vec![method_arn.to_owned()];
41+
statement.effect = aws_lambda_events::iam::IamPolicyEffect::Allow;
42+
statement.condition = None;
4243
#[cfg(feature = "catch-all-fields")]
43-
other: Default::default(),
44+
{
45+
statement.other = Default::default();
46+
}
47+
statement
4448
};
45-
let policy = ApiGatewayCustomAuthorizerPolicy {
46-
version: Some("2012-10-17".to_string()),
47-
statement: vec![stmt],
49+
let policy = {
50+
let mut policy = ApiGatewayCustomAuthorizerPolicy::default();
51+
policy.version = Some("2012-10-17".to_string());
52+
policy.statement = vec![stmt];
4853
#[cfg(feature = "catch-all-fields")]
49-
other: Default::default(),
54+
{
55+
policy.other = Default::default();
56+
}
57+
policy
5058
};
51-
ApiGatewayCustomAuthorizerResponse {
52-
principal_id: Some("user".to_owned()),
53-
policy_document: policy,
54-
context: json!({ "hello": "world" }),
55-
usage_identifier_key: None,
56-
#[cfg(feature = "catch-all-fields")]
57-
other: Default::default(),
59+
let mut response = ApiGatewayCustomAuthorizerResponse::default();
60+
response.principal_id = Some("user".to_owned());
61+
response.policy_document = policy;
62+
response.context = json!({ "hello": "world" });
63+
response.usage_identifier_key = None;
64+
#[cfg(feature = "catch-all-fields")]
65+
{
66+
response.other = Default::default();
5867
}
68+
response
5969
}

0 commit comments

Comments
 (0)