diff --git a/build.zig b/build.zig index 3d85ae1..55cfe19 100644 --- a/build.zig +++ b/build.zig @@ -59,7 +59,7 @@ pub fn build(b: *std.Build) !void { const universal_lambda = @import("src/universal_lambda_build.zig"); universal_lambda.module_root = b.build_root.path; - // re-expose modules downstream + // re-expose flexilib-interface and aws_lambda modules downstream const flexilib_dep = b.dependency("flexilib", .{ .target = target, .optimize = optimize, @@ -70,7 +70,18 @@ pub fn build(b: *std.Build) !void { .target = target, .optimize = optimize, }); + const aws_lambda_dep = b.dependency("lambda-zig", .{ + .target = target, + .optimize = optimize, + }); + const aws_lambda_module = aws_lambda_dep.module("lambda_runtime"); + _ = b.addModule("aws_lambda_runtime", .{ + .root_source_file = aws_lambda_module.root_source_file, + .target = target, + .optimize = optimize, + }); + // Expose our own modules downstream _ = b.addModule("universal_lambda_interface", .{ .root_source_file = b.path("src/interface.zig"), .target = target, diff --git a/src/awslambda.zig b/src/awslambda.zig index 756115b..e3263c3 100644 --- a/src/awslambda.zig +++ b/src/awslambda.zig @@ -1,6 +1,83 @@ const std = @import("std"); -// const lambda_zig = @import("lambda-zig"); +const interface = @import("universal_lambda_interface"); +const lambda_zig = @import("aws_lambda_runtime"); -pub fn run() !void { - // lambda_zig.run(); +const log = std.log.scoped(.awslambda); + +threadlocal var universal_handler: interface.HandlerFn = undefined; + +/// This is called by the aws lambda runtime (our import), and must +/// call the universal handler (our downstream client). The main job here +/// is to convert signatures, ore more specifically, build out the context +/// that the universal handler is expecting +fn lambdaHandler(arena: std.mem.Allocator, event_data: []const u8) ![]const u8 { + const response_body: std.ArrayList(u8) = std.ArrayList(u8).init(arena); + // Marshal lambda_zig data into a context + // TODO: Maybe this should parse API Gateway data into a proper response + // TODO: environment variables -> Headers? + var response = interface.Response{ + .allocator = arena, + .headers = &.{}, + .body = response_body, + .request = .{ + .headers = &.{}, + }, + }; + + // Call back to the handler that we were given + const result = universal_handler(arena, event_data, &response); + + // TODO: If our universal handler writes to the response body, we should + // handle that in a consistent way + + // Return result from our handler back to AWS lambda via the lambda module + return result; +} + +// AWS lambda zig handler: const HandlerFn = *const fn (std.mem.Allocator, []const u8) anyerror![]const u8; +// Our handler: pub const HandlerFn = *const fn (std.mem.Allocator, []const u8, Context) anyerror![]const u8; +pub fn run(allocator: ?std.mem.Allocator, event_handler: interface.HandlerFn) !u8 { + universal_handler = event_handler; + + // pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { // TODO: remove inferred error set? + try lambda_zig.run(allocator, lambdaHandler); + return 0; +} + +fn handler(allocator: std.mem.Allocator, event_data: []const u8) ![]const u8 { + _ = allocator; + return event_data; +} + +//////////////////////////////////////////////////////////////////////// +// All code below this line is for testing +//////////////////////////////////////////////////////////////////////// +test { + std.testing.refAllDecls(lambda_zig); +} +test "basic request" { + // std.testing.log_level = .debug; + const allocator = std.testing.allocator; + const request = + \\{"foo": "bar", "baz": "qux"} + ; + + // This is what's actually coming back. Is this right? + const expected_response = + \\nothing but net + ; + const TestHandler = struct { + pub fn handler(alloc: std.mem.Allocator, event_data: []const u8, context: interface.Context) ![]const u8 { + _ = alloc; + _ = event_data; + _ = context; + log.debug("in handler", .{}); + return "nothing but net"; + } + }; + universal_handler = TestHandler.handler; + const lambda_response = try lambda_zig.test_lambda_request(allocator, request, 1, lambdaHandler); + defer lambda_zig.deinit(); + defer allocator.free(lambda_response); + try std.testing.expectEqualStrings(expected_response, lambda_response); } diff --git a/src/universal_lambda_build.zig b/src/universal_lambda_build.zig index 258e78e..6b80f8f 100644 --- a/src/universal_lambda_build.zig +++ b/src/universal_lambda_build.zig @@ -35,6 +35,7 @@ pub fn configureBuild(b: *std.Build, cs: *std.Build.Step.Compile, universal_lamb /// * universal_lambda_handler pub fn addImports(b: *std.Build, cs: *std.Build.Step.Compile, universal_lambda_zig_dep: ?*std.Build.Dependency) void { const Modules = struct { + aws_lambda_runtime: *std.Build.Module, flexilib_interface: *std.Build.Module, universal_lambda_interface: *std.Build.Module, universal_lambda_handler: *std.Build.Module, @@ -43,6 +44,7 @@ pub fn addImports(b: *std.Build, cs: *std.Build.Step.Compile, universal_lambda_z const modules = if (universal_lambda_zig_dep) |d| Modules{ + .aws_lambda_runtime = d.module("aws_lambda_runtime"), .flexilib_interface = d.module("flexilib-interface"), .universal_lambda_interface = d.module("universal_lambda_interface"), .universal_lambda_handler = d.module("universal_lambda_handler"), @@ -50,6 +52,7 @@ pub fn addImports(b: *std.Build, cs: *std.Build.Step.Compile, universal_lambda_z } else Modules{ + .aws_lambda_runtime = b.modules.get("aws_lambda_runtime").?, .flexilib_interface = b.modules.get("flexilib-interface").?, .universal_lambda_interface = b.modules.get("universal_lambda_interface").?, .universal_lambda_handler = b.modules.get("universal_lambda_handler").?, @@ -60,11 +63,13 @@ pub fn addImports(b: *std.Build, cs: *std.Build.Step.Compile, universal_lambda_z cs.root_module.addImport("flexilib-interface", modules.flexilib_interface); cs.root_module.addImport("universal_lambda_interface", modules.universal_lambda_interface); cs.root_module.addImport("universal_lambda_handler", modules.universal_lambda_handler); + cs.root_module.addImport("aws_lambda_runtime", modules.aws_lambda_runtime); // universal lambda handler also needs these imports modules.universal_lambda_handler.addImport("universal_lambda_interface", modules.universal_lambda_interface); modules.universal_lambda_handler.addImport("flexilib-interface", modules.flexilib_interface); modules.universal_lambda_handler.addImport("universal_lambda_build_options", modules.universal_lambda_build_options); + modules.universal_lambda_handler.addImport("aws_lambda_runtime", modules.aws_lambda_runtime); return; }