srctree

Gregory Mullen parent d70530a3 ec1ee666
rename comments to messages

this is very sad, but it also deletes a lot of the data because I still couldn't be arsed to do that one task on the readme :D
src/endpoints/repos/commits.zig added: 372, removed: 449, total 0
@@ -23,7 +23,6 @@ const Types = @import("../../types.zig");
const RequestData = @import("../../request_data.zig").RequestData;
 
const S = Template.Structs;
const Comment = Types.Comment;
const CommitMap = Types.CommitMap;
const Delta = Types.Delta;
const Error = Route.Error;
@@ -136,11 +135,11 @@ fn commitHtml(ctx: *Context, sha: []const u8, repo_name: []const u8, repo: Git.R
if (delta.getMessages(ctx.alloc)) |messages| {
thread = try ctx.alloc.alloc(Template.Structs.Thread, messages.len);
for (messages, thread) |msg, *pg_comment| {
switch (msg) {
switch (msg.kind) {
.comment => |cmt| {
pg_comment.* = .{
.author = try Bleach.sanitizeAlloc(ctx.alloc, cmt.author, .{}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(cmt.updated)}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(msg.updated)}),
.message = try Bleach.sanitizeAlloc(ctx.alloc, cmt.message, .{}),
.direct_reply = null,
.sub_thread = null,
 
src/endpoints/repos/diffs.zig added: 372, removed: 449, total 0
@@ -28,7 +28,6 @@ const Template = @import("../../template.zig");
const Types = @import("../../types.zig");
const Highlighting = @import("../../syntax-highlight.zig");
 
const Comment = Types.Comment;
const Delta = Types.Delta;
const Error = Route.Error;
const GET = Route.GET;
@@ -224,9 +223,8 @@ fn newComment(ctx: *Context) Error!void {
(ctx.auth.currentUser(ctx.alloc) catch unreachable).username
else
"public";
const c = Comment.new(username, msg.value) catch unreachable;
_ = delta.loadThread(ctx.alloc) catch unreachable;
delta.addComment(ctx.alloc, c) catch unreachable;
var thread = delta.loadThread(ctx.alloc) catch unreachable;
thread.newComment(ctx.alloc, .{ .author = username, .message = msg.value }) catch unreachable;
// TODO record current revision at comment time
delta.commit() catch unreachable;
return ctx.response.redirect(loc, true) catch unreachable;
@@ -701,16 +699,19 @@ fn view(ctx: *Context) Error!void {
if (delta.getMessages(ctx.alloc)) |messages| {
root_thread = try ctx.alloc.alloc(S.Thread, messages.len);
for (messages, root_thread) |msg, *c_ctx| {
switch (msg) {
switch (msg.kind) {
.comment => |comment| {
c_ctx.* = .{
.author = try Bleach.sanitizeAlloc(ctx.alloc, comment.author, .{}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(comment.updated)}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(msg.updated)}),
.message = if (patch) |pt|
translateComment(ctx.alloc, comment.message, pt, &repo) catch unreachable
else
try Bleach.sanitizeAlloc(ctx.alloc, comment.message, .{}),
.direct_reply = .{ .uri = try allocPrint(ctx.alloc, "{}/direct_reply/{x}", .{ index, fmtSliceHexLower(comment.hash[0..]) }) },
.direct_reply = .{ .uri = try allocPrint(ctx.alloc, "{}/direct_reply/{x}", .{
index,
fmtSliceHexLower(msg.hash[0..]),
}) },
.sub_thread = null,
};
},
 
src/endpoints/repos/issues.zig added: 372, removed: 449, total 0
@@ -20,7 +20,6 @@ const Repos = @import("../repos.zig");
const CURL = @import("../../curl.zig");
const Bleach = @import("../../bleach.zig");
const Types = @import("../../types.zig");
const Comment = Types.Comment;
const Delta = Types.Delta;
const Humanize = @import("../../humanize.zig");
 
@@ -107,14 +106,13 @@ fn newComment(ctx: *Context) Error!void {
rd.name,
issue_index,
) catch unreachable orelse return error.Unrouteable;
_ = delta.loadThread(ctx.alloc) catch unreachable;
const username = if (ctx.auth.valid())
(ctx.auth.currentUser(ctx.alloc) catch unreachable).username
else
"public";
const c = Comment.new(username, msg.value) catch unreachable;
 
delta.addComment(ctx.alloc, c) catch {};
var thread = delta.loadThread(ctx.alloc) catch unreachable;
thread.newComment(ctx.alloc, .{ .author = username, .message = msg.value }) catch {};
delta.commit() catch unreachable;
var buf: [2048]u8 = undefined;
const loc = try std.fmt.bufPrint(&buf, "/repo/{s}/issues/{x}", .{ rd.name, issue_index });
@@ -138,13 +136,16 @@ fn view(ctx: *Context) Error!void {
if (delta.getMessages(ctx.alloc)) |messages| {
root_thread = try ctx.alloc.alloc(S.Thread, messages.len);
for (messages, root_thread) |msg, *c_ctx| {
switch (msg) {
switch (msg.kind) {
.comment => |comment| {
c_ctx.* = .{
.author = try Bleach.sanitizeAlloc(ctx.alloc, comment.author, .{}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(comment.updated)}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(msg.updated)}),
.message = try Bleach.sanitizeAlloc(ctx.alloc, comment.message, .{}),
.direct_reply = .{ .uri = try allocPrint(ctx.alloc, "{}/direct_reply/{x}", .{ index, fmtSliceHexLower(comment.hash[0..]) }) },
.direct_reply = .{ .uri = try allocPrint(ctx.alloc, "{}/direct_reply/{x}", .{
index,
fmtSliceHexLower(msg.hash[0..]),
}) },
.sub_thread = null,
};
},
 
src/types.zig added: 372, removed: 449, total 0
@@ -1,6 +1,6 @@
const std = @import("std");
 
pub const Comment = @import("types/comment.zig");
pub const Message = @import("types/message.zig");
pub const CommitMap = @import("types/commit-map.zig");
pub const Delta = @import("types/delta.zig");
pub const Diff = @import("types/diff.zig");
@@ -19,7 +19,7 @@ pub const Storage = std.fs.Dir;
 
pub fn init(dir: Storage) !void {
inline for (.{
Comment,
Message,
CommitMap,
Delta,
Diff,
 
ev/null added: 372, removed: 449, total 0
@@ -1,320 +0,0 @@
const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const endian = builtin.cpu.arch.endian();
const sha256 = std.crypto.hash.sha2.Sha256;
const allocPrint = std.fmt.allocPrint;
const bufPrint = std.fmt.bufPrint;
const AnyWriter = std.io.AnyWriter;
const fmtSliceHexLower = std.fmt.fmtSliceHexLower;
 
const Humanize = @import("../humanize.zig");
const Types = @import("../types.zig");
const Bleach = @import("../bleach.zig");
const Template = @import("../template.zig");
 
pub const Comment = @This();
const CMMT_VERSION: usize = 0;
pub const TYPE_PREFIX = "messages";
var datad: Types.Storage = undefined;
 
state: usize = 0,
tz: i32 = 0,
created: i64 = 0,
updated: i64 = 0,
target: Targets = .{ .nos = {} },
hash: [sha256.digest_length]u8 = undefined,
 
author: []const u8,
message: []const u8,
replies: ?usize = null,
 
pub fn init(_: []const u8) !void {}
pub fn initType(stor: Types.Storage) !void {
datad = stor;
}
 
pub fn raze() void {
datad.close();
}
 
pub fn charToKind(c: u8) TargetKind {
return switch (c) {
'C' => .commit,
'D' => .diff,
'I' => .issue,
'r' => .reply,
else => .nos,
};
}
 
fn readVersioned(a: Allocator, file: std.fs.File, hash: []const u8) !Comment {
var reader = file.reader();
const ver: usize = try reader.readInt(usize, endian);
return switch (ver) {
0 => return Comment{
.state = try reader.readInt(usize, endian),
.created = try reader.readInt(i64, endian),
.updated = try reader.readInt(i64, endian),
.tz = try reader.readInt(i32, endian),
.hash = hash[0..sha256.digest_length].*,
.target = switch (try reader.readInt(u8, endian)) {
0 => .{ .diff = try reader.readInt(usize, endian) },
'D' => .{ .diff = try reader.readInt(usize, endian) },
'I' => .{ .issue = try reader.readInt(usize, endian) },
'r' => .{ .reply = .{
.to = switch (try reader.readInt(u8, endian)) {
'c' => .{ .comment = try reader.readInt(usize, endian) },
'C' => .{ .commit = .{
.number = try reader.readInt(usize, endian),
.meta = try reader.readInt(usize, endian),
} },
'd' => .{ .diff = .{
.number = try reader.readInt(usize, endian),
.file = try reader.readInt(usize, endian),
.revision = try reader.readInt(usize, endian),
} },
else => return error.CommentCorrupted,
},
} },
else => return error.CommentCorrupted,
},
.author = try reader.readUntilDelimiterAlloc(a, 0, 0xFFFF),
.message = try reader.readAllAlloc(a, 0xFFFF),
.replies = reader.readInt(usize, endian) catch null,
},
else => error.UnsupportedVersion,
};
}
 
pub const TargetKind = enum(u7) {
nos = 0,
commit = 'C',
diff = 'D',
issue = 'I',
reply = 'r',
};
 
/// Comments can be directly attached to a commit, or a diff, but are
/// replies when referencing a specific line/target within them.
const ReplyKinds = enum(u7) {
nothing = 0,
comment = 'c',
commit = 'C',
diff = 'd',
};
 
pub const Reply = struct {
to: union(ReplyKinds) {
nothing: void,
comment: usize,
commit: CommitLine,
diff: DiffLine,
},
};
 
pub const CommitLine = struct {
number: usize,
meta: usize,
};
 
pub const DiffLine = struct {
number: usize,
file: usize,
revision: usize, // surely no one will ever need to use more than a u16
// number of revisions... right? I'll just shrink this... WCGW?!
};
 
pub const Targets = union(TargetKind) {
nos: void,
commit: [20]u8,
diff: usize,
issue: usize,
reply: Reply,
};
 
pub fn toHash(self: *Comment) *const [sha256.digest_length]u8 {
var h = sha256.init(.{});
h.update(self.author);
h.update(self.message);
h.update(std.mem.asBytes(&self.created));
h.update(std.mem.asBytes(&self.updated));
h.final(&self.hash);
return &self.hash;
}
 
pub fn writeNew(self: *Comment, d: std.fs.Dir) !void {
var buf: [2048]u8 = undefined;
_ = self.toHash();
const filename = try bufPrint(&buf, "{x}.comment", .{fmtSliceHexLower(&self.hash)});
var file = try d.createFile(filename, .{});
defer file.close();
const w = file.writer().any();
try self.writeOut(w);
}
 
pub fn commit(self: Comment) !void {
var file = try openFile(self.hash[0..]);
defer file.close();
const writer = file.writer().any();
try self.writeOut(writer);
}
 
fn writeOut(self: Comment, w: AnyWriter) !void {
try w.writeInt(usize, CMMT_VERSION, endian);
try w.writeInt(usize, self.state, endian);
try w.writeInt(i64, self.created, endian);
try w.writeInt(i64, self.updated, endian);
try w.writeInt(i32, self.tz, endian);
try w.writeInt(u8, @intFromEnum(self.target), endian);
switch (self.target) {
.nos => try w.writeInt(usize, 0, endian),
.commit => |c| try w.writeAll(&c),
.diff => try w.writeInt(usize, self.target.diff, endian),
.issue => try w.writeInt(usize, self.target.issue, endian),
.reply => unreachable,
}
 
try w.writeAll(self.author);
try w.writeAll("\x00");
try w.writeAll(self.message);
if (self.replies) |r| try w.writeInt(usize, r, endian);
}
 
pub fn readFile(a: std.mem.Allocator, file: std.fs.File, hash: []const u8) !Comment {
return readVersioned(a, file, hash);
}
 
pub fn toContext(self: Comment, a: Allocator) !Template.Context {
return Template.Context.initBuildable(a, self);
}
 
pub fn builder(self: Comment) Template.Context.Builder(Comment) {
return Template.Context.Builder(Comment).init(self);
}
 
pub fn contextBuilder(self: Comment, a: Allocator, ctx: *Template.Context) !void {
try ctx.putSlice("Author", try Bleach.sanitizeAlloc(a, self.author, .{}));
try ctx.putSlice("Message", try Bleach.sanitizeAlloc(a, self.message, .{}));
try ctx.putSlice("Date", try allocPrint(a, "{}", .{Humanize.unix(self.updated)}));
}
 
fn openFile(hash: []const u8) !std.fs.File {
var buf: [2048]u8 = undefined;
const filename = try bufPrint(&buf, "{x}.comment", .{fmtSliceHexLower(hash)});
return try datad.openFile(filename, .{ .mode = .read_write });
}
 
pub fn open(a: Allocator, hash: []const u8) !Comment {
var file = try openFile(hash);
defer file.close();
return try Comment.readFile(a, file, hash);
}
 
pub fn new(ath: []const u8, msg: []const u8) !Comment {
var c = Comment{
.author = ath,
.message = msg,
.created = std.time.timestamp(),
.updated = std.time.timestamp(),
};
try c.writeNew(datad);
 
return c;
}
 
test "comment" {
var a = std.testing.allocator;
 
var c = Comment{
.author = "grayhatter",
.message = "test comment, please ignore",
};
 
// zig fmt: off
const hash = c.toHash();
try std.testing.expectEqualSlices(
u8,
&[_]u8{
0x21, 0x78, 0x05, 0xE5, 0xB5, 0x0C, 0x05, 0xF5,
0x22, 0xAC, 0xFE, 0xBA, 0xEA, 0xA4, 0xAC, 0xC2,
0xD6, 0x50, 0xD1, 0xDD, 0x48, 0xFC, 0x34, 0x0E,
0xBB, 0x53, 0x94, 0x60, 0x56, 0x93, 0xC9, 0xC8
},
hash,
);
// zig fmt: on
 
var dir = std.testing.tmpDir(.{});
defer dir.cleanup();
 
try c.writeNew(dir.dir);
 
var buf: [2048]u8 = undefined;
const filename = try std.fmt.bufPrint(&buf, "{x}.comment", .{std.fmt.fmtSliceHexLower(hash)});
try std.testing.expectEqualStrings(
"217805e5b50c05f522acfebaeaa4acc2d650d1dd48fc340ebb5394605693c9c8.comment",
filename,
);
const blob = try dir.dir.readFileAlloc(a, filename, 0xFF);
defer a.free(blob);
 
// zig fmt: off
try std.testing.expectEqualSlices(
u8,
&[_]u8{
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
103, 114, 97, 121, 104, 97, 116, 116, 101, 114,
0, 116, 101, 115, 116, 32, 99, 111, 109, 109,
101, 110, 116, 44, 32, 112, 108, 101, 97, 115,
101, 32, 105, 103, 110, 111, 114, 101,
},
blob,
);
// zig fmt: on
}
 
test Comment {
const a = std.testing.allocator;
var tempdir = std.testing.tmpDir(.{});
defer tempdir.cleanup();
try Types.init(try tempdir.dir.makeOpenPath("datadir", .{ .iterate = true }));
 
var c = try Comment.new("author", "message");
 
// LOL, you thought
const mask: i64 = ~@as(i64, 0xffffff);
c.created = std.time.timestamp() & mask;
c.updated = std.time.timestamp() & mask;
 
var out = std.ArrayList(u8).init(a);
defer out.clearAndFree();
const outw = out.writer().any();
try c.writeOut(outw);
 
const v0: Comment = undefined;
const v0_bin: []const u8 = &[_]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x75, 0x74,
0x68, 0x6F, 0x72, 0x00, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
};
try std.testing.expectEqualSlices(u8, v0_bin, out.items);
const v1: Comment = undefined;
// TODO... eventually
_ = v0;
_ = v1;
 
const v1_bin: []const u8 = &[_]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x75, 0x74,
0x68, 0x6F, 0x72, 0x00, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65,
};
try std.testing.expectEqualSlices(u8, v1_bin, out.items);
}
 
src/types/delta.zig added: 372, removed: 449, total 0
@@ -2,10 +2,10 @@ const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const endian = builtin.cpu.arch.endian();
const AnyReader = std.io.AnyReader;
 
const Bleach = @import("../bleach.zig");
const Types = @import("../types.zig");
const Comment = Types.Comment;
const Thread = Types.Thread;
const Message = Thread.Message;
const Template = @import("../template.zig");
@@ -38,7 +38,7 @@ test State {
try std.testing.expectEqual(zero, ptr.*);
}
 
fn readVersioned(a: Allocator, idx: usize, reader: *std.io.AnyReader) !Delta {
fn readVersioned(a: Allocator, idx: usize, reader: *AnyReader) !Delta {
const ver: usize = try reader.readInt(usize, endian);
return switch (ver) {
0 => Delta{
@@ -191,12 +191,12 @@ pub fn getMessages(self: *Delta, a: Allocator) ![]Message {
return error.ThreadNotLoaded;
}
 
pub fn addComment(self: *Delta, a: Allocator, c: Comment) !void {
if (self.thread) |thread| {
return thread.addComment(a, c);
}
return error.ThreadNotLoaded;
}
//pub fn addComment(self: *Delta, a: Allocator, c: Comment) !void {
// if (self.thread) |thread| {
// return thread.addComment(a, c);
// }
// return error.ThreadNotLoaded;
//}
 
pub fn countComments(self: Delta) struct { count: usize, new: bool } {
if (self.thread) |thread| {
@@ -204,9 +204,9 @@ pub fn countComments(self: Delta) struct { count: usize, new: bool } {
const ts = std.time.timestamp() - 86400;
var cmtnew: bool = false;
var cmtlen: usize = 0;
for (msgs) |m| switch (m) {
.comment => |c| {
cmtnew = cmtnew or c.updated > ts;
for (msgs) |m| switch (m.kind) {
.comment => {
cmtnew = cmtnew or m.updated > ts;
cmtlen += 1;
},
else => {},
 
src/types/diff.zig added: 372, removed: 449, total 0
@@ -1,7 +1,7 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const Comment = @import("comment.zig");
//const Comment = @import("comment.zig");
const Delta = @import("delta.zig");
const Thread = @import("thread.zig");
 
@@ -75,42 +75,42 @@ pub fn commit(self: Diff) !void {
try self.file.setEndPos(self.file.getPos() catch unreachable);
}
 
pub fn readFile(a: std.mem.Allocator, idx: usize, file: std.fs.File) !Diff {
var diff: Diff = try readVersioned(a, idx, file);
var list = std.ArrayList(Comment).init(a);
if (diff.comment_data) |cd| {
const count = cd.len / 32;
for (0..count) |i| {
try list.append(Comment.open(a, cd[i * 32 .. (i + 1) * 32]) catch continue);
}
diff.comments = try list.toOwnedSlice();
}
return diff;
}
//pub fn readFile(a: std.mem.Allocator, idx: usize, file: std.fs.File) !Diff {
// var diff: Diff = try readVersioned(a, idx, file);
// var list = std.ArrayList(Comment).init(a);
// if (diff.comment_data) |cd| {
// const count = cd.len / 32;
// for (0..count) |i| {
// try list.append(Comment.open(a, cd[i * 32 .. (i + 1) * 32]) catch continue);
// }
// diff.comments = try list.toOwnedSlice();
// }
// return diff;
//}
 
pub fn getComments(self: *Diff, a: Allocator) ![]Comment {
if (self.comments) |_| return self.comments.?;
 
if (self.comment_data) |cd| {
self.comments = try Comment.loadFromData(a, cd);
}
return &[0]Comment{};
}
 
pub fn addComment(self: *Diff, a: Allocator, c: Comment) !void {
const target = (self.comments orelse &[0]Comment{}).len;
if (self.comments) |*comments| {
if (a.resize(comments.*, target + 1)) {
comments.*.len = target + 1;
} else {
self.comments = try a.realloc(comments.*, target + 1);
}
} else {
self.comments = try a.alloc(Comment, target + 1);
}
self.comments.?[target] = c;
try self.writeOut();
}
//pub fn getComments(self: *Diff, a: Allocator) ![]Comment {
// if (self.comments) |_| return self.comments.?;
//
// if (self.comment_data) |cd| {
// self.comments = try Comment.loadFromData(a, cd);
// }
// return &[0]Comment{};
//}
//
//pub fn addComment(self: *Diff, a: Allocator, c: Comment) !void {
// const target = (self.comments orelse &[0]Comment{}).len;
// if (self.comments) |*comments| {
// if (a.resize(comments.*, target + 1)) {
// comments.*.len = target + 1;
// } else {
// self.comments = try a.realloc(comments.*, target + 1);
// }
// } else {
// self.comments = try a.alloc(Comment, target + 1);
// }
// self.comments.?[target] = c;
// try self.writeOut();
//}
 
pub fn raze(self: Diff, a: std.mem.Allocator) void {
//if (self.alloc_data) |data| {
 
src/types/issue.zig added: 372, removed: 449, total 0
@@ -1,8 +1,6 @@
const std = @import("std");
const Allocator = std.mem.Allocator;
 
const Comment = @import("comment.zig");
 
const Types = @import("../types.zig");
 
pub const Issue = @This();
@@ -63,7 +61,6 @@ title: []const u8,
desc: []const u8,
 
comment_data: ?[]const u8,
comments: ?[]Comment = null,
file: std.fs.File,
 
pub fn writeOut(self: Issue) !void {
@@ -94,29 +91,29 @@ pub fn readFile(a: std.mem.Allocator, idx: usize, file: std.fs.File) !Issue {
return issue;
}
 
pub fn getComments(self: *Issue, a: Allocator) ![]Comment {
if (self.comments) |_| return self.comments.?;
 
if (self.comment_data) |cd| {
self.comments = try Comment.loadFromData(a, cd);
}
return &[0]Comment{};
}
 
pub fn addComment(self: *Issue, a: Allocator, c: Comment) !void {
const target = (self.comments orelse &[0]Comment{}).len;
if (self.comments) |*comments| {
if (a.resize(comments.*, target + 1)) {
comments.*.len = target + 1;
} else {
self.comments = try a.realloc(comments.*, target + 1);
}
} else {
self.comments = try a.alloc(Comment, target + 1);
}
self.comments.?[target] = c;
try self.writeOut();
}
//pub fn getComments(self: *Issue, a: Allocator) ![]Comment {
// if (self.comments) |_| return self.comments.?;
//
// if (self.comment_data) |cd| {
// self.comments = try Comment.loadFromData(a, cd);
// }
// return &[0]Comment{};
//}
//
//pub fn addComment(self: *Issue, a: Allocator, c: Comment) !void {
// const target = (self.comments orelse &[0]Comment{}).len;
// if (self.comments) |*comments| {
// if (a.resize(comments.*, target + 1)) {
// comments.*.len = target + 1;
// } else {
// self.comments = try a.realloc(comments.*, target + 1);
// }
// } else {
// self.comments = try a.alloc(Comment, target + 1);
// }
// self.comments.?[target] = c;
// try self.writeOut();
//}
 
pub fn raze(self: Issue, a: std.mem.Allocator) void {
//if (self.alloc_data) |data| {
 
filename was Deleted added: 372, removed: 449, total 0
@@ -0,0 +1,258 @@
const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const endian = builtin.cpu.arch.endian();
const Sha256 = std.crypto.hash.sha2.Sha256;
const allocPrint = std.fmt.allocPrint;
const bufPrint = std.fmt.bufPrint;
const AnyWriter = std.io.AnyWriter;
const AnyReader = std.io.AnyReader;
const fmtSliceHexLower = std.fmt.fmtSliceHexLower;
 
const Humanize = @import("../humanize.zig");
const Types = @import("../types.zig");
 
pub const Message = @This();
const CMMT_VERSION: usize = 0;
pub const TYPE_PREFIX = "messages";
var datad: Types.Storage = undefined;
pub const HashType = [Sha256.digest_length]u8;
const ThreadIDType = Types.Thread.IDType;
 
state: usize = 0,
created: i64 = 0,
updated: i64 = 0,
src_tz: i32 = 0,
target: usize,
kind: Kind,
hash: HashType = undefined,
replies: ?usize = null,
 
pub const Flavor = enum(u16) {
comment,
_,
};
 
pub const Kind = union(Flavor) {
comment: Comment,
};
 
pub const Comment = struct {
author: []const u8,
message: []const u8,
 
pub fn readVersioned(a: Allocator, ver: usize, r: AnyReader) !Kind {
return switch (ver) {
0 => .{ .comment = .{
.author = try r.readUntilDelimiterAlloc(a, 0, 0xFF),
.message = try r.readUntilDelimiterAlloc(a, 0, 0xFFFF),
} },
else => unreachable,
};
}
 
pub fn writeOut(c: Comment, w: AnyWriter) !void {
try w.writeAll(c.author);
try w.writeInt(u8, 0, endian);
try w.writeAll(c.message);
try w.writeInt(u8, 0, endian);
}
 
pub fn updateHash(c: Comment, hash: *Sha256) void {
hash.update(c.author);
hash.update(c.message);
}
};
 
pub fn init(_: []const u8) !void {}
pub fn initType(stor: Types.Storage) !void {
datad = stor;
}
 
pub fn raze() void {
datad.close();
}
 
fn readVersioned(a: Allocator, reader: AnyReader, hash: []const u8) !Message {
const ver: usize = try reader.readInt(usize, endian);
return switch (ver) {
0 => return Message{
.state = try reader.readInt(usize, endian),
.created = try reader.readInt(i64, endian),
.updated = try reader.readInt(i64, endian),
.src_tz = try reader.readInt(i32, endian),
.hash = hash[0..Sha256.digest_length].*,
.target = try reader.readInt(ThreadIDType, endian),
.kind = switch (reader.readEnum(Flavor, endian) catch return error.EnumError) {
.comment => try Comment.readVersioned(a, ver, reader),
else => return error.UnsupportedKind,
},
.replies = if ((reader.readInt(usize, endian) catch null)) |r| if (r == 0) null else r else null,
},
else => error.UnsupportedVersion,
};
}
 
pub fn toHash(self: *Message) *const HashType {
var h = Sha256.init(.{});
h.update(std.mem.asBytes(&self.created));
h.update(std.mem.asBytes(&self.updated));
switch (self.kind) {
.comment => |c| c.updateHash(&h),
else => unreachable,
}
h.final(&self.hash);
return &self.hash;
}
 
pub fn writeNew(self: *Message, d: std.fs.Dir) !void {
var buf: [2048]u8 = undefined;
_ = self.toHash();
const filename = try bufPrint(&buf, "{x}.message", .{fmtSliceHexLower(&self.hash)});
var file = try d.createFile(filename, .{});
defer file.close();
const w = file.writer().any();
try self.writeOut(w);
}
 
pub fn commit(self: Message) !void {
var file = try openFile(self.hash);
defer file.close();
const writer = file.writer().any();
try self.writeOut(writer);
}
 
fn writeOut(self: Message, w: AnyWriter) !void {
try w.writeInt(usize, CMMT_VERSION, endian);
try w.writeInt(usize, self.state, endian);
try w.writeInt(i64, self.created, endian);
try w.writeInt(i64, self.updated, endian);
try w.writeInt(i32, self.src_tz, endian);
try w.writeInt(ThreadIDType, self.target, endian);
try w.writeInt(u16, @intFromEnum(self.kind), endian);
switch (self.kind) {
.comment => |c| try c.writeOut(w),
else => unreachable,
}
try w.writeInt(usize, if (self.replies) |r| r else 0, endian);
}
 
pub fn readFile(a: std.mem.Allocator, file: std.fs.File, hash: []const u8) !Message {
const reader = file.reader().any();
return readVersioned(a, reader, hash);
}
 
fn openFile(hash: HashType) !std.fs.File {
var buf: [2048]u8 = undefined;
const filename = try bufPrint(&buf, "{}.message", .{fmtSliceHexLower(hash[0..])});
return try datad.openFile(filename, .{ .mode = .read_write });
}
 
pub fn open(a: Allocator, hash: HashType) !Message {
var file = try openFile(hash);
defer file.close();
return try Message.readFile(a, file, hash[0..]);
}
 
pub fn newComment(tid: ThreadIDType, c: Comment) !Message {
var m = Message{
.target = tid,
.kind = .{ .comment = c },
.created = std.time.timestamp(),
.updated = std.time.timestamp(),
};
try m.writeNew(datad);
 
return m;
}
 
test "comment" {
var a = std.testing.allocator;
 
var c = Message{
.target = 0,
.kind = .{ .comment = .{
.author = "grayhatter",
.message = "test comment, please ignore",
} },
};
 
const hash = c.toHash();
try std.testing.expectEqualSlices(
u8,
&[_]u8{
0xA3, 0xE1, 0x6B, 0xD7, 0x46, 0xDC, 0x52, 0x43, 0x7B, 0xBC, 0x38, 0x99, 0x97, 0x0E, 0xD8, 0xEC,
0x77, 0xFD, 0x99, 0x16, 0x87, 0xA4, 0x19, 0x50, 0x12, 0x6A, 0x1D, 0xCD, 0xCA, 0x61, 0xC6, 0xB3,
},
hash,
);
 
var dir = std.testing.tmpDir(.{});
defer dir.cleanup();
 
try c.writeNew(dir.dir);
 
var buf: [2048]u8 = undefined;
const filename = try std.fmt.bufPrint(&buf, "{x}.message", .{std.fmt.fmtSliceHexLower(hash)});
try std.testing.expectEqualStrings(
"a3e16bd746dc52437bbc3899970ed8ec77fd991687a41950126a1dcdca61c6b3.message",
filename,
);
const blob = try dir.dir.readFileAlloc(a, filename, 0xFF);
defer a.free(blob);
 
try std.testing.expectEqualSlices(
u8,
&[_]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x72,
0x61, 0x79, 0x68, 0x61, 0x74, 0x74, 0x65, 0x72, 0x00, 0x74, 0x65, 0x73, 0x74, 0x20, 0x63, 0x6F,
0x6D, 0x6D, 0x65, 0x6E, 0x74, 0x2C, 0x20, 0x70, 0x6C, 0x65, 0x61, 0x73, 0x65, 0x20, 0x69, 0x67,
0x6E, 0x6F, 0x72, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
},
blob,
);
}
 
test Message {
const a = std.testing.allocator;
var tempdir = std.testing.tmpDir(.{});
defer tempdir.cleanup();
try Types.init(try tempdir.dir.makeOpenPath("datadir", .{ .iterate = true }));
 
var c = try Message.newComment(0, .{ .author = "author", .message = "message" });
 
// LOL, you thought
const mask: i64 = ~@as(i64, 0xffffff);
c.created = std.time.timestamp() & mask;
c.updated = std.time.timestamp() & mask;
 
var out = std.ArrayList(u8).init(a);
defer out.clearAndFree();
const outw = out.writer().any();
try c.writeOut(outw);
 
const v0: Message = undefined;
const v0_bin: []const u8 = &[_]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x75,
0x74, 0x68, 0x6F, 0x72, 0x00, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
};
try std.testing.expectEqualSlices(u8, v0_bin, out.items);
const v1: Message = undefined;
// TODO... eventually
_ = v0;
_ = v1;
 
const v1_bin: []const u8 = &[_]u8{
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x67, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0x75,
0x74, 0x68, 0x6F, 0x72, 0x00, 0x6D, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00,
};
try std.testing.expectEqualSlices(u8, v1_bin, out.items);
}
 
src/types/thread.zig added: 372, removed: 449, total 0
@@ -2,8 +2,9 @@ const std = @import("std");
const builtin = @import("builtin");
const Allocator = std.mem.Allocator;
const endian = builtin.cpu.arch.endian();
const sha256 = std.crypto.hash.sha2.Sha256;
 
const Comment = @import("comment.zig");
pub const Message = @import("message.zig");
const Delta = @import("delta.zig");
const State = Delta.State;
 
@@ -14,6 +15,8 @@ pub const Thread = @This();
pub const TYPE_PREFIX = "threads";
const THREADS_VERSION: usize = 0;
var datad: Types.Storage = undefined;
pub const IDType = usize;
pub const HashType = [sha256.digest_length]u8;
 
pub fn init(_: []const u8) !void {}
pub fn initType(stor: Types.Storage) !void {
@@ -39,33 +42,20 @@ fn readVersioned(a: Allocator, idx: usize, reader: *std.io.AnyReader) !Thread {
};
}
 
index: usize,
index: IDType,
state: State = .{},
created: i64 = 0,
updated: i64 = 0,
delta_hash: [32]u8 = [_]u8{0} ** 32,
hash: [32]u8 = [_]u8{0} ** 32,
delta_hash: HashType = [_]u8{0} ** 32,
hash: HashType = [_]u8{0} ** 32,
 
message_data: ?[]const u8 = null,
messages: ?[]Message = null,
 
pub const MessageTypes = enum {
comment,
unknown,
};
 
pub const Message = union(MessageTypes) {
comment: Comment,
unknown: void,
};
 
pub fn commit(self: Thread) !void {
if (self.messages) |msgs| {
// Make a best effort to save/protect all data
for (msgs) |msg| switch (msg) {
.comment => |cmt| cmt.commit() catch continue,
.unknown => {},
};
for (msgs) |msg| msg.commit() catch continue;
}
const file = try openFile(self.index);
defer file.close();
@@ -81,10 +71,7 @@ fn writeOut(self: Thread, writer: std.io.AnyWriter) !void {
try writer.writeAll(&self.delta_hash);
 
if (self.messages) |msgs| {
for (msgs) |*msg| switch (msg.*) {
.comment => |*c| try writer.writeAll(c.toHash()),
.unknown => {},
};
for (msgs) |*msg| try writer.writeAll(msg.toHash());
}
try writer.writeAll("\x00");
}
@@ -109,7 +96,7 @@ fn loadFromData(a: Allocator, cd: []const u8) ![]Message {
const msgs = try a.alloc(Message, count);
var data = cd[0..];
for (msgs, 0..count) |*c, i| {
c.* = .{ .comment = Comment.open(a, data[0..32]) catch |err| {
c.* = Message.open(a, data[0..32].*) catch |err| {
std.debug.print(
\\Error loading msg data {} of {}
\\error: {} target {any}
@@ -117,7 +104,7 @@ fn loadFromData(a: Allocator, cd: []const u8) ![]Message {
, .{ i, count, err, data[0..32] });
data = data[32..];
continue;
} };
};
data = data[32..];
}
return msgs;
@@ -133,7 +120,7 @@ pub fn getMessages(self: Thread) ![]Message {
return error.NotLoaded;
}
 
pub fn addComment(self: *Thread, a: Allocator, c: Comment) !void {
pub fn newComment(self: *Thread, a: Allocator, c: Message.Comment) !void {
if (self.messages) |*messages| {
if (a.resize(messages.*, messages.len + 1)) {
messages.*.len += 1;
@@ -143,7 +130,7 @@ pub fn addComment(self: *Thread, a: Allocator, c: Comment) !void {
} else {
self.messages = try a.alloc(Message, 1);
}
self.messages.?[self.messages.?.len - 1] = .{ .comment = c };
self.messages.?[self.messages.?.len - 1] = try Message.newComment(self.index, c);
self.updated = std.time.timestamp();
try self.commit();
}
@@ -221,7 +208,7 @@ pub fn new(delta: Delta) !Thread {
return thread;
}
 
fn openFile(index: usize) !std.fs.File {
fn openFile(index: IDType) !std.fs.File {
var buf: [2048]u8 = undefined;
const filename = std.fmt.bufPrint(&buf, "{x}.thread", .{index}) catch return error.InvalidTarget;
return try datad.openFile(filename, .{ .mode = .read_write });