srctree

Gregory Mullen parent 821f1ca5 208814dc
half implentation for direct replies

this feature is unfinished, but I can't decide if comments should reply, of if threads should be the container for direct replies.
README.md added: 137, removed: 37, total 100
@@ -15,6 +15,7 @@ Good luck!
## TODO
In an unsorted order
- [ ] srctree
- [ ] internal database upgrade scripts
- [ ] manual code sync
- [x] view code
- [x] public http clone
 
src/endpoints/repos/commits.zig added: 137, removed: 37, total 100
@@ -140,6 +140,7 @@ fn commitHtml(ctx: *Context, sha: []const u8, repo_name: []const u8, repo: Git.R
.author = try Bleach.sanitizeAlloc(ctx.alloc, thr_cmt.author, .{}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(thr_cmt.updated)}),
.message = try Bleach.sanitizeAlloc(ctx.alloc, thr_cmt.message, .{}),
.direct_reply = null,
} };
}
} else |err| {
 
src/endpoints/repos/diffs.zig added: 137, removed: 37, total 100
@@ -7,6 +7,7 @@ const splitScalar = std.mem.splitScalar;
const indexOf = std.mem.indexOf;
const indexOfAny = std.mem.indexOfAny;
const parseInt = std.fmt.parseInt;
const fmtSliceHexLower = std.fmt.fmtSliceHexLower;
 
const Commits = @import("commits.zig");
 
@@ -52,10 +53,18 @@ fn isHex(input: []const u8) ?usize {
}
 
pub fn router(ctx: *Context) Error!Route.Callable {
std.debug.assert(std.mem.eql(u8, "diffs", ctx.uri.next().?));
if (!eql(u8, "diffs", ctx.uri.next() orelse return error.Unrouteable))
return error.Unrouteable;
const verb = ctx.uri.peek() orelse return Route.router(ctx, &routes);
 
if (isHex(verb)) |_| {
const uri_save = ctx.uri.index;
defer ctx.uri.index = uri_save;
_ = ctx.uri.next();
if (ctx.uri.peek()) |action| {
if (eql(u8, action, "direct_reply")) return directReply;
}
 
return view;
}
 
@@ -225,6 +234,13 @@ fn newComment(ctx: *Context) Error!void {
return error.Unknown;
}
 
pub fn directReply(ctx: *Context) Error!void {
_ = ctx.uri.next().?;
_ = ctx.uri.next().?;
std.debug.print("{s}\n", .{ctx.uri.next().?});
return error.Unknown;
}
 
pub fn patchStruct(a: Allocator, patch: *Patch.Patch, unified: bool) !Template.Structs.PatchHtml {
patch.parse(a) catch |err| {
if (std.mem.indexOf(u8, patch.blob, "\nMerge: ") == null) {
@@ -531,6 +547,7 @@ fn view(ctx: *Context) Error!void {
.author = try Bleach.sanitizeAlloc(ctx.alloc, comment.author, .{}),
.date = try allocPrint(ctx.alloc, "{}", .{Humanize.unix(comment.updated)}),
.message = translateComment(ctx.alloc, comment.message, patch) catch unreachable,
.direct_reply = .{ .uri = try allocPrint(ctx.alloc, "{}/direct_reply/{x}", .{ index, fmtSliceHexLower(comment.hash[0..]) }) },
} };
}
} else |err| {
 
src/types/comment.zig added: 137, removed: 37, total 100
@@ -4,23 +4,31 @@ 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 Writer = Types.Writer;
 
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: ?[sha256.digest_length]u8 = null,
 
pub fn init(_: []const u8) !void {}
pub fn initType(stor: Types.Storage) !void {
datad = stor;
@@ -40,7 +48,7 @@ pub fn charToKind(c: u8) TargetKind {
};
}
 
fn readVersioned(a: Allocator, file: std.fs.File) !Comment {
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) {
@@ -49,6 +57,7 @@ fn readVersioned(a: Allocator, file: std.fs.File) !Comment {
.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) },
@@ -72,6 +81,41 @@ fn readVersioned(a: Allocator, file: std.fs.File) !Comment {
},
.author = try reader.readUntilDelimiterAlloc(a, 0, 0xFFFF),
.message = try reader.readAllAlloc(a, 0xFFFF),
.replies = null,
},
1 => 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 = if (reader.readBoundedBytes(sha256.digest_length)) |bb|
bb.slice()[0..sha256.digest_length].*
else |_|
null,
},
else => error.UnsupportedVersion,
};
@@ -123,17 +167,6 @@ pub const Targets = union(TargetKind) {
reply: Reply,
};
 
state: usize = 0,
created: i64 = 0,
tz: i32 = 0,
updated: i64 = 0,
target: Targets = .{ .nos = {} },
 
author: []const u8,
message: []const u8,
 
hash: [sha256.digest_length]u8 = undefined,
 
pub fn toHash(self: *Comment) *const [sha256.digest_length]u8 {
var h = sha256.init(.{});
h.update(self.author);
@@ -147,14 +180,14 @@ pub fn toHash(self: *Comment) *const [sha256.digest_length]u8 {
pub fn writeNew(self: *Comment, d: std.fs.Dir) !void {
var buf: [2048]u8 = undefined;
_ = self.toHash();
const filename = try std.fmt.bufPrint(&buf, "{x}.comment", .{std.fmt.fmtSliceHexLower(&self.hash)});
const filename = try bufPrint(&buf, "{x}.comment", .{fmtSliceHexLower(&self.hash)});
var file = try d.createFile(filename, .{});
defer file.close();
const w = file.writer();
try self.writeStruct(w);
const w = file.writer().any();
try self.writeOut(w);
}
 
fn writeStruct(self: Comment, w: Writer) !void {
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);
@@ -172,10 +205,11 @@ fn writeStruct(self: Comment, w: Writer) !void {
try w.writeAll(self.author);
try w.writeAll("\x00");
try w.writeAll(self.message);
if (self.replies) |r| try w.writeAll(r[0..]);
}
 
pub fn readFile(a: std.mem.Allocator, file: std.fs.File) !Comment {
return readVersioned(a, file);
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 {
@@ -194,10 +228,10 @@ pub fn contextBuilder(self: Comment, a: Allocator, ctx: *Template.Context) !void
 
pub fn open(a: Allocator, hash: []const u8) !Comment {
var buf: [2048]u8 = undefined;
const filename = try std.fmt.bufPrint(&buf, "{x}.comment", .{std.fmt.fmtSliceHexLower(hash)});
const filename = try bufPrint(&buf, "{x}.comment", .{fmtSliceHexLower(hash)});
var file = try datad.openFile(filename, .{});
defer file.close();
return try Comment.readFile(a, file);
return try Comment.readFile(a, file, hash);
}
 
pub fn new(ath: []const u8, msg: []const u8) !Comment {
@@ -281,3 +315,43 @@ test "comment" {
);
// 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, 0xfffff);
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, 0x30, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 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, 0x30, 0x67, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 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);
}
 
static/main.css added: 137, removed: 37, total 100
@@ -575,6 +575,7 @@ comment {
border: 1px solid gray;
display: block;
padding: 8px;
overflow: auto
}
input, textarea {
display: block;
@@ -594,8 +595,9 @@ comment {
height: 25px;
padding: 4px 0 4px 12px;
}
message {
overflow: auto
.muted.reply {
text-align: right;
float: right;
}
div.coderef {
border-top: 1px solid black;
 
templates/_comment.html added: 137, removed: 37, total 100
@@ -3,5 +3,10 @@
<Author>
<date><Date></date>
</context>
<message><Message></message>
<message>
<Message>
<With DirectReply>
<a class="muted reply" href="<Uri>">Reply</a>
</With>
</message>
</comment>