twig-sys 2.2.3

FFI bindings and native library for Twig (the Djot/Markdown/HTML/XML document engine). Used by the `twig-doc` crate.
Documentation
//! Twig's CLI entry point: `twig <command> [options] <file>`, modeled on
//! sister project fig's `cli/main.zig` at Twig's smaller scale. Deliberately
//! thin — process/IO setup, turning argv into a `CliConfig`
//! (`cli/args.zig`), and a dispatch switch straight into `cli/actions.zig`.
//! Everything else (format inference/registry, argument parsing, the verb
//! implementations, the AST JSON encoder) lives in its own sibling module
//! under `cli/` — see each file's own doc comment.
//!
//! Unlike fig's `cli/main.zig`, there is no `std.log`/terminal-color/`NO_COLOR`
//! machinery here: Twig's CLI is plain `stdout`/`stderr` writers throughout
//! (Twig keeps its CLI modest and clean — see DESIGN.md's design principles),
//! and diagnostics
//! are printed directly by whichever `cli/` module detects the problem
//! (`args.zig` for bad flags/undetectable formats, `actions.zig` for
//! read/parse/render/serialize failures — see their module doc comments for
//! the shared "print then return a sentinel error" convention that keeps
//! this file's dispatch switch simple).

const std = @import("std");
const Io = std.Io;

const cli_args = @import("cli/args.zig");
const cli_format = @import("cli/format.zig");
const cli_actions = @import("cli/actions.zig");

pub fn main(init: std.process.Init) !void {
    const arena: std.mem.Allocator = init.arena.allocator();
    const io = init.io;

    var stdout_buffer: [4096]u8 = undefined;
    var stdout_file_writer: Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
    const stdout_writer = &stdout_file_writer.interface;

    var stderr_buffer: [1024]u8 = undefined;
    var stderr_file_writer: Io.File.Writer = .init(.stderr(), io, &stderr_buffer);
    const stderr_writer = &stderr_file_writer.interface;

    const argv = try init.minimal.args.toSlice(arena);
    var arg_iter = cli_args.ArgIterator{ .items = argv };

    const config = cli_args.parseConfig(&arg_iter, stderr_writer) catch {
        // `args.zig` already printed a tailored diagnostic for every failure —
        // the specific message plus the failing command's one-line usage (via
        // `argFail`), or the supported-format list for a bad `-i`/`-o`/
        // extension. The full `runHelp` manual is reserved for an explicit
        // `twig help`, so here we only set the exit code.
        stderr_writer.flush() catch {};
        std.process.exit(2);
    };

    switch (config.action) {
        .help => try cli_actions.runHelp(stdout_writer, config.binary_name),
        .version => try cli_actions.runVersion(stdout_writer),
        .identify => try cli_actions.runIdentify(stdout_writer, config.options.identify),
        .convert => cli_actions.runConvert(arena, io, stdout_writer, stderr_writer, config.options.convert) catch |err| switch (err) {
            // `actions.zig` already printed and flushed a clear message;
            // just set the exit code.
            error.ActionFailed => std.process.exit(1),
        },
        .edit => cli_actions.runEdit(arena, io, stdout_writer, stderr_writer, config.options.edit) catch |err| switch (err) {
            error.ActionFailed => std.process.exit(1),
        },
        .query => cli_actions.runQuery(arena, io, stdout_writer, stderr_writer, config.options.query) catch |err| switch (err) {
            error.ActionFailed => std.process.exit(1),
        },
        .filter => cli_actions.runFilter(arena, io, stdout_writer, stderr_writer, config.options.filter) catch |err| switch (err) {
            error.ActionFailed => std.process.exit(1),
        },
    }
}

// Pull every CLI sibling module's tests into this binary's `exe_tests` (the
// fig/djot/xml convention — see e.g. `djot.zig`'s trailing `test {}` block).
// Without this, a `test {}`-only module reachable solely through `main.zig`
// (never through `root.zig`'s module graph) would never actually run.
test {
    _ = cli_args;
    _ = cli_format;
    _ = cli_actions;
}