twig-sys 3.11.0

FFI bindings and native library for Twig (the Djot/Markdown/HTML/XML document engine). Used by the `twig-doc` crate.
Documentation
//! Registry-wide engine contracts. Corpora belong to conformance readers.
//!
//! The checks are `contract.zig`'s, which registration runs over a runtime
//! language too; this file is the loop over the compiled registry, plus the
//! one property that belongs to core's encoder rather than to a format — the
//! node table's identity over every sample.
const std = @import("std");
const testing = std.testing;
const format = @import("../format.zig");
const contract = @import("../contract.zig");
const node_table = @import("../ast/table.zig");

fn expectKept(result: contract.Error!void, report: *const contract.Report) !void {
    result catch |err| {
        std.debug.print("\n{s}\n", .{report.message()});
        return err;
    };
}

test "harness: every authorable format moves a block across its containers" {
    for (&format.registry) |*entry| {
        if (!entry.syntax.authorable()) continue;
        var report: contract.Report = .{};
        try expectKept(contract.moveBlock(testing.allocator, entry, &report), &report);
    }
}

test "harness: every declared renderer keeps the engine's promise" {
    for (&format.registry) |*entry| {
        var report: contract.Report = .{};
        try expectKept(contract.renderers(testing.allocator, entry, &report), &report);
    }
}

test "harness: every format declares samples and satisfies the engine contract" {
    for (&format.registry) |*entry| {
        var report: contract.Report = .{};
        try expectKept(contract.samples(testing.allocator, entry, &report), &report);
        // The compiled formats cross the contract a runtime one will: every
        // sample's parse, written as a node table and read back, is the parse.
        for (entry.samples, 0..) |sample, index| {
            errdefer std.debug.print("\n{s}: harness sample {d}\n--- source ---\n{s}\n", .{ entry.id.name(), index, sample });
            const config: format.ParseConfig = .{};
            var parsed = try entry.parse(&config, testing.allocator, sample);
            defer parsed.deinit();
            try node_table.expectIdentity(testing.allocator, &parsed.doc);
        }
    }
}

/// Broken promises the gesture check knows of, each with the task that owns
/// it. A finding here is tolerated; one that stops being found fails the test,
/// so the list only shrinks.
const known_findings = [_]struct { header: []const u8, task: []const u8 }{
    .{ .header = "asciidoc: toggle_block_container over sample 0: the paragraph \"An admonition.\" is gone", .task = "docs/tasks/asciidoc-admonition-label-gestures.md" },
    .{ .header = "asciidoc: toggle_code_block over sample 0: no zig code block holding the paragraph came back", .task = "docs/tasks/asciidoc-admonition-label-gestures.md" },
};

test "harness: every gesture, everywhere, over every authorable format" {
    var log: std.ArrayList(u8) = .empty;
    defer log.deinit(std.testing.allocator);
    for (&format.registry) |*entry| {
        var report: contract.Report = .{ .log = &log };
        contract.gestures(std.testing.allocator, entry, &report) catch |err| switch (err) {
            error.ContractBroken => {},
            else => return err,
        };
    }
    var seen = [_]bool{false} ** known_findings.len;
    var unknown: usize = 0;
    var messages = std.mem.splitScalar(u8, log.items, 0);
    next: while (messages.next()) |m| {
        if (m.len == 0) continue;
        for (known_findings, 0..) |k, i| {
            if (std.mem.startsWith(u8, m, k.header)) {
                seen[i] = true;
                continue :next;
            }
        }
        unknown += 1;
        std.debug.print("\n{s}\n", .{m});
    }
    for (known_findings, seen) |k, s| {
        if (!s) {
            std.debug.print("\nno longer found — remove it, and close {s}:\n{s}\n", .{ k.task, k.header });
            unknown += 1;
        }
    }
    if (unknown != 0) return error.ContractBroken;
}

test "harness: a broken promise is reported, not asserted" {
    // A row whose samples are fine but whose renderer lies: djot's own row
    // with a `renderBlock` that prints nothing. The contract names the check
    // and the format rather than failing an expectation somewhere inside.
    const Lying = struct {
        fn render(_: std.mem.Allocator, _: *const @import("../ast/ast.zig"), _: @import("../ast/ast.zig").Node.Id, _: *std.Io.Writer) anyerror!void {}
    };
    var table = format.syntaxFor(.djot).*;
    table.renderBlock = &Lying.render;
    var entry = format.entryFor(.djot).*;
    entry.syntax = &table;
    entry.syntaxFor = null;
    var report: contract.Report = .{};
    try testing.expectError(error.ContractBroken, contract.renderBlock(testing.allocator, &entry, &report));
    try testing.expect(std.mem.startsWith(u8, report.message(), "djot: renderBlock:"));
}