diff --git a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift index 62a51892..fc3611ba 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaHandler.swift @@ -62,9 +62,12 @@ public protocol SimpleLambdaHandler { func decode(buffer: ByteBuffer) throws -> Event } +@usableFromInline final class CodableSimpleLambdaHandler: ByteBufferLambdaHandler { - private let handler: Underlying - private var outputBuffer: ByteBuffer + @usableFromInline + let handler: Underlying + @usableFromInline + private(set) var outputBuffer: ByteBuffer @inlinable static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { @@ -181,9 +184,12 @@ public protocol LambdaHandler { func decode(buffer: ByteBuffer) throws -> Event } +@usableFromInline final class CodableLambdaHandler: ByteBufferLambdaHandler { - private let handler: Underlying - private var outputBuffer: ByteBuffer + @usableFromInline + let handler: Underlying + @usableFromInline + private(set) var outputBuffer: ByteBuffer @inlinable static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { @@ -329,9 +335,12 @@ extension EventLoopLambdaHandler where Output == Void { public func encode(value: Output, into buffer: inout ByteBuffer) throws {} } -internal final class CodableEventLoopLambdaHandler: ByteBufferLambdaHandler { - private let handler: Underlying - private var outputBuffer: ByteBuffer +@usableFromInline +final class CodableEventLoopLambdaHandler: ByteBufferLambdaHandler { + @usableFromInline + let handler: Underlying + @usableFromInline + private(set) var outputBuffer: ByteBuffer @inlinable static func makeHandler(context: LambdaInitializationContext) -> EventLoopFuture { diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift index 31136d8b..3d4f55b3 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRunner.swift @@ -33,7 +33,7 @@ internal final class LambdaRunner { /// Run the user provided initializer. This *must* only be called once. /// /// - Returns: An `EventLoopFuture` fulfilled with the outcome of the initialization. - func initialize(handlerType: (some ByteBufferLambdaHandler).Type, logger: Logger, terminator: LambdaTerminator) -> EventLoopFuture { + func initialize(handlerType: Handler.Type, logger: Logger, terminator: LambdaTerminator) -> EventLoopFuture { logger.debug("initializing lambda") // 1. create the handler from the factory // 2. report initialization error if one occurred @@ -45,7 +45,6 @@ internal final class LambdaRunner { ) return handlerType.makeHandler(context: context) - .map { $0 as any ByteBufferLambdaHandler } // Hopping back to "our" EventLoop is important in case the factory returns a future // that originated from a foreign EventLoop/EventLoopGroup. // This can happen if the factory uses a library (let's say a database client) that manages its own threads/loops @@ -60,7 +59,7 @@ internal final class LambdaRunner { } } - func run(handler: any ByteBufferLambdaHandler, logger: Logger) -> EventLoopFuture { + func run(handler: some ByteBufferLambdaHandler, logger: Logger) -> EventLoopFuture { logger.debug("lambda invocation sequence starting") // 1. request invocation from lambda runtime engine self.isGettingNextInvocation = true diff --git a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift index 689559d5..96b77489 100644 --- a/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift +++ b/Sources/AWSLambdaRuntimeCore/LambdaRuntime.swift @@ -19,14 +19,12 @@ import NIOCore /// `LambdaRuntime` manages the Lambda process lifecycle. /// /// Use this API, if you build a higher level web framework which shall be able to run inside the Lambda environment. -public final class LambdaRuntime { +public final class LambdaRuntime { private let eventLoop: EventLoop private let shutdownPromise: EventLoopPromise private let logger: Logger private let configuration: LambdaConfiguration - private let handlerType: any ByteBufferLambdaHandler.Type - private var state = State.idle { willSet { self.eventLoop.assertInEventLoop() @@ -34,52 +32,21 @@ public final class LambdaRuntime { } } - /// Create a new `LambdaRuntime`. - /// - /// - parameters: - /// - handlerType: The ``SimpleLambdaHandler`` type the `LambdaRuntime` shall create and manage. - /// - eventLoop: An `EventLoop` to run the Lambda on. - /// - logger: A `Logger` to log the Lambda events. - public convenience init(_ handlerType: Handler.Type, eventLoop: EventLoop, logger: Logger) { - self.init(CodableSimpleLambdaHandler.self, eventLoop: eventLoop, logger: logger) - } - - /// Create a new `LambdaRuntime`. - /// - /// - parameters: - /// - handlerType: The ``LambdaHandler`` type the `LambdaRuntime` shall create and manage. - /// - eventLoop: An `EventLoop` to run the Lambda on. - /// - logger: A `Logger` to log the Lambda events. - public convenience init(_ handlerType: Handler.Type, eventLoop: EventLoop, logger: Logger) { - self.init(CodableLambdaHandler.self, eventLoop: eventLoop, logger: logger) - } - - /// Create a new `LambdaRuntime`. - /// - /// - parameters: - /// - handlerType: The ``EventLoopLambdaHandler`` type the `LambdaRuntime` shall create and manage. - /// - eventLoop: An `EventLoop` to run the Lambda on. - /// - logger: A `Logger` to log the Lambda events. - public convenience init(_ handlerType: Handler.Type, eventLoop: EventLoop, logger: Logger) { - self.init(CodableEventLoopLambdaHandler.self, eventLoop: eventLoop, logger: logger) - } - /// Create a new `LambdaRuntime`. /// /// - parameters: /// - handlerType: The ``ByteBufferLambdaHandler`` type the `LambdaRuntime` shall create and manage. /// - eventLoop: An `EventLoop` to run the Lambda on. /// - logger: A `Logger` to log the Lambda events. - public convenience init(_ handlerType: (some ByteBufferLambdaHandler).Type, eventLoop: EventLoop, logger: Logger) { + public convenience init(_ handlerType: Handler.Type, eventLoop: EventLoop, logger: Logger) { self.init(handlerType: handlerType, eventLoop: eventLoop, logger: logger, configuration: .init()) } - init(handlerType: (some ByteBufferLambdaHandler).Type, eventLoop: EventLoop, logger: Logger, configuration: LambdaConfiguration) { + init(handlerType: Handler.Type, eventLoop: EventLoop, logger: Logger, configuration: LambdaConfiguration) { self.eventLoop = eventLoop self.shutdownPromise = eventLoop.makePromise(of: Int.self) self.logger = logger self.configuration = configuration - self.handlerType = handlerType } deinit { @@ -118,7 +85,7 @@ public final class LambdaRuntime { let terminator = LambdaTerminator() let runner = LambdaRunner(eventLoop: self.eventLoop, configuration: self.configuration) - let startupFuture = runner.initialize(handlerType: self.handlerType, logger: logger, terminator: terminator) + let startupFuture = runner.initialize(handlerType: Handler.self, logger: logger, terminator: terminator) startupFuture.flatMap { handler -> EventLoopFuture> in // after the startup future has succeeded, we have a handler that we can use // to `run` the lambda. @@ -229,5 +196,40 @@ public final class LambdaRuntime { } } +public enum LambdaRuntimeFactory { + /// Create a new `LambdaRuntime`. + /// + /// - parameters: + /// - handlerType: The ``SimpleLambdaHandler`` type the `LambdaRuntime` shall create and manage. + /// - eventLoop: An `EventLoop` to run the Lambda on. + /// - logger: A `Logger` to log the Lambda events. + @inlinable + public static func makeRuntime(_ handlerType: H.Type, eventLoop: any EventLoop, logger: Logger) -> LambdaRuntime { + LambdaRuntime>(CodableSimpleLambdaHandler.self, eventLoop: eventLoop, logger: logger) + } + + /// Create a new `LambdaRuntime`. + /// + /// - parameters: + /// - handlerType: The ``LambdaHandler`` type the `LambdaRuntime` shall create and manage. + /// - eventLoop: An `EventLoop` to run the Lambda on. + /// - logger: A `Logger` to log the Lambda events. + @inlinable + public static func makeRuntime(_ handlerType: H.Type, eventLoop: any EventLoop, logger: Logger) -> LambdaRuntime { + LambdaRuntime>(CodableLambdaHandler.self, eventLoop: eventLoop, logger: logger) + } + + /// Create a new `LambdaRuntime`. + /// + /// - parameters: + /// - handlerType: The ``EventLoopLambdaHandler`` type the `LambdaRuntime` shall create and manage. + /// - eventLoop: An `EventLoop` to run the Lambda on. + /// - logger: A `Logger` to log the Lambda events. + @inlinable + public static func makeRuntime(_ handlerType: H.Type, eventLoop: any EventLoop, logger: Logger) -> LambdaRuntime { + LambdaRuntime>(CodableEventLoopLambdaHandler.self, eventLoop: eventLoop, logger: logger) + } +} + /// This is safe since lambda runtime synchronizes by dispatching all methods to a single `EventLoop` extension LambdaRuntime: @unchecked Sendable {} diff --git a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift index 578048cf..39764ccc 100644 --- a/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift +++ b/Tests/AWSLambdaRuntimeCoreTests/LambdaRuntimeTest.swift @@ -29,7 +29,7 @@ class LambdaRuntimeTest: XCTestCase { let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(StartupErrorHandler.self, eventLoop: eventLoop, logger: logger) + let runtime = LambdaRuntimeFactory.makeRuntime(StartupErrorHandler.self, eventLoop: eventLoop, logger: logger) // eventLoop.submit in this case returns an EventLoopFuture> // which is why we need `wait().wait()` @@ -51,7 +51,7 @@ class LambdaRuntimeTest: XCTestCase { let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(EchoHandler.self, eventLoop: eventLoop, logger: logger) + let runtime = LambdaRuntimeFactory.makeRuntime(EchoHandler.self, eventLoop: eventLoop, logger: logger) XCTAssertNoThrow(_ = try eventLoop.flatSubmit { runtime.start() }.wait()) XCTAssertThrowsError(_ = try runtime.shutdownFuture.wait()) { @@ -98,7 +98,7 @@ class LambdaRuntimeTest: XCTestCase { let eventLoop = eventLoopGroup.next() let logger = Logger(label: "TestLogger") - let runtime = LambdaRuntime(ShutdownErrorHandler.self, eventLoop: eventLoop, logger: logger) + let runtime = LambdaRuntimeFactory.makeRuntime(ShutdownErrorHandler.self, eventLoop: eventLoop, logger: logger) XCTAssertNoThrow(try eventLoop.flatSubmit { runtime.start() }.wait()) XCTAssertThrowsError(try runtime.shutdownFuture.wait()) { error in