srctree

Gregory Mullen parent b454eb37 4f55f378
migrate commit list view to typed syntax

src/endpoints/repos/commits.zig added: 106, removed: 97, total 9
@@ -3,6 +3,7 @@ const Allocator = std.mem.Allocator;
const allocPrint = std.fmt.allocPrint;
const bufPrint = std.fmt.bufPrint;
const endsWith = std.mem.endsWith;
const eql = std.mem.eql;
 
const Diffs = @import("diffs.zig");
 
@@ -21,6 +22,7 @@ const Template = @import("../../template.zig");
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;
@@ -32,19 +34,22 @@ const Thread = Types.Thread;
const UriIter = Route.UriIter;
 
pub const routes = [_]Route.MatchRouter{
ROUTE("", commits),
ROUTE("", commitsView),
GET("before", commitsBefore),
};
 
const CommitPage = Template.PageData("commit.html");
const CommitsListPage = Template.PageData("commit-list.html");
 
const AddComment = struct {
text: []const u8,
};
 
pub fn router(ctx: *Context) Error!Route.Callable {
const rd = RouteData.make(&ctx.uri) orelse return commits;
const rd = RouteData.make(&ctx.uri) orelse return commitsView;
if (rd.verb != null and std.mem.eql(u8, "commit", rd.verb.?))
return viewCommit;
return commits;
return commitsView;
}
 
fn newComment(ctx: *Context) Error!void {
@@ -68,8 +73,6 @@ pub fn patchContext(a: Allocator, patch: *Patch.Patch) ![]Template.Context {
return try patch.diffsContextSlice(a);
}
 
const CommitPage = Template.PageData("commit.html");
 
fn commitHtml(ctx: *Context, sha: []const u8, repo_name: []const u8, repo: Git.Repo) Error!void {
if (!Git.commitish(sha)) {
std.debug.print("Abusive ''{s}''\n", .{sha});
@@ -115,7 +118,7 @@ fn commitHtml(ctx: *Context, sha: []const u8, repo_name: []const u8, repo: Git.R
diffstat.additions,
diffstat.deletions,
});
const meta_head = Template.Structs.MetaHeadHtml{
const meta_head = S.MetaHeadHtml{
.open_graph = .{
.title = og_title,
.desc = Bleach.sanitizeAlloc(ctx.alloc, current.message, .{}) catch unreachable,
@@ -185,7 +188,7 @@ pub fn commitPatch(ctx: *Context, sha: []const u8, repo: Git.Repo) Error!void {
 
pub fn viewCommit(ctx: *Context) Error!void {
const rd = RouteData.make(&ctx.uri) orelse return error.Unrouteable;
if (rd.verb == null) return commits(ctx);
if (rd.verb == null) return commitsView(ctx);
 
const sha = rd.noun orelse return error.Unrouteable;
if (std.mem.indexOf(u8, sha, ".") != null and !std.mem.endsWith(u8, sha, ".patch")) return error.Unrouteable;
@@ -278,102 +281,94 @@ pub fn htmlCommit(a: Allocator, c: Git.Commit, repo: []const u8, comptime top: b
return dom.done();
}
 
fn commitContext(a: Allocator, c: Git.Commit, repo: []const u8, comptime _: bool) !Template.Context {
var ctx = Template.Context.init(a);
 
try ctx.putSlice("Sha", c.sha.hex[0..8]);
try ctx.putSlice("Uri", try std.fmt.allocPrint(a, "/repo/{s}/commit/{s}", .{ repo, c.sha.hex[0..8] }));
// TODO handle error.NotImplemented
try ctx.putSlice("Msg_title", Bleach.sanitizeAlloc(a, c.title, .{}) catch unreachable);
try ctx.putSlice("Msg", Bleach.sanitizeAlloc(a, c.body, .{}) catch unreachable);
//if (top) "top" else "foot", null, null));
try ctx.putSlice("Author", c.author.name);
const parents = try a.alloc(Template.Context, c.parent.len);
errdefer a.free(parents);
for (parents, c.parent) |*par, par_cmt| {
// TODO leaks on err
var pctx = Template.Context.init(a);
if (par_cmt == null) continue;
 
try pctx.putSlice("Parent", par_cmt.?.hex[0..8]);
try pctx.putSlice("Parent_uri", try std.fmt.allocPrint(
a,
"/repo/{s}/commit/{s}",
.{ repo, par_cmt.?.hex[0..8] },
));
par.* = pctx;
fn commitContext(a: Allocator, c: Git.Commit, repo_name: []const u8) !S.Commits {
var parcount: usize = 0;
for (c.parent) |p| {
if (p != null) parcount += 1;
}
try ctx.putBlock("Parents", parents);
return ctx;
const parents = try a.alloc(S.CommitParent, parcount);
errdefer a.free(parents);
var par_ptr: [*]const ?Git.SHA = &c.parent;
for (0..parcount) |i| {
var lim = 9 - i;
while (lim > 0 and par_ptr[i] == null) {
par_ptr += 1;
lim -= 1;
}
parents[i] = .{
.uri = try allocPrint(a, "/repo/{s}/commit/{s}", .{ repo_name, par_ptr[i].?.hex[0..8] }),
.sha = try allocPrint(a, "{s}", .{par_ptr[i].?.hex[0..8]}),
};
}
return .{
.sha = try allocPrint(a, "{s}", .{c.sha.hex[0..8]}),
.uri = try allocPrint(a, "/repo/{s}/commit/{s}", .{ repo_name, c.sha.hex[0..8] }),
.msg_title = try Bleach.sanitizeAlloc(a, c.title, .{}),
.msg = try Bleach.sanitizeAlloc(a, c.body, .{}),
.author = c.author.name,
.commit_parent = parents,
};
}
 
fn buildList(
a: Allocator,
repo: Git.Repo,
name: []const u8,
before: ?[]const u8,
elms: []Template.Context,
before: ?Git.SHA,
count: usize,
outsha: *Git.SHA,
) ![]Template.Context {
return buildListBetween(a, repo, name, null, before, elms, outsha);
) ![]S.Commits {
return buildListBetween(a, repo, name, null, before, count, outsha);
}
 
fn buildListBetween(
a: Allocator,
repo: Git.Repo,
name: []const u8,
left: ?[]const u8,
right: ?[]const u8,
elms: []Template.Context,
left: ?Git.SHA,
right: ?Git.SHA,
count: usize,
outsha: *Git.SHA,
) ![]Template.Context {
) ![]S.Commits {
var commits = try a.alloc(S.Commits, count);
var current: Git.Commit = repo.headCommit(a) catch return error.Unknown;
if (right) |r| {
std.debug.assert(r.len <= 40);
const min = @min(r.len, current.sha.hex.len);
while (!std.mem.eql(u8, r, current.sha.hex[0..min])) {
current = current.toParent(a, 0, &repo) catch {
while (!current.sha.eqlIsh(r)) {
current = current.toParent(a, 0, &repo) catch |err| {
std.debug.print("unable to build commit history\n", .{});
return elms[0..0];
return err;
};
}
current = current.toParent(a, 0, &repo) catch {
current = current.toParent(a, 0, &repo) catch |err| {
std.debug.print("unable to build commit history\n", .{});
return elms[0..0];
return err;
};
}
var count: usize = 0;
for (elms, 1..) |*c, i| {
count = i;
outsha.* = Git.SHA.init(current.sha.bin[0..]);
c.* = try commitContext(a, current, name, false);
if (left) |l| {
const min = @min(l.len, current.sha.hex.len);
if (std.mem.eql(u8, l, current.sha.hex[0..min])) break;
}
current = current.toParent(a, 0, &repo) catch {
break;
};
var found: usize = 0;
for (commits, 1..) |*c, i| {
c.* = try commitContext(a, current, name);
found = i;
outsha.* = current.sha;
if (left) |l| if (current.sha.eqlIsh(l)) break;
current = current.toParent(a, 0, &repo) catch break;
}
return elms[0..count];
if (a.resize(commits, found)) {
commits.len = found;
} else unreachable; // lol, good luck!
return commits;
}
 
pub fn commits(ctx: *Context) Error!void {
pub fn commitsView(ctx: *Context) Error!void {
const rd = RouteData.make(&ctx.uri) orelse return error.Unrouteable;
 
if (ctx.uri.next()) |next| {
if (!std.mem.eql(u8, next, "commits")) return error.Unrouteable;
}
 
var commitish: ?[]const u8 = null;
if (ctx.uri.next()) |next| {
if (std.mem.eql(u8, next, "before")) {
if (ctx.uri.next()) |before| {
if (!Git.commitish(before)) return error.Unrouteable;
commitish = before;
}
}
}
var commitish: ?Git.SHA = null;
if (ctx.uri.next()) |next| if (eql(u8, next, "before")) {
if (ctx.uri.next()) |before| commitish = Git.SHA.initPartial(before);
};
 
// TODO use left and right commit finding
//if (commitish) |cmish| {
@@ -395,15 +390,11 @@ pub fn commits(ctx: *Context) Error!void {
repo.loadData(ctx.alloc) catch return error.Unknown;
defer repo.raze();
 
const commits_b = try ctx.alloc.alloc(Template.Context, 50);
var last_sha: Git.SHA = undefined;
const cmts_list = try buildList(ctx.alloc, repo, rd.name, commitish, commits_b, &last_sha);
const cmts_list = buildList(ctx.alloc, repo, rd.name, commitish, 50, &last_sha) catch
return error.Unknown;
 
const before_txt = try std.fmt.allocPrint(ctx.alloc, "/repo/{s}/commits/before/{s}", .{
rd.name,
last_sha.hex[0..8],
});
return sendCommits(ctx, cmts_list, before_txt);
return sendCommits(ctx, cmts_list, rd.name, last_sha.hex[0..8]);
}
 
pub fn commitsBefore(ctx: *Context) Error!void {
@@ -417,23 +408,28 @@ pub fn commitsBefore(ctx: *Context) Error!void {
var repo = Git.Repo.init(dir) catch return error.Unknown;
repo.loadData(ctx.alloc) catch return error.Unknown;
 
const before = ctx.uri.next();
const before: Git.SHA = if (ctx.uri.next()) |bf| Git.SHA.initPartial(bf);
const commits_b = try ctx.alloc.alloc(Template.Context, 50);
var last_sha: [8]u8 = undefined;
var last_sha: Git.SHA = undefined;
const cmts_list = try buildList(ctx.alloc, repo, rd.name, before, commits_b, &last_sha);
const before_txt = try std.fmt.allocPrint(ctx.alloc, "/repo/{s}/commits/before/{s}", .{ rd.name, last_sha });
return sendCommits(ctx, cmts_list, before_txt);
return sendCommits(ctx, cmts_list, rd.name, last_sha[0..]);
}
 
fn sendCommits(ctx: *Context, list: []Template.Context, before_txt: []const u8) Error!void {
var tmpl = Template.find("commit-list.html");
fn sendCommits(ctx: *Context, list: []const S.Commits, repo_name: []const u8, sha: []const u8) Error!void {
const meta_head = S.MetaHeadHtml{ .open_graph = .{} };
 
try ctx.putContext("Commits", .{ .block = list });
var page = CommitsListPage.init(.{
.meta_head = meta_head,
.body_header = .{ .nav = .{
.nav_buttons = &try Repos.navButtons(ctx),
.nav_auth = undefined,
} },
.commits = list,
.after_commits = .{
.repo_name = repo_name,
.sha = sha,
},
});
 
_ = ctx.addElements(ctx.alloc, "After", &[_]HTML.E{
try HTML.linkBtnAlloc(ctx.alloc, "More", before_txt),
}) catch return error.Unknown;
 
ctx.response.status = .ok;
ctx.sendTemplate(&tmpl) catch return error.Unknown;
try ctx.sendPage(&page);
}
 
src/git.zig added: 106, removed: 97, total 9
@@ -65,6 +65,7 @@ pub const SHA = struct {
} else unreachable;
}
 
/// TODO return error, and validate it's actually hex
pub fn initPartial(sha: []const u8) SHA {
var buf: [40]u8 = ("0" ** 40).*;
for (buf[0..sha.len], sha[0..]) |*dst, src| dst.* = src;
@@ -90,6 +91,18 @@ pub const SHA = struct {
}
return bin;
}
 
pub fn eql(self: SHA, peer: SHA) bool {
if (self.partial == true) @panic("not implemented");
if (self.partial != peer.partial) return false;
return std.mem.eql(u8, self.bin[0..20], peer.bin[0..20]);
}
 
pub fn eqlIsh(self: SHA, peer: SHA) bool {
if (self.partial == true) @panic("not implemented");
if (peer.partial != true) return self.eql(peer);
return std.mem.eql(u8, self.bin[0..peer.binlen], peer.bin[0..peer.binlen]);
}
};
 
pub const Object = struct {
 
templates/commit-list.html added: 106, removed: 97, total 9
@@ -11,17 +11,17 @@
<commit>
<data>
<a href="<Uri ORELSE #>" class=muted><Sha></a>
<span><Msg_title ORNULL></span>
<span><MsgTitle ORNULL></span>
<br />
<Msg>
</data>
<foot>
<author><Author></author>
<span>parent <a href="<Parent_uri>"><Parent></a></span>
<For CommitParent><span>parent <a href="<Uri>"><Sha></a></span></For>
</foot>
</commit>
</For>
<After ORNULL>
<With AfterCommits><a href="/repo/<RepoName>/commits/before/<Sha>" class="btn">More</a></With>
</content>
</body>
</html>