download patch
commit 7a470a36b217fa4e0a5a4707a27f49a9d2d50791
Author: tri <tri@thac.loan>
Date: Sat Oct 4 14:49:46 2025 +0700
write blob pages to /objects/<hash>/index.html
So that it matches commit pages url scheme too.
diff --git a/src/main.zig b/src/main.zig
index c67d5ac..e05e18a 100644
--- a/src/main.zig
+++ b/src/main.zig
pub fn writeHomePage(arena: mem.Allocator, dir: fs.Dir, repos: []RepoSummary) !v
else
try html.escapeAlloc(arena, repo.description), // 2
repo.commit_count, // 3
- repo.last_commit_msg, // 4
+ try html.escapeAlloc(arena, repo.last_commit_msg), // 4
repo.last_commit_time, // 5
},
);
pub fn writeRepoPage(
var treeWalker = try git.walkTree(arena, args.in_repo_dir);
while (try treeWalker.next()) |src_file| {
- const file_name = try std.fmt.allocPrint(arena, "{s}.html", .{src_file.hash});
-
try writer.print(
\\<tr>
- \\ <td><a href="{2s}/{3s}">{0s}</a></td>
- \\ <td style="opacity:0.4">{1s}</td>
+ \\ <td><a href="{0s}/{1s}/">{2s}</a></td>
+ \\ <td style="opacity:0.4">{3s}</td>
\\</tr>
\\
, .{
+ constants.web_objects_path,
+ src_file.hash,
try html.escapeAlloc(arena, src_file.path),
try utils.humanReadableSize(arena, src_file.size),
- constants.web_objects_path,
- file_name,
});
- try writeFilePage(args, objects_dir, src_file, file_name);
+ try writeBlobPage(args, objects_dir, src_file);
}
try writer.writeAll(
pub fn writeCommitPage(
try writer.flush();
}
-pub fn writeFilePage(
+pub fn writeBlobPage(
args: *const RepoArgs,
- out_dir: fs.Dir,
+ objects_dir: fs.Dir,
src_file: git.File,
- file_name: []const u8,
) !void {
const arena = args.arena;
- _ = out_dir.statFile(file_name) catch |err| {
- switch (err) {
- error.FileNotFound => {
- const object_type = try git.objectType(arena, args.in_repo_dir, src_file.hash);
- if (object_type != .blob) {
- return;
- }
+ var out_dir = try objects_dir.makeOpenPath(src_file.hash, .{});
+ defer out_dir.close();
- println(" writing {s}: {s}/{s}", .{
- args.repo_name,
- constants.web_objects_path,
- file_name,
- });
- var file = try out_dir.createFile(file_name, .{});
- errdefer out_dir.deleteFile(file_name) catch {};
- defer file.close();
-
- var buf: [4096]u8 = undefined;
- var file_writer = file.writer(&buf);
- const writer = &file_writer.interface;
-
- const file_label = try std.fmt.allocPrint(
- arena,
- "{s}: {s}",
- .{ src_file.hash[0..10], src_file.path },
- );
-
- try templates.base_start(arena, writer, .{
- .title = try std.fmt.allocPrint(
- arena,
- "{s} - {s}",
- .{ file_label, args.repo_name },
- ),
- .breadcrumbs = &.{ .{
- .href = "/",
- .text = "repos",
- }, .{
- .href = "../",
- .text = args.repo_name,
- }, .{
- .text = file_label,
- } },
- });
+ const index_html = "index.html";
+
+ const should_skip = blk: {
+ _ = out_dir.statFile(index_html) catch |err| {
+ switch (err) {
+ error.FileNotFound => break :blk false,
+ else => return err,
+ }
+ };
+ break :blk true;
+ };
+ if (should_skip) return;
+
+ println(" writing {s} blob: {s}", .{ args.repo_name, src_file.hash });
+
+ const object_type = try git.objectType(arena, args.in_repo_dir, src_file.hash);
+ if (object_type != .blob) {
+ return;
+ }
+
+ var file = try out_dir.createFile(index_html, .{});
+ errdefer out_dir.deleteFile(index_html) catch {};
+ defer file.close();
+
+ var buf: [4096]u8 = undefined;
+ var file_writer = file.writer(&buf);
+ const writer = &file_writer.interface;
+
+ const file_label = try std.fmt.allocPrint(
+ arena,
+ "{s}: {s}",
+ .{ src_file.hash[0..10], src_file.path },
+ );
+
+ try templates.base_start(arena, writer, .{
+ .title = try std.fmt.allocPrint(
+ arena,
+ "{s} - {s}",
+ .{ file_label, args.repo_name },
+ ),
+ .breadcrumbs = &.{ .{
+ .href = "/",
+ .text = "repos",
+ }, .{
+ .href = "../../",
+ .text = args.repo_name,
+ }, .{
+ .text = file_label,
+ } },
+ });
+
+ try writer.print(
+ \\<p>size: {0s}</p>
+ , .{
+ try utils.humanReadableSize(arena, src_file.size),
+ });
+
+ try writer.writeAll(
+ \\<pre class="blob-content">
+ \\
+ );
+ // This wastefully reads the whole file into memory.
+ // TODO: Try to implement a git.catFile() that only reads the
+ // first n bytes.
+ const file_content = try git.catFile(
+ arena,
+ args.in_repo_dir,
+ src_file.hash,
+ );
+ const blob_type = utils.blobType(src_file.path);
+
+ if (file_content.len <= 1024 * 1024 * 16 and
+ (blob_type == .image or blob_type == .video))
+ {
+ const src_file_name =
+ if (mem.lastIndexOfScalar(u8, src_file.path, '/')) |slash_idx|
+ src_file.path[slash_idx + 1 ..]
+ else
+ src_file.path;
+
+ // write the blob's actual content next to its html page
+ var blob_file = try out_dir.createFile(src_file_name, .{});
+ errdefer out_dir.deleteFile(src_file_name) catch {};
+ defer blob_file.close();
+ try blob_file.writeAll(file_content);
+
+ // write <img> markup in blob page
+ const escaped_file_name = try html.escapeAlloc(arena, src_file_name);
+
+ switch (blob_type) {
+ .image => {
try writer.print(
- \\<p>size: {0s}</p>
+ \\<img src="{0s}" alt="{0s}">
+ \\
, .{
- try utils.humanReadableSize(arena, src_file.size),
+ escaped_file_name,
});
-
- try writer.writeAll(
- \\<pre class="blob-content">
- \\
- );
-
- // This wastefully reads the whole file into memory.
- // TODO: Try to implement a git.catFile() that only reads the
- // first n bytes.
- const file_content = try git.catFile(
- arena,
- args.in_repo_dir,
- src_file.hash,
- );
- const blob_type = utils.blobType(src_file.path);
-
- if (file_content.len <= 1024 * 1024 * 16 and
- (blob_type == .image or blob_type == .video))
- {
- const src_file_name =
- if (mem.lastIndexOfScalar(u8, src_file.path, '/')) |slash_idx|
- src_file.path[slash_idx + 1 ..]
- else
- src_file.path;
-
- const blob_file_name = try std.fmt.allocPrint(
- arena,
- "{s}.{s}",
- .{ src_file.hash, src_file_name },
- );
-
- // write the blob's actual content next to its html page
- var blob_file = try out_dir.createFile(blob_file_name, .{});
- errdefer out_dir.deleteFile(blob_file_name) catch {};
- defer blob_file.close();
- try blob_file.writeAll(file_content);
-
- // write <img> markup in blob page
- const escaped_file_name = try html.escapeAlloc(arena, src_file_name);
-
- switch (blob_type) {
- .image => {
- try writer.print(
- \\<img src="{0s}.{1s}" alt="{1s}">
- \\
- , .{
- src_file.hash,
- escaped_file_name,
- });
- },
- .video => {
- try writer.print(
- \\<video src="{0s}.{1s}" controls></video>
- \\
- , .{
- src_file.hash,
- escaped_file_name,
- });
- },
- else => return error.UnexpectedBlobType,
- }
- } else if (git.isBinary(file_content)) {
- try writer.writeAll(
- \\<span style="opacity: 0.5">(binary data)</span>
- \\
- );
- } else {
- try html.escape(writer, file_content);
- }
-
- try writer.writeAll(
- \\</pre>
+ },
+ .video => {
+ try writer.print(
+ \\<video src="{0s}" controls></video>
\\
- );
-
- try writer.flush();
+ , .{
+ escaped_file_name,
+ });
},
- else => return err,
+ else => return error.UnexpectedBlobType,
}
- };
+ } else if (git.isBinary(file_content)) {
+ try writer.writeAll(
+ \\<span style="opacity: 0.5">(binary data)</span>
+ \\
+ );
+ } else {
+ try html.escape(writer, file_content);
+ }
+
+ try writer.writeAll(
+ \\</pre>
+ \\
+ );
+
+ try writer.flush();
}
test "all" {