Track creation time for notes
This commit is contained in:
parent
b1190ddea8
commit
07019cb090
6 changed files with 45 additions and 18 deletions
|
@ -4,6 +4,7 @@ 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 DateTime = util.DateTime;
|
||||||
pub const Uuid = util.Uuid;
|
pub const Uuid = util.Uuid;
|
||||||
|
|
||||||
// Frees an api struct and its fields allocated from alloc
|
// Frees an api struct and its fields allocated from alloc
|
||||||
|
@ -12,7 +13,7 @@ pub fn free(alloc: std.mem.Allocator, val: anytype) void {
|
||||||
// TODO
|
// 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) {
|
} else if (f.field_type == Uuid or f.field_type == DateTime) {
|
||||||
// nothing
|
// nothing
|
||||||
} else {
|
} else {
|
||||||
@compileError("unsupported field type " ++ @typeName(f.field_type));
|
@compileError("unsupported field type " ++ @typeName(f.field_type));
|
||||||
|
@ -89,6 +90,8 @@ pub const ApiServer = struct {
|
||||||
.id = id,
|
.id = id,
|
||||||
.author_id = ctx.user.id,
|
.author_id = ctx.user.id,
|
||||||
.content = info.content,
|
.content = info.content,
|
||||||
|
|
||||||
|
.created_at = DateTime.now(),
|
||||||
};
|
};
|
||||||
try self.db.insert(models.Note, note);
|
try self.db.insert(models.Note, note);
|
||||||
|
|
||||||
|
|
|
@ -104,6 +104,7 @@ pub const Database = struct {
|
||||||
\\ id TEXT PRIMARY KEY,
|
\\ id TEXT PRIMARY KEY,
|
||||||
\\ content TEXT NOT NULL,
|
\\ content TEXT NOT NULL,
|
||||||
\\ author_id TEXT NOT NULL,
|
\\ author_id TEXT NOT NULL,
|
||||||
|
\\ created_at INTEGER NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
\\
|
\\
|
||||||
\\ FOREIGN KEY (author_id) REFERENCES user(id)
|
\\ FOREIGN KEY (author_id) REFERENCES user(id)
|
||||||
\\) STRICT;
|
\\) STRICT;
|
||||||
|
@ -153,12 +154,7 @@ pub const Database = struct {
|
||||||
@field(result, field_name) = val;
|
@field(result, field_name) = val;
|
||||||
|
|
||||||
inline for (fields) |f, i| {
|
inline for (fields) |f, i| {
|
||||||
@field(result, f) = switch (@TypeOf(@field(result, f))) {
|
@field(result, f) = row.getAlloc(@TypeOf(@field(result, f)), i, alloc) catch unreachable;
|
||||||
// TODO: Handle allocation failures gracefully
|
|
||||||
[]const u8 => row.getTextAlloc(i, alloc) catch unreachable,
|
|
||||||
Uuid => row.getUuid(i) catch unreachable,
|
|
||||||
else => @compileError("unknown type " ++ @typeName(@TypeOf(@field(result, f)))),
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|
|
@ -36,7 +36,7 @@ pub const RequestServer = struct {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn listenAndRun(self: *RequestServer, addr: std.net.Address) noreturn {
|
fn listenAndRun(self: *RequestServer, addr: std.net.Address) !void {
|
||||||
var srv = http.Server.listen(addr) catch unreachable;
|
var srv = http.Server.listen(addr) catch unreachable;
|
||||||
defer srv.shutdown();
|
defer srv.shutdown();
|
||||||
|
|
||||||
|
@ -45,12 +45,12 @@ pub const RequestServer = struct {
|
||||||
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
var fba = std.heap.FixedBufferAllocator.init(&buf);
|
||||||
const alloc = fba.allocator();
|
const alloc = fba.allocator();
|
||||||
|
|
||||||
var ctx = srv.accept(alloc) catch unreachable;
|
var ctx = try srv.accept(alloc);
|
||||||
defer ctx.close();
|
defer ctx.close();
|
||||||
|
|
||||||
router.dispatch(self, &ctx, ctx.request.method, ctx.request.path) catch |err| switch (err) {
|
router.dispatch(self, &ctx, ctx.request.method, ctx.request.path) catch |err| switch (err) {
|
||||||
error.NotFound, error.RouteNotApplicable => c.notFound(self, &ctx),
|
error.NotFound, error.RouteNotApplicable => c.notFound(self, &ctx),
|
||||||
else => unreachable,
|
else => return err,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -59,5 +59,5 @@ pub const RequestServer = struct {
|
||||||
pub fn main() anyerror!void {
|
pub fn main() anyerror!void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
var srv = try RequestServer.init(gpa.allocator());
|
var srv = try RequestServer.init(gpa.allocator());
|
||||||
srv.listenAndRun(std.net.Address.parseIp("0.0.0.0", 8080) catch unreachable);
|
return srv.listenAndRun(std.net.Address.parseIp("0.0.0.0", 8080) catch unreachable);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,15 @@
|
||||||
const Uuid = @import("util").Uuid;
|
const std = @import("std");
|
||||||
|
const util = @import("util");
|
||||||
|
|
||||||
|
const Uuid = util.Uuid;
|
||||||
|
const DateTime = util.DateTime;
|
||||||
|
|
||||||
pub const Note = struct {
|
pub const Note = struct {
|
||||||
id: Uuid,
|
id: Uuid,
|
||||||
content: []const u8,
|
content: []const u8,
|
||||||
author_id: Uuid,
|
author_id: Uuid,
|
||||||
|
|
||||||
|
created_at: DateTime,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const User = struct {
|
pub const User = struct {
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const util = @import("util");
|
||||||
const c = @cImport({
|
const c = @cImport({
|
||||||
@cInclude("sqlite3.h");
|
@cInclude("sqlite3.h");
|
||||||
});
|
});
|
||||||
|
|
||||||
const Uuid = @import("util").Uuid;
|
const Uuid = util.Uuid;
|
||||||
|
const DateTime = util.DateTime;
|
||||||
|
|
||||||
pub const Sqlite = struct {
|
pub const Sqlite = struct {
|
||||||
db: *c.sqlite3,
|
db: *c.sqlite3,
|
||||||
|
@ -66,6 +68,21 @@ pub const Row = struct {
|
||||||
_ = try self.getText(idx, &buf);
|
_ = try self.getText(idx, &buf);
|
||||||
return try Uuid.parse(buf[0..Uuid.string_len]);
|
return try Uuid.parse(buf[0..Uuid.string_len]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getDateTime(self: Row, idx: u15) !DateTime {
|
||||||
|
return DateTime{ .seconds_since_epoch = try self.getI64(idx) };
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getAlloc(self: Row, comptime T: type, idx: u15, alloc: std.mem.Allocator) !T {
|
||||||
|
// TODO: handle optionals
|
||||||
|
return switch (T) {
|
||||||
|
[]u8, []const u8 => self.getTextAlloc(idx, alloc),
|
||||||
|
i64 => self.getI64(idx),
|
||||||
|
Uuid => self.getUuid(idx),
|
||||||
|
DateTime => self.getDateTime(idx),
|
||||||
|
else => @compileError("unknown type " ++ @typeName(T)),
|
||||||
|
};
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const PreparedStmt = struct {
|
pub const PreparedStmt = struct {
|
||||||
|
@ -93,17 +110,22 @@ pub const PreparedStmt = struct {
|
||||||
|
|
||||||
pub fn bindI64(self: *PreparedStmt, idx: u15, val: i64) !void {
|
pub fn bindI64(self: *PreparedStmt, idx: u15, val: i64) !void {
|
||||||
return switch (c.sqlite3_bind_int64(self.stmt, idx, val)) {
|
return switch (c.sqlite3_bind_int64(self.stmt, idx, val)) {
|
||||||
.SQLITE_OK => {},
|
c.SQLITE_OK => {},
|
||||||
else => error.UnknownError,
|
else => error.UnknownError,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn bindDateTime(self: *PreparedStmt, idx: u15, val: DateTime) !void {
|
||||||
|
return self.bindI64(idx, val.seconds_since_epoch);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn bind(self: *PreparedStmt, idx: u15, val: anytype) !void {
|
pub fn bind(self: *PreparedStmt, idx: u15, val: anytype) !void {
|
||||||
return switch (@TypeOf(val)) {
|
return switch (@TypeOf(val)) {
|
||||||
[]const u8 => self.bindText(idx, val),
|
[]u8, []const u8 => self.bindText(idx, val),
|
||||||
i64 => self.bindI64(idx, val),
|
i64 => self.bindI64(idx, val),
|
||||||
Uuid => self.bindUuid(idx, val),
|
Uuid => self.bindUuid(idx, val),
|
||||||
@Type(.Null) => self.bindNull(idx),
|
DateTime => self.bindDateTime(idx, val),
|
||||||
|
@TypeOf(null) => self.bindNull(idx),
|
||||||
else => @compileError("Unknown Type"),
|
else => @compileError("Unknown Type"),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ pub fn month(value: DateTime) std.time.epoch.Month {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn day(value: DateTime) u9 {
|
pub fn day(value: DateTime) u9 {
|
||||||
return value.epochSeconds().getEpochDay().calculateYearDay().calculateMonthDay().day;
|
return value.epochSeconds().getEpochDay().calculateYearDay().calculateMonthDay().day_index;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hour(value: DateTime) u5 {
|
pub fn hour(value: DateTime) u5 {
|
||||||
|
@ -39,7 +39,7 @@ pub fn second(value: DateTime) u6 {
|
||||||
pub fn format(value: DateTime, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
pub fn format(value: DateTime, comptime _: []const u8, _: std.fmt.FormatOptions, writer: anytype) !void {
|
||||||
return std.fmt.format(
|
return std.fmt.format(
|
||||||
writer,
|
writer,
|
||||||
"{}-{}-{}T{}{}{}",
|
"{}-{}-{} {}:{}:{}",
|
||||||
.{ value.year(), value.month().numeric(), value.day(), value.hour(), value.minute(), value.second() },
|
.{ value.year(), value.month().numeric(), value.day(), value.hour(), value.minute(), value.second() },
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue