size: 4 KiB

const std = @import("std");
const mem = std.mem;
const Io = std.Io;
const fmt = std.fmt;
const html = @import("html.zig");
const utils = @import("utils.zig");
const constants = @import("constants.zig");

pub const Crumb = struct {
    href: ?[]const u8 = null,
    text: []const u8,
};

pub const BaseArgs = struct {
    title: ?[]const u8 = null,
    breadcrumbs: []const Crumb,
};

// Remember to close with base_end()
pub fn base_start(arena: mem.Allocator, writer: *Io.Writer, args: BaseArgs) !void {
    try writer.print(
        \\<!doctype html>
        \\<html lang="en">
        \\  <head>
        \\    <meta charset="utf-8" />
        \\    <title>{0s}</title>
        \\    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        \\    <link rel="stylesheet" href="/_/_khoe-hang/style.css">
        \\    <script src="/_/_khoe-hang/script.js"></script>
        \\  </head>
        \\  <body>
        \\    <header>
        \\      <div id="breadcrumbs">
        \\        /
    , .{
        if (args.title) |title|
            try html.escapeAlloc(
                arena,
                try fmt.allocPrint(arena, "{s} | khoe", .{title}),
            )
        else
            "khoe",
    });

    for (args.breadcrumbs) |crumb| {
        const escaped_text = try html.escapeAlloc(arena, crumb.text);
        if (crumb.href) |href| {
            try writer.print(
                \\<a href="{0s}">{1s}</a>/
            , .{ href, escaped_text });
        } else {
            try writer.print(
                \\<span>{0s}</span>
            , .{escaped_text});
        }
    }

    try writer.writeAll(
        \\      </div>
        \\    </header>
        \\
    );
}

pub fn base_end(writer: *Io.Writer) !void {
    try writer.writeAll(
        \\  </body>
        \\</html>
        \\
    );
}

pub fn repo_metadata(arena: mem.Allocator, writer: *Io.Writer, args: struct {
    description: []const u8,
    repo_size: u64,
    commit_count: usize,
    site_url: [*:0]const u8,
    repo_name: []const u8,
    git_dir_path: []const u8,
}) !void {
    const clone_path =
        if (mem.eql(u8, args.git_dir_path, "."))
            args.repo_name
        else
            try std.fmt.allocPrint(
                arena,
                "{s}/{s}",
                .{ args.repo_name, args.git_dir_path },
            );

    try writer.print(
        \\    <p style="margin:0; font-style:italic;">{0s}</p>
        \\    <p style="margin:0">{1s} | {2d} commits</p>
        \\
        \\    <pre class="git-clone-command">git clone {3s}/{4s}</pre>
        \\
    ,
        .{
            try html.escapeAlloc(arena, args.description),
            try utils.humanReadableSize(arena, args.repo_size),
            args.commit_count,
            args.site_url,
            clone_path,
        },
    );
}

pub fn repo_nav(
    writer: *Io.Writer,
    args: struct {
        repo_name: []const u8,
        active: enum { source, readme, commits },
        readme_filename: ?[]const u8,
    },
) !void {
    try writer.writeAll(
        \\<nav class="repo-nav">
        \\  <ul>
        \\
    );

    const source_class = if (args.active == .source) "active" else "";
    const readme_class = if (args.active == .readme) "active" else "";
    const commits_class = if (args.active == .commits) "active" else "";

    try writer.print(
        \\    <li class="{0s}"><a href="/{1s}/{2s}/">source</a></li>
        \\
    , .{
        source_class,
        constants.web_path,
        args.repo_name,
    });

    if (args.readme_filename) |fname| {
        try writer.print(
            \\    <li class="{0s}"><a href="/{1s}/{2s}/{3s}/">{4s}</a></li>
            \\
        , .{
            readme_class,
            constants.web_path,
            args.repo_name,
            constants.web_readme_path,
            fname,
        });
    }

    try writer.print(
        \\    <li class="{0s}"><a href="/{1s}/{2s}/{3s}">commits</a></li>
        \\
    , .{
        commits_class,
        constants.web_path,
        args.repo_name,
        constants.web_commits_path,
    });

    try writer.writeAll(
        \\  </ul>
        \\</nav>
        \\
    );
}

pub fn repo_content_start(writer: *Io.Writer) !void {
    try writer.writeAll(
        \\<div class="repo-content">
        \\
    );
}

pub fn repo_content_end(writer: *Io.Writer) !void {
    try writer.writeAll(
        \\</div>
        \\
    );
}