zopt is a low-ceremony command line argument parser. It is lazy and uses "schema on read" semantics. Which is a fancy way of saying: it scans argv when you query it, rather than parses it upfront according to a schema.
zopt defers to the user's application code for help text, error/absence handling, and default values. zopt is a deliberately thin wrapper around std.process.Args rather than a batteries-included framework; as such, it is most useful for smaller-scale command-line tools with limited sets of options and without need for schema validation.
Although it is schemaless, zopt keeps track of the flags and options you query such that it can later provide you with positional arguments (defined as non-options/flags).
Philosophically, zopt can be seen as an attempt to create a pared down and aesthetically pleasing API, and it is willing to trade off the benefits of a schema-based command line parser for the ease of use.
Please see the changelog to follow along with changes and find out which versions are available.
First, add zopt as a dependency.
# latest
zig fetch --save git+https://codeberg.org/hgrsd/zopt/#HEAD
# specific version
zig fetch --save git+https://codeberg.org/hgrsd/zopt/#v0.0.11
const std = @import("std");
const Zopt = @import("zopt").Zopt;
pub fn main(init: std.process.Init) !void {
const arena = init.arena.allocator();
const Enum = enum { Foo, Bar };
const z = try Zopt.from(arena, init.minimal.args);
// flags match -F, --foo, or -F as part of multiple flags, e.g. "-FAX"
const flag = z.flag('F', "foo");
// short-only flag; matches -F, or -F as part of multiple flags, e.g. -FAO
const short_flag = z.flag('F', null);
// option matches -F <value>, -F=<value>, --foo <value> or --foo=<value>.
const value = z.option('F', "foo");
// short-only option matches -F <value>, -F=<value>
const short_value = z.option('F', null);
// long-only option matches --foo <value>, --foo=<value>
const long_value = z.option(null, "foo");
// compose with zig's orelse for default values
const value_with_default = z.option('F', "foo") orelse "my_default";
// fall back to help if value is absent
const value_with_help_fallback = z.option('F', "foo") orelse help();
// repeatable options; similar matching logic to z.option, collecting all matches
const values = try z.optionRepeated('F', "foo");
// optionAs allows parsing into specific (int, float, enum) types.
const int_value = try z.optionAs(usize, 'F', "foo");
const enum_value = try z.optionAs(Enum, 'E', "enum");
// positionals returns a slice of positional arguments, where arguments are assumed to be positional that are
// note that positionals does _not_ do any inspection of the shape of the values. This means that a misspelled
// option, for instance, will be considered a positional argument.
// As such, it is recommended to first validate that no unknown arguments are present in argv.
const unknown = try z.unknownArguments();
if (unknown.len > 0) {
std.process.fatal("unknown argument found: {s}", .{ unknown });
}
// now it's safe to consider everything to be a positional arg
const positional_args = try z.positionals();
// positional returns a specific positional argument, if present, using the same heuristics as above
const first_positional = try z.positional(0);
const second_positional = try z.positional(1);
}
The examples folder contains examples of how to use zopt in various scenarios.
Each can be executed using the example name, and passed arguments to see it in action.
For a "real-world" example, see duplik's argument parsing.