Use Uuids for id

This commit is contained in:
jaina heartles 2022-07-12 21:16:33 -07:00
parent 20af2c82c8
commit ad6f1454a6
4 changed files with 34 additions and 17 deletions

View file

@ -1,10 +1,11 @@
const std = @import("std"); const std = @import("std");
const util = @import("util");
const db = @import("./db.zig"); const db = @import("./db.zig");
pub const models = @import("./models.zig"); pub const models = @import("./models.zig");
pub const free = db.free; pub const free = db.free;
pub const Id = []const u8; pub const Uuid = util.Uuid;
pub fn CreateInfo(comptime T: type) type { pub fn CreateInfo(comptime T: type) type {
const t_fields = std.meta.fields(T); 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; var result: T = undefined;
result.id = id; result.id = id;
inline for (std.meta.fields(CreateInfo(T))) |f| { 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; return result;
} }
const ApiServer = struct { pub const ApiServer = struct {
db: db.Database, db: db.Database,
prng: std.rand.DefaultPrng,
last_id: u64 = 0, last_id: u64 = 0,
pub fn init(alloc: std.mem.Allocator) !ApiServer { pub fn init(alloc: std.mem.Allocator) !ApiServer {
return ApiServer{ return ApiServer{
.db = try db.Database.init(alloc), .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; self.last_id += 1;
return std.fmt.allocPrint(alloc, "{}", .{self.last_id}); return std.fmt.allocPrint(alloc, "{}", .{self.last_id});
} }
pub fn createNote(self: *ApiServer, info: CreateInfo(models.Note), alloc: std.mem.Allocator) !models.Note { pub fn createNote(self: *ApiServer, info: CreateInfo(models.Note)) !Uuid {
const id = try self.genId(alloc); const id = Uuid.randV4(self.prng.random());
const note = reify(models.Note, id, info); const note = reify(models.Note, id, info);
try self.db.putNote(note); try self.db.putNote(note);
@ -59,7 +62,7 @@ const ApiServer = struct {
return id; 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); return self.db.getNote(id, alloc);
} }
}; };

View file

@ -1,7 +1,8 @@
const std = @import("std"); const std = @import("std");
const util = @import("util");
const Uuid = util.Uuid;
const models = @import("./models.zig"); const models = @import("./models.zig");
const Id = []const u8;
// Clones a struct and its fields to a single layer of depth. // Clones a struct and its fields to a single layer of depth.
// Caller owns memory, can be freed using free below // 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| { inline for (std.meta.fields(@TypeOf(val))) |f| {
// TODO
if (f.field_type == []u8 or f.field_type == []const u8) { if (f.field_type == []u8 or f.field_type == []const u8) {
@field(result, f.name) = try cloneString(alloc, @field(val, f.name)); @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 { } else {
@compileError("unsupported field type " ++ @typeName(f.field_type)); @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 // Frees a struct and its fields returned by clone
pub fn free(alloc: std.mem.Allocator, val: anytype) void { pub fn free(alloc: std.mem.Allocator, val: anytype) void {
inline for (std.meta.fields(@TypeOf(val))) |f| { inline for (std.meta.fields(@TypeOf(val))) |f| {
// TODO
if (f.field_type == []u8 or f.field_type == []const u8) { if (f.field_type == []u8 or f.field_type == []const u8) {
alloc.free(@field(val, f.name)); alloc.free(@field(val, f.name));
} else if (f.field_type == Uuid) {
// nothing
} else { } else {
@compileError("unsupported field type " ++ @typeName(f.field_type)); @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 { pub const Database = struct {
internal_alloc: std.mem.Allocator, internal_alloc: std.mem.Allocator,
notes: std.StringHashMap(models.Note), notes: std.AutoHashMap(Uuid, models.Note),
pub fn init(alloc: std.mem.Allocator) !Database { pub fn init(alloc: std.mem.Allocator) !Database {
var db = Database{ var db = Database{
.internal_alloc = alloc, .internal_alloc = alloc,
.notes = std.StringHashMap(models.Note).init(alloc), .notes = std.AutoHashMap(Uuid, models.Note).init(alloc),
}; };
try db.putNote(models.Note{ try db.putNote(models.Note{
.id = "1", .id = Uuid{ .data = @bitCast([16]u8, @as(u128, 1)) },
.content = "abcd", .content = "abcd",
}); });
@ -68,13 +75,13 @@ pub const Database = struct {
self.notes.deinit(); self.notes.deinit();
} }
pub fn containsNote(self: *Database, id: Id) !bool { pub fn containsNote(self: *Database, id: Uuid) !bool {
return self.notes.contains(id); return self.notes.contains(id);
} }
// returns a copy of the note data from storage. memory is allocated with the provided // returns a copy of the note data from storage. memory is allocated with the provided
// allocator. can be freed using free() above // 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; const note = self.notes.get(id) orelse return null;
return try clone(alloc, note); return try clone(alloc, note);
} }

View file

@ -1,8 +1,11 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin"); const builtin = @import("builtin");
const http = @import("http"); const http = @import("http");
const util = @import("util");
const api = @import("./api.zig"); const api = @import("./api.zig");
const models = @import("./models.zig"); const models = @import("./models.zig");
const Uuid = util.Uuid;
// this thing is overcomplicated and weird. stop this // this thing is overcomplicated and weird. stop this
const Router = http.Router(*RequestServer); 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); 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); var tokens = std.json.TokenStream.init(body);
const note = try std.json.parse(api.CreateInfo(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 }); 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); try respondJson(ctx, .created, note, srv.alloc);
} }
fn getNote(srv: *RequestServer, ctx: *http.server.Context, args: RouteArgs) !void { 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; const note = (try srv.api.getNote(id, srv.alloc)) orelse return error.NotFound;
defer api.free(srv.alloc, note); defer api.free(srv.alloc, note);

View file

@ -1,4 +1,6 @@
const Uuid = @import("util").Uuid;
pub const Note = struct { pub const Note = struct {
id: []const u8, id: Uuid,
content: []const u8, content: []const u8,
}; };