280 lines
7.3 KiB
Markdown
280 lines
7.3 KiB
Markdown
# 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
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
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
|
|
|
|
```zig
|
|
// 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(®istry, args);
|
|
|
|
const mod1 = try zargs.populateStruct(Module1Config, ®istry, allocator);
|
|
const mod2 = try zargs.populateStruct(Module2Config, ®istry, allocator);
|
|
```
|
|
|
|
## Command-Line Syntax
|
|
|
|
### Boolean Flags
|
|
```bash
|
|
./program --verbose # Sets verbose = true
|
|
./program -v # Short form
|
|
./program -vdq # Multi-flag (sets verbose, debug, quiet)
|
|
```
|
|
|
|
### String Arguments
|
|
```bash
|
|
./program --output=file.txt # With equals
|
|
./program --output file.txt # Space-separated
|
|
./program -o file.txt # Short form
|
|
```
|
|
|
|
### Integer Arguments
|
|
```bash
|
|
./program --count=42
|
|
./program --count 0xFF # Hex supported
|
|
./program --count 0b1010 # Binary supported
|
|
```
|
|
|
|
### Enum Arguments
|
|
```bash
|
|
./program --mode=fast
|
|
./program --mode slow
|
|
```
|
|
|
|
### List Arguments
|
|
```bash
|
|
./program --files=a.txt,b.txt,c.txt # Comma-separated
|
|
./program --files=a.txt --files=b.txt # Repeated (both work!)
|
|
```
|
|
|
|
### Help
|
|
```bash
|
|
./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
|
|
|
|
```zig
|
|
// 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
|
|
|
|
```zig
|
|
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).
|
|
|
|
```bash
|
|
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.
|