A simple, fast argument parser for Zig.
- Short and long options
- Positional arguments
- Subcommands
- Struct-based API
- Minimal runtime overhead
Add to your project:
zig fetch --save "git+https://github.com/aufam/argparse#v0.1.0"In build.zig:
const argparse = b.dependency("argparse", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("argparse", argparse.module("argparse"));const std = @import("std");
const argparse = @import("argparse");
const App = struct {
/// boolean
verbose: bool,
/// enum, fallback value is optional, but if provided, it must be a valid enum variant
@"log-level": enum { trace, info, debug } = .info,
//
// subcommands must be ?struct {...}
//
version: ?struct {},
run: ?struct {
//
// non-bool and non-optional fields are required unless they have a default value
//
/// string slice
name: []const u8,
/// integer
age: u32,
/// float
height: f32,
/// array slice type, must be freed, and must not be confused with positional arguments
hobbies: []const []const u8,
/// with default value, implies optional
country: []const u8 = "Unknown",
/// optional value
nickname: ?[]const u8,
//
// custom options
//
/// positional arguments
input: argparse.Positional([]const u8),
/// can be an option or positional
output: argparse.Option([]const u8, .{ .short = &.{"o"}, .long = &.{"output"}, .help = "Output file path", .positional = true }),
/// custom flag
print_hash: argparse.Flag(.{ .short = &.{"H"}, .long = &.{"hash"}, .help = "Print the hash of the input file" }),
},
};
pub fn main() !void {
const allocator = std.heap.page_allocator;
const app = argparse.parseInto(App, .{ .allocator = allocator }) catch |err| if (err == argparse.ParseError.Help) {
return; // help information is automatically printed by the library, so we can just exit
} else {
return err;
};
if (app.verbose) {
// enable verbose logging
}
switch (app."log-level") {
.trace => { /* set log level to trace */ },
.info => { /* set log level to info */ },
.debug => { /* set log level to debug */ },
}
// subcommands are ensured to be mutually exclusive, so we can use if-else statements to handle them
if (app.version) |_| {
// print version information
} else if (app.run) |run| {
// don't forget to free slice types
defer allocator.free(run.hobbies);
// accessing custom flags
const input: []const u8 = run.input.value;
const output: []const u8 = run.output.value;
const print_hash: bool = run.print_hash.value;
// run the application with the provided arguments
} else {
// no subcommand provided, handle the case if necessary
}
}argparse maps CLI arguments directly into a user-defined struct.
Rules:
Flagsare boolean.Optionsare for values that can be set by the user. Fields with default values are optional, and fields without default values are required.Subcommandsmust be?struct {...}.- Primitive types for options: integers, floats, string slices, and enums.
- Parse long options with
=or spaceconst App = struct { name: []const u8, age: u8, }; // ./app --name=John --age 30
- Slices are allocated by allocator and must be freed by the user.
- Positional arguments can be defined with
argparse.Positionaland can be used in conjunction with flagged options. For example:const App = struct { verbose: bool, m: []const []const u8, input: argparse.Positional([]const u8), output: argparse.Option([]const u8, .{ .short = &.{"o"}, .long = &.{"output"}, .help = "Output file path", .positional = true }), };
./app input.txt output.txt -m foo bar # ok ./app input.txt -o output.txt -m foo bar # ok ./app -m foo bar -o output.txt input.txt # ok ./app -m foo bar input.txt -o output.txt # err: missing input field, because the parser will assume input.txt is the value for the -m option ./app -o output.txt -m foo bar -- input.txt # ok
- Better help message.
- Better compile error message.
- Windows and wasm.