Skip to content

Commit ae3ce1d

Browse files
authored
Make lambda::Context a first-class part of the Handler API (#233)
1 parent ed3fd16 commit ae3ce1d

File tree

10 files changed

+72
-71
lines changed

10 files changed

+72
-71
lines changed

lambda-attributes/src/lib.rs

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -38,42 +38,48 @@ pub fn lambda(attr: TokenStream, item: TokenStream) -> TokenStream {
3838
}
3939

4040
let result = match inputs.len() {
41-
1 => {
42-
let event = match inputs.first().unwrap() {
41+
2 => {
42+
let event = match inputs.first().expect("expected event argument") {
4343
FnArg::Typed(arg) => arg,
4444
_ => {
4545
let tokens = quote_spanned! { inputs.span() =>
46-
compile_error!("fn main must take a fully formed argument");
46+
compile_error!("fn main's first argument must be fully formed");
4747
};
4848
return TokenStream::from(tokens);
4949
}
5050
};
51-
let arg_name = &event.pat;
52-
let arg_type = &event.ty;
51+
let event_name = &event.pat;
52+
let event_type = &event.ty;
53+
let context = match inputs.iter().nth(1).expect("expected context argument") {
54+
FnArg::Typed(arg) => arg,
55+
_ => {
56+
let tokens = quote_spanned! { inputs.span() =>
57+
compile_error!("fn main's second argument must be fully formed");
58+
};
59+
return TokenStream::from(tokens);
60+
}
61+
};
62+
let context_name = &context.pat;
63+
let context_type = &context.ty;
5364

5465
if is_http(&args) {
5566
quote_spanned! { input.span() =>
56-
use lambda_http::lambda::LambdaCtx;
5767

5868
#(#attrs)*
5969
#asyncness fn main() {
60-
async fn actual(#arg_name: #arg_type) #ret {
61-
#body
62-
}
70+
async fn actual(#event_name: #event_type, #context_name: #context_type) #ret #body
71+
6372
let f = lambda_http::handler(actual);
6473
lambda_http::lambda::run(f).await.unwrap();
6574
}
6675
}
6776
} else {
6877
quote_spanned! { input.span() =>
6978

70-
use lambda::LambdaCtx;
71-
7279
#(#attrs)*
7380
#asyncness fn main() {
74-
async fn actual(#arg_name: #arg_type) #ret {
75-
#body
76-
}
81+
async fn actual(#event_name: #event_type, #context_name: #context_type) #ret #body
82+
7783
let f = lambda::handler_fn(actual);
7884
lambda::run(f).await.unwrap();
7985
}
@@ -82,7 +88,7 @@ pub fn lambda(attr: TokenStream, item: TokenStream) -> TokenStream {
8288
}
8389
_ => {
8490
let tokens = quote_spanned! { inputs.span() =>
85-
compile_error!("The #[lambda] macro can accept only a single argument.");
91+
compile_error!("The #[lambda] macro can expects two arguments: a triggered event and lambda context.");
8692
};
8793
return TokenStream::from(tokens);
8894
}

lambda-http/examples/hello-http-without-macros.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
use lambda_http::{handler, lambda, IntoResponse, Request, RequestExt, Response};
1+
use lambda_http::{
2+
handler,
3+
lambda::{self, Context},
4+
IntoResponse, Request, RequestExt, Response,
5+
};
26

37
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
48

@@ -8,7 +12,7 @@ async fn main() -> Result<(), Error> {
812
Ok(())
913
}
1014

11-
async fn func(event: Request) -> Result<impl IntoResponse, Error> {
15+
async fn func(event: Request, _: Context) -> Result<impl IntoResponse, Error> {
1216
Ok(match event.query_string_parameters().get("first_name") {
1317
Some(first_name) => format!("Hello, {}!", first_name).into_response(),
1418
_ => Response::builder()

lambda-http/examples/hello-http.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
1-
use lambda_http::{lambda, IntoResponse, Request};
1+
use lambda_http::{
2+
lambda::{lambda, Context},
3+
IntoResponse, Request,
4+
};
25

36
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
47

58
#[lambda(http)]
69
#[tokio::main]
7-
async fn main(_: Request) -> Result<impl IntoResponse, Error> {
10+
async fn main(_: Request, _: Context) -> Result<impl IntoResponse, Error> {
811
Ok("👋 world")
912
}

lambda-http/src/ext.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ impl Error for PayloadError {
6767
/// as well as `{"x":1, "y":2}` respectively.
6868
///
6969
/// ```rust,no_run
70-
/// use lambda_http::{handler, lambda, Body, IntoResponse, Request, Response, RequestExt};
70+
/// use lambda_http::{handler, lambda::{self, Context}, Body, IntoResponse, Request, Response, RequestExt};
7171
/// use serde_derive::Deserialize;
7272
///
7373
/// type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
@@ -87,7 +87,8 @@ impl Error for PayloadError {
8787
/// }
8888
///
8989
/// async fn add(
90-
/// request: Request
90+
/// request: Request,
91+
/// _: Context
9192
/// ) -> Result<Response<Body>, Error> {
9293
/// let args: Args = request.payload()
9394
/// .unwrap_or_else(|_parse_err| None)

lambda-http/src/lib.rs

Lines changed: 16 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
//! The full body of your `main` function will be executed on **every** invocation of your lambda task.
2323
//!
2424
//! ```rust,no_run
25-
//! use lambda_http::{lambda, Request, IntoResponse};
25+
//! use lambda_http::{lambda::{lambda, Context}, Request, IntoResponse};
2626
//!
2727
//! type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
2828
//!
2929
//! #[lambda(http)]
3030
//! #[tokio::main]
31-
//! async fn main(_: Request) -> Result<impl IntoResponse, Error> {
31+
//! async fn main(_: Request, _: Context) -> Result<impl IntoResponse, Error> {
3232
//! Ok("👋 world!")
3333
//! }
3434
//! ```
@@ -48,7 +48,7 @@
4848
//! async fn main() -> Result<(), Error> {
4949
//! // initialize dependencies once here for the lifetime of your
5050
//! // lambda task
51-
//! lambda::run(handler(|request| async { Ok("👋 world!") })).await?;
51+
//! lambda::run(handler(|request, context| async { Ok("👋 world!") })).await?;
5252
//! Ok(())
5353
//! }
5454
//!
@@ -60,7 +60,7 @@
6060
//! with the [`RequestExt`](trait.RequestExt.html) trait.
6161
//!
6262
//! ```rust,no_run
63-
//! use lambda_http::{handler, lambda, IntoResponse, Request, RequestExt};
63+
//! use lambda_http::{handler, lambda::{self, Context}, IntoResponse, Request, RequestExt};
6464
//!
6565
//! type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
6666
//!
@@ -72,6 +72,7 @@
7272
//!
7373
//! async fn hello(
7474
//! request: Request,
75+
//! _: Context
7576
//! ) -> Result<impl IntoResponse, Error> {
7677
//! Ok(format!(
7778
//! "hello {}",
@@ -90,7 +91,7 @@ extern crate maplit;
9091

9192
pub use http::{self, Response};
9293
use lambda::Handler as LambdaHandler;
93-
pub use lambda::{self};
94+
pub use lambda::{self, Context};
9495
pub use lambda_attributes::lambda;
9596

9697
mod body;
@@ -103,7 +104,7 @@ use crate::{request::LambdaRequest, response::LambdaResponse};
103104
use std::{
104105
future::Future,
105106
pin::Pin,
106-
task::{Context, Poll},
107+
task::{Context as TaskContext, Poll},
107108
};
108109

109110
/// Error type that lambdas may result in
@@ -123,7 +124,7 @@ pub trait Handler: Sized {
123124
/// The type of Future this Handler will return
124125
type Fut: Future<Output = Result<Self::Response, Self::Error>> + 'static;
125126
/// Function used to execute handler behavior
126-
fn call(&mut self, event: Request) -> Self::Fut;
127+
fn call(&mut self, event: Request, context: Context) -> Self::Fut;
127128
}
128129

129130
/// Adapts a [`Handler`](trait.Handler.html) to the `lambda::run` interface
@@ -134,15 +135,15 @@ pub fn handler<H: Handler>(handler: H) -> Adapter<H> {
134135
/// An implementation of `Handler` for a given closure return a `Future` representing the computed response
135136
impl<F, R, Fut> Handler for F
136137
where
137-
F: FnMut(Request) -> Fut,
138+
F: FnMut(Request, Context) -> Fut,
138139
R: IntoResponse,
139140
Fut: Future<Output = Result<R, Error>> + Send + 'static,
140141
{
141142
type Response = R;
142143
type Error = Error;
143144
type Fut = Fut;
144-
fn call(&mut self, event: Request) -> Self::Fut {
145-
(*self)(event)
145+
fn call(&mut self, event: Request, context: Context) -> Self::Fut {
146+
(*self)(event, context)
146147
}
147148
}
148149

@@ -157,7 +158,7 @@ where
157158
R: IntoResponse,
158159
{
159160
type Output = Result<LambdaResponse, E>;
160-
fn poll(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Self::Output> {
161+
fn poll(mut self: Pin<&mut Self>, cx: &mut TaskContext) -> Poll<Self::Output> {
161162
match self.fut.as_mut().poll(cx) {
162163
Poll::Ready(result) => {
163164
Poll::Ready(result.map(|resp| LambdaResponse::from_response(self.is_alb, resp.into_response())))
@@ -182,17 +183,17 @@ impl<H: Handler> Handler for Adapter<H> {
182183
type Response = H::Response;
183184
type Error = H::Error;
184185
type Fut = H::Fut;
185-
fn call(&mut self, event: Request) -> Self::Fut {
186-
self.handler.call(event)
186+
fn call(&mut self, event: Request, context: Context) -> Self::Fut {
187+
self.handler.call(event, context)
187188
}
188189
}
189190

190191
impl<H: Handler> LambdaHandler<LambdaRequest<'_>, LambdaResponse> for Adapter<H> {
191192
type Error = H::Error;
192193
type Fut = TransformResponse<H::Response, Self::Error>;
193-
fn call(&mut self, event: LambdaRequest<'_>) -> Self::Fut {
194+
fn call(&mut self, event: LambdaRequest<'_>, context: Context) -> Self::Fut {
194195
let is_alb = event.is_alb();
195-
let fut = Box::pin(self.handler.call(event.into()));
196+
let fut = Box::pin(self.handler.call(event.into(), context));
196197
TransformResponse { is_alb, fut }
197198
}
198199
}

lambda/examples/hello-with-ctx.rs

Lines changed: 0 additions & 10 deletions
This file was deleted.

lambda/examples/hello-without-macro.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
use lambda::handler_fn;
1+
use lambda::{handler_fn, Context};
22
use serde_json::Value;
33

44
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
@@ -10,6 +10,6 @@ async fn main() -> Result<(), Error> {
1010
Ok(())
1111
}
1212

13-
async fn func(event: Value) -> Result<Value, Error> {
13+
async fn func(event: Value, _: Context) -> Result<Value, Error> {
1414
Ok(event)
1515
}

lambda/examples/hello.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
use lambda::lambda;
1+
use lambda::{lambda, Context};
22
use serde_json::Value;
33

44
type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
55

66
#[lambda]
77
#[tokio::main]
8-
async fn main(event: Value) -> Result<Value, Error> {
8+
async fn main(event: Value, _: Context) -> Result<Value, Error> {
99
Ok(event)
1010
}

lambda/src/lib.rs

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,23 @@
1717
//! to the the `lambda::run` function, which launches and runs the Lambda runtime.
1818
//!
1919
//! An asynchronous function annotated with the `#[lambda]` attribute must
20-
//! accept an argument of type `A` which implements [`serde::Deserialize`] and
20+
//! accept an argument of type `A` which implements [`serde::Deserialize`], a [`lambda::Context`] and
2121
//! return a `Result<B, E>`, where `B` implements [`serde::Serializable`]. `E` is
2222
//! any type that implements `Into<Box<dyn std::error::Error + Send + Sync + 'static>>`.
2323
//!
24-
//! Optionally, the `#[lambda]` annotated function can accept an argument
25-
//! of [`lambda::LambdaCtx`].
26-
//!
2724
//! ```no_run
28-
//! use lambda::{lambda};
25+
//! use lambda::{lambda, Context};
2926
//! use serde_json::Value;
3027
//!
3128
//! type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
3229
//!
3330
//! #[lambda]
3431
//! #[tokio::main]
35-
//! async fn main(event: Value) -> Result<Value, Error> {
32+
//! async fn main(event: Value, _: Context) -> Result<Value, Error> {
3633
//! Ok(event)
3734
//! }
3835
//! ```
39-
pub use crate::types::LambdaCtx;
36+
pub use crate::types::Context;
4037
use client::Client;
4138
use futures::stream::{Stream, StreamExt};
4239
use genawaiter::{sync::gen, yield_};
@@ -93,7 +90,7 @@ impl Config {
9390
}
9491

9592
tokio::task_local! {
96-
pub static INVOCATION_CTX: types::LambdaCtx;
93+
pub static INVOCATION_CTX: types::Context;
9794
}
9895

9996
/// A trait describing an asynchronous function `A` to `B.
@@ -102,8 +99,8 @@ pub trait Handler<A, B> {
10299
type Error;
103100
/// The future response value of this handler.
104101
type Fut: Future<Output = Result<B, Self::Error>>;
105-
/// Process the incoming event and return the response asynchronously.
106-
fn call(&mut self, event: A) -> Self::Fut;
102+
/// Process the incoming event and `Context` then return the response asynchronously.
103+
fn call(&mut self, event: A, context: Context) -> Self::Fut;
107104
}
108105

109106
/// Returns a new `HandlerFn` with the given closure.
@@ -119,15 +116,14 @@ pub struct HandlerFn<F> {
119116

120117
impl<F, A, B, Error, Fut> Handler<A, B> for HandlerFn<F>
121118
where
122-
F: Fn(A) -> Fut,
119+
F: Fn(A, Context) -> Fut,
123120
Fut: Future<Output = Result<B, Error>> + Send,
124121
Error: Into<Error> + fmt::Debug,
125122
{
126123
type Error = Error;
127124
type Fut = Fut;
128-
fn call(&mut self, req: A) -> Self::Fut {
129-
// we pass along the context here
130-
(self.f)(req)
125+
fn call(&mut self, req: A, ctx: Context) -> Self::Fut {
126+
(self.f)(req, ctx)
131127
}
132128
}
133129

@@ -136,7 +132,7 @@ where
136132
///
137133
/// # Example
138134
/// ```no_run
139-
/// use lambda::{handler_fn};
135+
/// use lambda::{handler_fn, Context};
140136
/// use serde_json::Value;
141137
///
142138
/// type Error = Box<dyn std::error::Error + Send + Sync + 'static>;
@@ -148,7 +144,7 @@ where
148144
/// Ok(())
149145
/// }
150146
///
151-
/// async fn func(event: Value) -> Result<Value, Error> {
147+
/// async fn func(event: Value, _: Context) -> Result<Value, Error> {
152148
/// Ok(event)
153149
/// }
154150
/// ```
@@ -211,12 +207,12 @@ where
211207
let event = event?;
212208
let (parts, body) = event.into_parts();
213209

214-
let ctx: LambdaCtx = LambdaCtx::try_from(parts.headers)?;
210+
let ctx: Context = Context::try_from(parts.headers)?;
215211
let body = hyper::body::to_bytes(body).await?;
216212
let body = serde_json::from_slice(&body)?;
217213

218214
let request_id = &ctx.request_id.clone();
219-
let f = INVOCATION_CTX.scope(ctx, { handler.call(body) });
215+
let f = INVOCATION_CTX.scope(ctx.clone(), handler.call(body, ctx));
220216

221217
let req = match f.await {
222218
Ok(res) => EventCompletionRequest { request_id, body: res }.into_req()?,

0 commit comments

Comments
 (0)