Skip to content
zit Artifact [0c7e575542]
Login

Artifact [0c7e575542]

Artifact 0c7e575542389de9b359f9a618378c8372e5528e2ed7c3d05ee869b93c09fda6:


//! Structural pins over the embedded command reference (`www/reference.html`).
//!
//! The reference is EMBEDDED DOCUMENTATION in fossil's sense: a file in the
//! source tree, versioned with the code it describes, served by the forge at
//! `/doc/<version>/www/reference.html`. Fossil's own `www/embeddeddoc.wiki`
//! gives the reason to prefer that over a wiki page, and it is the reason this
//! file can exist at all:
//!
//!   "The documentation files are versioned together with the source code
//!    files so it is always clear what version of the documentation goes with
//!    a particular release."
//!
//! A wiki page cannot be pinned by a test, because nothing ties its content to
//! a checkout. A tracked file can. That is the whole argument, and this is the
//! pin that collects on it: the reference drifted silently for weeks -- `grep`,
//! `purge`, `config` and `trust` all shipped without ever being written up --
//! and nothing noticed, because noticing required a human to compare two lists.
//!
//! Both directions are checked, because both have gone wrong:
//!   * a verb the CLI dispatches with no section is UNDOCUMENTED;
//!   * a section naming no verb is a claim the binary does not honour.

const std = @import("std");
const cli = @import("cli.zig");

const reference_doc = @embedFile("reference_doc");

/// Sections that deliberately name no verb. Each is documentation ABOUT the
/// absence of one, which is a real thing a reference has to be able to say --
/// and the reviewed-exception list is what keeps "no verb by that name" from
/// being indistinguishable from "we forgot to implement it".
const documented_non_verbs = [_][]const u8{
    // There is no `export-fossil` verb by design: a stock fossil clones
    // straight from `zit serve`, so the export path is the sync protocol.
    // The section exists to say exactly that, and to explain why the
    // asymmetry with `import-fossil` is deliberate rather than an omission.
    "export-fossil",
};

/// The doc's section id for a verb: the enum tag with `_` spelled `-`, which
/// is the same transformation `cli.verbOf` accepts on the command line.
fn docId(comptime tag: []const u8, buf: []u8) []const u8 {
    @memcpy(buf[0..tag.len], tag);
    for (buf[0..tag.len]) |*c| {
        if (c.* == '_') c.* = '-';
    }
    return buf[0..tag.len];
}

fn hasSection(id: []const u8) bool {
    var needle_buf: [64]u8 = undefined;
    const needle = std.fmt.bufPrint(&needle_buf, "id=\"{s}\"", .{id}) catch return false;
    return std.mem.indexOf(u8, reference_doc, needle) != null;
}

test "reference: every dispatched verb has a section" {
    var missing: usize = 0;
    inline for (comptime std.enums.values(cli.Verb)) |v| {
        if (v != .unknown) {
            var buf: [64]u8 = undefined;
            const id = docId(@tagName(v), &buf);
            if (!hasSection(id)) {
                std.debug.print(
                    "\nreference pin: `zit {s}` is dispatched but www/reference.html has no " ++
                        "section for it. A verb that ships undocumented is how the reference " ++
                        "drifted before; document it in the same commit that adds it.\n",
                    .{id},
                );
                missing += 1;
            }
        }
    }
    try std.testing.expectEqual(@as(usize, 0), missing);
}

test "reference: every section names a verb, or is a reviewed exception" {
    var scan: usize = 0;
    var unknown: usize = 0;
    const marker = "<section class=\"cmd\" id=\"";
    while (std.mem.indexOfPos(u8, reference_doc, scan, marker)) |hit| {
        const start = hit + marker.len;
        const end = std.mem.indexOfScalarPos(u8, reference_doc, start, '"') orelse break;
        const id = reference_doc[start..end];
        scan = end;

        // `verbOf` takes the command-line spelling, which is the id itself.
        var known = cli.verbOf(id) != .unknown;
        if (!known) {
            for (documented_non_verbs) |allowed| {
                if (std.mem.eql(u8, allowed, id)) known = true;
            }
        }
        if (!known) {
            std.debug.print(
                "\nreference pin: www/reference.html documents `{s}`, which the CLI does not " ++
                    "dispatch. Either the verb was removed and its section outlived it, or the " ++
                    "section describes something that never shipped -- a reference that promises " ++
                    "a verb is worse than one that omits it. Add it to `documented_non_verbs` " ++
                    "with the reason if the absence is deliberate.\n",
                .{id},
            );
            unknown += 1;
        }
    }
    try std.testing.expect(scan != 0); // the doc must actually contain sections
    try std.testing.expectEqual(@as(usize, 0), unknown);
}