diff --git a/src/main/api.zig b/src/main/api.zig new file mode 100644 index 0000000..79d7f19 --- /dev/null +++ b/src/main/api.zig @@ -0,0 +1,65 @@ +const std = @import("std"); + +const db = @import("./db.zig"); + +pub const models = @import("./models.zig"); +pub const free = db.free; +pub const Id = []const u8; + +pub fn CreateInfo(comptime T: type) type { + const t_fields = std.meta.fields(T); + var fields: [t_fields.len - 1]std.builtin.Type.StructField = undefined; + var count = 0; + + inline for (t_fields) |f| { + if (std.mem.eql(u8, f.name, "id")) continue; + + fields[count] = f; + count += 1; + } + + return @Type(.{ .Struct = .{ + .layout = .Auto, + .fields = &fields, + .decls = &[0]std.builtin.Type.Declaration{}, + .is_tuple = false, + } }); +} + +fn reify(comptime T: type, id: Id, val: CreateInfo(T)) T { + var result: T = undefined; + result.id = id; + inline for (std.meta.fields(CreateInfo(T))) |f| { + @field(result, f.name) = @field(val, f.name); + } + return result; +} + +const ApiServer = struct { + db: db.Database, + last_id: u64 = 0, + + pub fn init(alloc: std.mem.Allocator) !ApiServer { + return ApiServer{ + .db = try db.Database.init(alloc), + }; + } + + fn genId(self: *ApiServer, alloc: std.mem.Allocator) ![]const u8 { + self.last_id += 1; + return std.fmt.allocPrint(alloc, "{}", .{self.last_id}); + } + + pub fn createNote(self: *ApiServer, info: CreateInfo(models.Note), alloc: std.mem.Allocator) !models.Note { + const id = try self.genId(alloc); + + const note = reify(models.Note, id, info); + try self.db.putNote(note); + + return id; + } + + pub fn getNote(self: *ApiServer, id: Id, alloc: std.mem.Allocator) !?models.Note { + return self.db.getNote(id, alloc); + } +}; diff --git a/src/main/main.zig b/src/main/main.zig index 3623047..7af76b9 100644 --- a/src/main/main.zig +++ b/src/main/main.zig @@ -1,7 +1,7 @@ const std = @import("std"); const builtin = @import("builtin"); const http = @import("http"); -const db = @import("./db.zig"); +const api = @import("./api.zig"); const models = @import("./models.zig"); // this thing is overcomplicated and weird. stop this @@ -49,18 +49,18 @@ fn respondJson(ctx: *http.server.Context, status: http.Status, value: anytype, a fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !void { const body = ctx.request.body orelse return respondJson(ctx, .bad_request, .{ .@"error" = "no note body provided" }, srv.alloc); var tokens = std.json.TokenStream.init(body); - const note = try std.json.parse(models.Note, &tokens, .{ .allocator = srv.alloc }); + const note = try std.json.parse(api.CreateInfo(models.Note), &tokens, .{ .allocator = srv.alloc }); defer std.json.parseFree(models.Note, note, .{ .allocator = srv.alloc }); - try srv.db.putNote(note); + try srv.api.createNote(note); try respondJson(ctx, .created, note, srv.alloc); } fn getNote(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void { const id = args.get("id") orelse return error.NotFound; - const note = (try srv.db.getNote(id, srv.alloc)) orelse return error.NotFound; - defer db.free(srv.alloc, note); + const note = (try srv.api.getNote(id, srv.alloc)) orelse return error.NotFound; + defer api.free(srv.alloc, note); try respondJson(ctx, .ok, note, srv.alloc); } @@ -71,12 +71,12 @@ fn healthcheck(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !vo const RequestServer = struct { alloc: std.mem.Allocator, - db: db.Database, + api: api.ApiServer, fn init(alloc: std.mem.Allocator) !RequestServer { return RequestServer{ .alloc = alloc, - .db = try db.Database.init(alloc), + .api = try api.ApiServer.init(alloc), }; }