@@ -26,7 +26,14 @@ import NIOCore
2626/// level protocols ``EventLoopLambdaHandler`` and
2727/// ``ByteBufferLambdaHandler``.
2828@available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
29- public protocol LambdaHandler : EventLoopLambdaHandler {
29+ public protocol LambdaHandler {
30+ /// The lambda functions input. In most cases this should be `Codable`. If your event originates from an
31+ /// AWS service, have a look at [AWSLambdaEvents](https://github.com/swift-server/swift-aws-lambda-events),
32+ /// which provides a number of commonly used AWS Event implementations.
33+ associatedtype Event
34+ /// The lambda functions output. Can be `Void`.
35+ associatedtype Output
36+
3037 /// The Lambda initialization method.
3138 /// Use this method to initialize resources that will be used in every request.
3239 ///
@@ -44,18 +51,38 @@ public protocol LambdaHandler: EventLoopLambdaHandler {
4451 ///
4552 /// - Returns: A Lambda result ot type `Output`.
4653 func handle( _ event: Event , context: LambdaContext ) async throws -> Output
54+
55+ /// Encode a response of type ``Output`` to `ByteBuffer`.
56+ /// Concrete Lambda handlers implement this method to provide coding functionality.
57+ /// - parameters:
58+ /// - allocator: A `ByteBufferAllocator` to help allocate the `ByteBuffer`.
59+ /// - value: Response of type ``Output``.
60+ ///
61+ /// - Returns: A `ByteBuffer` with the encoded version of the `value`.
62+ func encode( allocator: ByteBufferAllocator , value: Output ) throws -> ByteBuffer ?
63+
64+ /// Decode a `ByteBuffer` to a request or event of type ``Event``.
65+ /// Concrete Lambda handlers implement this method to provide coding functionality.
66+ ///
67+ /// - parameters:
68+ /// - buffer: The `ByteBuffer` to decode.
69+ ///
70+ /// - Returns: A request or event of type ``Event``.
71+ func decode( buffer: ByteBuffer ) throws -> Event
4772}
4873
4974@available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
5075extension LambdaHandler {
51- public static func makeHandler( context: LambdaInitializationContext ) -> EventLoopFuture < Self > {
76+ // FIXME: why public?
77+ public static func makeHandler( context: LambdaInitializationContext ) -> EventLoopFuture < ( ByteBuffer , LambdaContext ) -> EventLoopFuture < ByteBuffer ? > > {
5278 let promise = context. eventLoop. makePromise ( of: Self . self)
5379 promise. completeWithTask {
5480 try await Self ( context: context)
5581 }
56- return promise. futureResult
82+ return promise. futureResult. map { $0 . handle ( buffer : context : ) }
5783 }
5884
85+ // FIXME: why public?
5986 public func handle( _ event: Event , context: LambdaContext ) -> EventLoopFuture < Output > {
6087 let promise = context. eventLoop. makePromise ( of: Output . self)
6188 // using an unchecked sendable wrapper for the handler
@@ -66,6 +93,68 @@ extension LambdaHandler {
6693 }
6794 return promise. futureResult
6895 }
96+
97+ /// Driver for `ByteBuffer` -> ``Event`` decoding and ``Output`` -> `ByteBuffer` encoding
98+ @inlinable
99+ internal func handle( buffer: ByteBuffer , context: LambdaContext ) -> EventLoopFuture < ByteBuffer ? > {
100+ let input : Event
101+ do {
102+ input = try self . decode ( buffer: buffer)
103+ } catch {
104+ return context. eventLoop. makeFailedFuture ( CodecError . requestDecoding ( error) )
105+ }
106+
107+ return self . handle ( input, context: context) . flatMapThrowing { output in
108+ do {
109+ return try self . encode ( allocator: context. allocator, value: output)
110+ } catch {
111+ throw CodecError . responseEncoding ( error)
112+ }
113+ }
114+ }
115+ }
116+
117+ /// Implementation of `ByteBuffer` to `Void` decoding.
118+ @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
119+ extension LambdaHandler where Output == Void {
120+ @inlinable
121+ public func encode( allocator: ByteBufferAllocator , value: Void ) throws -> ByteBuffer ? {
122+ nil
123+ }
124+ }
125+
126+ @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
127+ extension LambdaHandler where Event == String {
128+ @inlinable
129+ public func decode( buffer: ByteBuffer ) throws -> String {
130+ guard let value = buffer. getString ( at: buffer. readerIndex, length: buffer. readableBytes) else {
131+ throw CodecError . invalidString
132+ }
133+ return value
134+ }
135+ }
136+
137+ @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
138+ extension LambdaHandler where Output == String {
139+ @inlinable
140+ public func encode( allocator: ByteBufferAllocator , value: String ) throws -> ByteBuffer ? {
141+ allocator. buffer ( string: value)
142+ }
143+ }
144+
145+ @available ( macOS 12 , iOS 15 , tvOS 15 , watchOS 8 , * )
146+ extension LambdaHandler {
147+ /// Initializes and runs the Lambda function.
148+ ///
149+ /// If you precede your ``ByteBufferLambdaHandler`` conformer's declaration with the
150+ /// [@main](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626)
151+ /// attribute, the system calls the conformer's `main()` method to launch the lambda function.
152+ ///
153+ /// The lambda runtime provides a default implementation of the method that manages the launch
154+ /// process.
155+ public static func main( ) {
156+ _ = Lambda . run ( configuration: . init( ) , handlerType: Self . self)
157+ }
69158}
70159
71160/// unchecked sendable wrapper for the handler
@@ -100,14 +189,22 @@ fileprivate struct UncheckedSendableHandler<Underlying: LambdaHandler, Event, Ou
100189/// as the core runtime engine, making the processing faster but requires more care from the
101190/// implementation to never block the `EventLoop`. Implement this protocol only in performance
102191/// critical situations and implement ``LambdaHandler`` in all other circumstances.
103- public protocol EventLoopLambdaHandler : ByteBufferLambdaHandler {
192+ public protocol EventLoopLambdaHandler {
104193 /// The lambda functions input. In most cases this should be `Codable`. If your event originates from an
105194 /// AWS service, have a look at [AWSLambdaEvents](https://github.com/swift-server/swift-aws-lambda-events),
106195 /// which provides a number of commonly used AWS Event implementations.
107196 associatedtype Event
108197 /// The lambda functions output. Can be `Void`.
109198 associatedtype Output
110199
200+ /// Create a Lambda handler for the runtime.
201+ ///
202+ /// Use this to initialize all your resources that you want to cache between invocations. This could be database
203+ /// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance
204+ /// to `EventLoopGroup` when initializing NIO dependencies. This will improve overall performance, as it
205+ /// minimizes thread hopping.
206+ static func makeHandler( context: LambdaInitializationContext ) -> EventLoopFuture < Self >
207+
111208 /// The Lambda handling method.
112209 /// Concrete Lambda handlers implement this method to provide the Lambda functionality.
113210 ///
@@ -139,12 +236,17 @@ public protocol EventLoopLambdaHandler: ByteBufferLambdaHandler {
139236}
140237
141238extension EventLoopLambdaHandler {
239+ @inlinable
240+ internal static func makeHandler( context: LambdaInitializationContext ) -> EventLoopFuture < ( ByteBuffer , LambdaContext ) -> EventLoopFuture < ByteBuffer ? > > {
241+ Self . makeHandler ( context: context) . map { $0. handle ( buffer: context: ) }
242+ }
243+
142244 /// Driver for `ByteBuffer` -> ``Event`` decoding and ``Output`` -> `ByteBuffer` encoding
143245 @inlinable
144- public func handle( _ event : ByteBuffer , context: LambdaContext ) -> EventLoopFuture < ByteBuffer ? > {
246+ internal func handle( buffer : ByteBuffer , context: LambdaContext ) -> EventLoopFuture < ByteBuffer ? > {
145247 let input : Event
146248 do {
147- input = try self . decode ( buffer: event )
249+ input = try self . decode ( buffer: buffer )
148250 } catch {
149251 return context. eventLoop. makeFailedFuture ( CodecError . requestDecoding ( error) )
150252 }
@@ -167,6 +269,20 @@ extension EventLoopLambdaHandler where Output == Void {
167269 }
168270}
169271
272+ extension EventLoopLambdaHandler {
273+ /// Initializes and runs the Lambda function.
274+ ///
275+ /// If you precede your ``ByteBufferLambdaHandler`` conformer's declaration with the
276+ /// [@main](https://docs.swift.org/swift-book/ReferenceManual/Attributes.html#ID626)
277+ /// attribute, the system calls the conformer's `main()` method to launch the lambda function.
278+ ///
279+ /// The lambda runtime provides a default implementation of the method that manages the launch
280+ /// process.
281+ public static func main( ) {
282+ _ = Lambda . run ( configuration: . init( ) , handlerType: Self . self)
283+ }
284+ }
285+
170286// MARK: - ByteBufferLambdaHandler
171287
172288/// An `EventLoopFuture` based processing protocol for a Lambda that takes a `ByteBuffer` and returns
@@ -176,7 +292,7 @@ extension EventLoopLambdaHandler where Output == Void {
176292/// ``LambdaHandler`` based APIs.
177293/// Most users are not expected to use this protocol.
178294public protocol ByteBufferLambdaHandler {
179- /// Create your Lambda handler for the runtime.
295+ /// Create a Lambda handler for the runtime.
180296 ///
181297 /// Use this to initialize all your resources that you want to cache between invocations. This could be database
182298 /// connections and HTTP clients for example. It is encouraged to use the given `EventLoop`'s conformance
@@ -193,7 +309,14 @@ public protocol ByteBufferLambdaHandler {
193309 ///
194310 /// - Returns: An `EventLoopFuture` to report the result of the Lambda back to the runtime engine.
195311 /// The `EventLoopFuture` should be completed with either a response encoded as `ByteBuffer` or an `Error`.
196- func handle( _ event: ByteBuffer , context: LambdaContext ) -> EventLoopFuture < ByteBuffer ? >
312+ func handle( _ buffer: ByteBuffer , context: LambdaContext ) -> EventLoopFuture < ByteBuffer ? >
313+ }
314+
315+ extension ByteBufferLambdaHandler {
316+ @inlinable
317+ internal static func makeHandler( context: LambdaInitializationContext ) -> EventLoopFuture < ( ByteBuffer , LambdaContext ) -> EventLoopFuture < ByteBuffer ? > > {
318+ Self . makeHandler ( context: context) . map { $0. handle ( _: context: ) }
319+ }
197320}
198321
199322extension ByteBufferLambdaHandler {
@@ -210,8 +333,12 @@ extension ByteBufferLambdaHandler {
210333 }
211334}
212335
336+ // MARK: - Other
337+
213338@usableFromInline
214339enum CodecError : Error {
215340 case requestDecoding( Error )
216341 case responseEncoding( Error )
342+ case invalidString
217343}
344+
0 commit comments