Backlog/lib/zargs/README.md

7.3 KiB

zargs - Zero-overhead Argument Parser for Zig

A type-safe, compile-time command-line argument parser for Zig that uses struct introspection to automatically generate parsers.

Features

  • Type-safe: Arguments are defined as struct fields with compile-time type checking
  • Zero runtime overhead: All metadata extraction happens at compile time
  • Flexible syntax: Supports --flag, --flag=value, -f, -f value, and multi-flags (-abc)
  • Rich types: Bool, integers, strings, enums, lists, and optional types
  • Automatic help: Generates professional help text from struct metadata
  • Multi-module: Multiple modules can register arguments with collision detection
  • Memory safe: No leaks, proper cleanup with defer
  • Zero dependencies: Pure Zig, no external dependencies

Quick Start

const std = @import("std");
const zargs = @import("zargs");

const Config = struct {
    verbose: bool = false,
    output: []const u8 = "output.txt",
    count: u32 = 10,
    
    pub const meta = .{
        .verbose = .{ .short = 'v', .help = "Enable verbose output" },
        .output = .{ .short = 'o', .help = "Output file path" },
        .count = .{ .short = 'c', .help = "Number of items" },
    };
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();
    
    const args = try std.process.argsAlloc(allocator);
    defer std.process.argsFree(allocator, args);
    
    const config = zargs.parse(Config, allocator, args) catch |err| {
        if (err == error.HelpRequested) return;
        return err;
    };
    
    std.debug.print("Output: {s}\n", .{config.output});
}

Usage

Define Your Configuration

const Config = struct {
    // Boolean flag (default: false)
    verbose: bool = false,
    
    // String argument (default: "output.txt")
    output: []const u8 = "output.txt",
    
    // Integer argument (default: 10)
    count: u32 = 10,
    
    // Enum argument (default: .balanced)
    mode: enum { fast, slow, balanced } = .balanced,
    
    // Optional argument (default: null)
    name: ?[]const u8 = null,
    
    // String list (can be repeated or comma-separated)
    files: []const []const u8 = &[_][]const u8{},
    
    // Add metadata for help text and short flags
    pub const meta = .{
        .verbose = .{
            .short = 'v',
            .help = "Enable verbose output",
        },
        .output = .{
            .short = 'o',
            .help = "Output file path",
        },
        .count = .{
            .short = 'c',
            .help = "Number of items to process",
        },
        .mode = .{
            .short = 'm',
            .help = "Processing mode",
        },
        .name = .{
            .help = "Optional name parameter",
        },
        .files = .{
            .short = 'f',
            .help = "Input files (can be repeated)",
        },
    };
};

Parse Arguments

// Simple parsing (shows help automatically)
const config = try zargs.parse(Config, allocator, args);

// Advanced: manual registry for multi-module apps
var registry = zargs.ArgumentRegistry.init(allocator);
defer registry.deinit();

try registry.registerMetadata(Module1Config, "Module1");
try registry.registerMetadata(Module2Config, "Module2");

try zargs.parseArgv(&registry, args);

const mod1 = try zargs.populateStruct(Module1Config, &registry, allocator);
const mod2 = try zargs.populateStruct(Module2Config, &registry, allocator);

Command-Line Syntax

Boolean Flags

./program --verbose        # Sets verbose = true
./program -v               # Short form
./program -vdq             # Multi-flag (sets verbose, debug, quiet)

String Arguments

./program --output=file.txt    # With equals
./program --output file.txt    # Space-separated
./program -o file.txt          # Short form

Integer Arguments

./program --count=42
./program --count 0xFF         # Hex supported
./program --count 0b1010       # Binary supported

Enum Arguments

./program --mode=fast
./program --mode slow

List Arguments

./program --files=a.txt,b.txt,c.txt   # Comma-separated
./program --files=a.txt --files=b.txt # Repeated (both work!)

Help

./program --help
./program -h

Supported Types

  • Booleans: bool
  • Integers: u8, u16, u32, u64, i8, i16, i32, i64
  • Strings: []const u8
  • Enums: Any Zig enum type
  • Lists: []const []const u8 (string lists)
  • Optionals: ?T for any supported type T

Help Text Generation

zargs automatically generates professional help text:

Usage: program [OPTIONS]

Options:
  -h, --help              Show this help message
  -c, --count <NUM>       Number of items to process
  -m, --mode <CHOICE>     Processing mode
  -o, --output <VALUE>    Output file path [default: output.txt]
  -v, --verbose           Enable verbose output [default: false]

Advanced Features

Collision Detection

When multiple modules register the same argument name:

  • Compatible (same type): Allowed, warns
  • Incompatible (different types): Compile error
// Both modules can register --verbose (bool)
try registry.registerMetadata(Module1, "Module1"); // has verbose: bool
try registry.registerMetadata(Module2, "Module2"); // has verbose: bool - OK!

// This would error at compile time:
// Module1 has verbose: bool
// Module2 has verbose: u32 - COMPILE ERROR!

Custom Metadata

pub const meta = .{
    .field_name = .{
        .short = 'x',           // Short flag (optional)
        .help = "Description",  // Help text (optional)
        .required = true,       // Override default requirement (optional)
    },
};

Examples

See the examples/ directory for complete examples:

  • simple.zig - Basic single-struct usage
  • multi_module.zig - Multiple modules with shared registry

Building

Requires Zig 0.14 or later (tested with Zig 0.15.2).

zig build
zig build test

API Reference

Main Functions

  • parse(T, allocator, argv) - Parse arguments into struct T
  • parseWithRegistry(T, registry, allocator, argv) - Parse with existing registry

Core Types

  • ArgumentRegistry - Central registry for argument metadata
  • ArgumentType - Enum of supported argument types
  • ParsedValue - Tagged union of parsed values
  • ArgumentMetadata - Complete metadata for an argument

Utilities

  • generateHelpText(registry, allocator, program_name) - Generate help text
  • parseArgv(registry, argv) - Parse argv into registry
  • populateStruct(T, registry, allocator) - Populate struct from parsed values

Design Philosophy

zargs is designed for game engines and plugin architectures where:

  • Arguments are scattered across many modules
  • Not all modules may load in every run
  • Comprehensive documentation is still needed
  • Type safety is non-negotiable

Version

Current version: 0.1.0-dev

License

[Add your license here]

Contributing

Contributions welcome! Please ensure:

  • All tests pass (zig build test)
  • No memory leaks (tests check with std.testing.allocator)
  • Code follows existing style
  • New features have tests and documentation

Acknowledgments

Built with ❤️ in Zig, following best practices from the Zig standard library.