diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3d9eccc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +core +zig-cache +zig-out diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a339f72 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Emil Lerch + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/build.zig b/build.zig new file mode 100644 index 0000000..29a307a --- /dev/null +++ b/build.zig @@ -0,0 +1,47 @@ +const std = @import("std"); + +// Although this function looks imperative, note that its job is to +// declaratively construct a build graph that will be executed by an external +// runner. +pub fn build(b: *std.Build) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard optimization options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not + // set a preferred release mode, allowing the user to decide how to optimize. + const optimize = b.standardOptimizeOption(.{}); + + const lib = b.addStaticLibrary(.{ + .name = "universal-lambda-zig", + // In this case the main source file is merely a path, however, in more + // complicated build scripts, this could be a generated file. + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + // This declares intent for the library to be installed into the standard + // location when the user invokes the "install" step (the default step when + // running `zig build`). + b.installArtifact(lib); + + // Creates a step for unit testing. This only builds the test executable + // but does not run it. + const main_tests = b.addTest(.{ + .root_source_file = .{ .path = "src/main.zig" }, + .target = target, + .optimize = optimize, + }); + + const run_main_tests = b.addRunArtifact(main_tests); + + // This creates a build step. It will be visible in the `zig build --help` menu, + // and can be selected like this: `zig build test` + // This will evaluate the `test` step rather than the default, which is "install". + const test_step = b.step("test", "Run library tests"); + test_step.dependOn(&run_main_tests.step); +} diff --git a/src/universal_lambda.zig b/src/universal_lambda.zig new file mode 100644 index 0000000..7296dc2 --- /dev/null +++ b/src/universal_lambda.zig @@ -0,0 +1,37 @@ +const std = @import("std"); +const build_options = @import("build_options"); + +const HandlerFn = *const fn (std.mem.Allocator, []const u8, Context) anyerror![]const u8; + +const log = std.log.scoped(.universal_lambda); + +// TODO: Should this be union? +pub const Context = struct {}; + +fn deinit() void { + // if (client) |*c| c.deinit(); + // client = null; +} +/// Starts the universal lambda framework. Handler will be called when an event is processing. +/// Depending on the serverless system used, from a practical sense, this may not return. +/// +/// If an allocator is not provided, an approrpriate allocator will be selected and used +/// This function is intended to loop infinitely. If not used in this manner, +/// make sure to call the deinit() function +pub fn run(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { // TODO: remove inferred error set? + switch (build_options.build_type) { + .exe_run => try runExe(allocator, event_handler), + else => return error.NotImplemented, + } +} + +fn runExe(allocator: ?std.mem.Allocator, event_handler: HandlerFn) !void { + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const gpa_alloc = allocator orelse gpa.allocator(); + + // TODO: set up an arena for this? Are we doing an arena for every type? + const writer = std.io.getStdOut().writer(); + try writer.writeAll(try event_handler(gpa_alloc, "", .{})); + try writer.writeAll("\n"); +} diff --git a/src/universal_lambda_build.zig b/src/universal_lambda_build.zig new file mode 100644 index 0000000..68413e8 --- /dev/null +++ b/src/universal_lambda_build.zig @@ -0,0 +1,46 @@ +const std = @import("std"); +pub const BuildType = enum { + awslambda_package, + awslambda_iam, + awslambda_deploy, + awslambda_run, + exe_run, + standalone_run, + // cloudflare_* (TBD) + // flexilib_* (TBD) +}; +// awslambda_package +// awslambda_iam +// awslambda_deploy +// awslambda_run +// exe_run // TODO: Can we skip this? +// cloudflare_* (TBD) +// flexilib_* (TBD) +pub fn configureBuild(b: *std.Build, exe: *std.Build.Step.Compile) !void { + // Make our target platform visible to runtime through an import + // called "build_options" + var options_module: *std.Build.Module = undefined; + { + // We need to go through the command line args, look for argument(s) + // between "build" and anything prefixed with "-". First take, blow up + // if there is more than one. That's the step we're rolling with + // These frameworks I believe are inextricably tied to both build and + // run behavior + const options = b.addOptions(); + options.addOption(BuildType, "build_type", .exe_run); + exe.addOptions("build_options", options); + options_module = exe.modules.get("build_options").?; + } + // Add modules + { + exe.addAnonymousModule("universal_lambda_handler", .{ + .source_file = .{ .path = "upstream/src/universal_lambda.zig" }, + .dependencies = &[_]std.Build.ModuleDependency{.{ + .name = "build_options", + .module = options_module, + }}, + }); + } + + // Add steps +}