diff --git a/src/main/api.zig b/src/main/api.zig index 79d7f19..b5aba8a 100644 --- a/src/main/api.zig +++ b/src/main/api.zig @@ -1,10 +1,11 @@ const std = @import("std"); +const util = @import("util"); const db = @import("./db.zig"); pub const models = @import("./models.zig"); pub const free = db.free; -pub const Id = []const u8; +pub const Uuid = util.Uuid; pub fn CreateInfo(comptime T: type) type { const t_fields = std.meta.fields(T); @@ -26,7 +27,7 @@ pub fn CreateInfo(comptime T: type) type { } }); } -fn reify(comptime T: type, id: Id, val: CreateInfo(T)) T { +fn reify(comptime T: type, id: Uuid, val: CreateInfo(T)) T { var result: T = undefined; result.id = id; inline for (std.meta.fields(CreateInfo(T))) |f| { @@ -35,23 +36,25 @@ fn reify(comptime T: type, id: Id, val: CreateInfo(T)) T { return result; } -const ApiServer = struct { +pub const ApiServer = struct { db: db.Database, + prng: std.rand.DefaultPrng, last_id: u64 = 0, pub fn init(alloc: std.mem.Allocator) !ApiServer { return ApiServer{ .db = try db.Database.init(alloc), + .prng = std.rand.DefaultPrng.init(1998), }; } - fn genId(self: *ApiServer, alloc: std.mem.Allocator) ![]const u8 { + fn genUuid(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); + pub fn createNote(self: *ApiServer, info: CreateInfo(models.Note)) !Uuid { + const id = Uuid.randV4(self.prng.random()); const note = reify(models.Note, id, info); try self.db.putNote(note); @@ -59,7 +62,7 @@ const ApiServer = struct { return id; } - pub fn getNote(self: *ApiServer, id: Id, alloc: std.mem.Allocator) !?models.Note { + pub fn getNote(self: *ApiServer, id: Uuid, alloc: std.mem.Allocator) !?models.Note { return self.db.getNote(id, alloc); } }; diff --git a/src/main/db.zig b/src/main/db.zig index 79760d5..4151631 100644 --- a/src/main/db.zig +++ b/src/main/db.zig @@ -1,7 +1,8 @@ const std = @import("std"); +const util = @import("util"); +const Uuid = util.Uuid; const models = @import("./models.zig"); -const Id = []const u8; // Clones a struct and its fields to a single layer of depth. // Caller owns memory, can be freed using free below @@ -14,8 +15,11 @@ fn clone(alloc: std.mem.Allocator, val: anytype) !@TypeOf(val) { } inline for (std.meta.fields(@TypeOf(val))) |f| { + // TODO if (f.field_type == []u8 or f.field_type == []const u8) { @field(result, f.name) = try cloneString(alloc, @field(val, f.name)); + } else if (f.field_type == Uuid) { + @field(result, f.name) = @field(val, f.name); } else { @compileError("unsupported field type " ++ @typeName(f.field_type)); } @@ -33,8 +37,11 @@ fn cloneString(alloc: std.mem.Allocator, str: []const u8) ![]const u8 { // Frees a struct and its fields returned by clone pub fn free(alloc: std.mem.Allocator, val: anytype) void { inline for (std.meta.fields(@TypeOf(val))) |f| { + // TODO if (f.field_type == []u8 or f.field_type == []const u8) { alloc.free(@field(val, f.name)); + } else if (f.field_type == Uuid) { + // nothing } else { @compileError("unsupported field type " ++ @typeName(f.field_type)); } @@ -43,16 +50,16 @@ pub fn free(alloc: std.mem.Allocator, val: anytype) void { pub const Database = struct { internal_alloc: std.mem.Allocator, - notes: std.StringHashMap(models.Note), + notes: std.AutoHashMap(Uuid, models.Note), pub fn init(alloc: std.mem.Allocator) !Database { var db = Database{ .internal_alloc = alloc, - .notes = std.StringHashMap(models.Note).init(alloc), + .notes = std.AutoHashMap(Uuid, models.Note).init(alloc), }; try db.putNote(models.Note{ - .id = "1", + .id = Uuid{ .data = @bitCast([16]u8, @as(u128, 1)) }, .content = "abcd", }); @@ -68,13 +75,13 @@ pub const Database = struct { self.notes.deinit(); } - pub fn containsNote(self: *Database, id: Id) !bool { + pub fn containsNote(self: *Database, id: Uuid) !bool { return self.notes.contains(id); } // returns a copy of the note data from storage. memory is allocated with the provided // allocator. can be freed using free() above - pub fn getNote(self: *Database, id: Id, alloc: std.mem.Allocator) !?models.Note { + pub fn getNote(self: *Database, id: Uuid, alloc: std.mem.Allocator) !?models.Note { const note = self.notes.get(id) orelse return null; return try clone(alloc, note); } diff --git a/src/main/main.zig b/src/main/main.zig index 7af76b9..432b38b 100644 --- a/src/main/main.zig +++ b/src/main/main.zig @@ -1,8 +1,11 @@ const std = @import("std"); const builtin = @import("builtin"); const http = @import("http"); +const util = @import("util"); + const api = @import("./api.zig"); const models = @import("./models.zig"); +const Uuid = util.Uuid; // this thing is overcomplicated and weird. stop this const Router = http.Router(*RequestServer); @@ -50,15 +53,17 @@ fn createNote(srv: *RequestServer, ctx: *http.server.Context, _: RouteArgs) !voi 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(api.CreateInfo(models.Note), &tokens, .{ .allocator = srv.alloc }); - defer std.json.parseFree(models.Note, note, .{ .allocator = srv.alloc }); + defer std.json.parseFree(api.CreateInfo(models.Note), note, .{ .allocator = srv.alloc }); - try srv.api.createNote(note); + // TODO + _ = 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 id_str = args.get("id") orelse return error.NotFound; + const id = Uuid.parse(id_str) catch return respondJson(ctx, .bad_request, .{ .@"error" = "invalid id" }, srv.alloc); const note = (try srv.api.getNote(id, srv.alloc)) orelse return error.NotFound; defer api.free(srv.alloc, note); diff --git a/src/main/models.zig b/src/main/models.zig index 94df39b..eac6bbe 100644 --- a/src/main/models.zig +++ b/src/main/models.zig @@ -1,4 +1,6 @@ +const Uuid = @import("util").Uuid; + pub const Note = struct { - id: []const u8, + id: Uuid, content: []const u8, };