- Zig 100%
|
|
||
|---|---|---|
| examples | Enum example | |
| src | switch *Arena parameters to Allocator | |
| .gitignore | Initial commit | |
| build.zig | Enum example | |
| build.zig.zon | Big refactor | |
| LICENSE | Initial commit | |
| README.md | Update README.md | |
argz
Command Line Parser for Zig 0.16.
How it works
It parses a given help text to produce a struct type with the appropriate fields.
This struct is then populated during the parsing of init.minimal.args.
Command line arguments that weren't used are copied as-is in the _argv field.
The first character of each line indicates its meaning:
- a
.signify an flag, positional argument, or sub-command - a
!signify a required flag or positional argument - any other character is ignored.
The automatic help text printer will discard the first character of every line.
The following flag syntax is recognized by the parser:
-a,--alice: simple flags-b=42,--bob=42,-b 42,--bob 42: flags with parameter.-abcde, multiple short form flags. Note that if-ctakes a parameter then-abcdeis parsed as-a -b -c=de.
An empty argument, or an argument equal to -, or any argument that doesn't start with - is treated as a sub-command if one matches or a positional argument.
In addition, a -- appearing anywhere will force subsequent arguments to be parsed as positional arguments.
Getting started
Make sure you have zig 0.16. Fetch argz and store it in your project:
zig fetch --save https://codeberg.org/chocapix/argz/archive/0.16.x.tar.gz
Then make argz available as a module for your executable, in build.zig, do:
const exe = b.addExecutable(...);
const argz = b.dependency("argz", .{});
exe.root_module.addImport("argz", argz.module("argz"));
Examples
The simplest way to use argz is via the parse function. It takes an std.process.Init argument and parses the command line arguments.
In case of error, it will print a diagnostic on stderr and returns an error. If the help text defines a --help flag and it is present, it will print the help text to stdout and call exit(0).
Simple Flags
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const argv = try @import("argz").parse(
\\ usage simple [options...]
\\
\\ options:
\\. -h, --help print this help and exit
\\. -f simple short flag
\\. --long simple long flag
, .{}, init);
std.debug.print("{any}\n", .{argv});
}
Here argv will have the following fields:
help: usize: the number of times the-hor--helpflag is present.f: usize: the number of times the-fflag is present.long: usize: the number of times the--longflag is present._argv: []const [:0]const u8: the unused command line arguments.
Flags with parameters, required flags, positional arguments
A ! denotes a required flag or positional argument. =<type> after a flag or positinal indicate how parameters will be parsed. =<type> is required for positional arguments.
Builtin argz types and their zig counter parts are as follows.
argz <type> |
zig type |
|---|---|
<str> |
[:0]const u8 |
<int> |
isize |
<uint> |
usize |
<float> |
f64 |
<iXX> |
iXX |
<uXX> |
uXX |
<fXX> |
fXX |
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const argv = try @import("argz").parse(
\\ usage simple [options...]
\\
\\ options:
\\. -h, --help print this help and exit
\\! --foo=<int> an integer required flag
\\. -g, --gaa=<float> an float optional flag
\\. bar=<str> an string optional positional argument
, .{}, init);
std.debug.print("{any}\n", .{argv});
}
Here argv will have the following fields:
help: usize: the number of times the-hor--helpflag is presentfoo: isize: the number passed to the--foo=XXXflaggaa: ?f64: the number passed to the--gaa=XXXflag, ornullif--gaais not presentbar: ?[:0]const u8: the first positional argument if any,nullotherwise_argv: []const [:0]const u8: the unused command line arguments
Note that a optional positional argument cannot appear before a required one.
The help text printer removes the name= part of positional argument definitions.
Sub-commands
A . line with a name and no =<type> defines a sub-command.
They define an optional enum _command field that one can switch on.
Then, the _argv field can be passed to the parseArgv function.
Sub-command definitions cannot start with a !.
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const argv = try @import("argz").parse(
\\ usage subcmd [options...] command [command options...]
\\
\\ options:
\\. -h, --help print this help and exit
\\
\\ commands:
\\. zero zeroth commmand
\\. one oneth commmand
, .{}, init);
return if (argv._command) |cmd| switch (cmd) {
.zero => zeroCmd(init, argv._argv),
.one => std.debug.print("cmd one not implemented\n", .{}),
} else error.MissingCommand;
}
fn zeroCmd(init: std.process.Init, argv: []const [:0]const u8) !void {
const args = try @import("argz").parseArgv(
\\ usage subcmd zero [options...] posi
\\. -f, --foo=<uint> foo
, .{}, init.arena.allocator(), init.io, argv);
std.debug.print("{any}\n", .{args});
}
Custom types
The second argument to parse() is expected to be a struct literal that defines parsers for custom types:
pub fn main(init: std.process.Init) !void {
const argv = try @import("argz").parse(
\\! foo=<footype>
, .{
.footype = parseFoo,
}, init);
}
fn parseFoo(str: [:0]const u8) !Foo { ... }
The type Foo of argv.foo is extracted from the result type of parseFoo.
Custom parsers must be callable with a [:0]const u8 and return a E!T for some error union E and some type T.
For convenience, argz provides an enum parser, parseEnum:
const std = @import("std");
pub fn main(init: std.process.Init) !void {
const argz = @import("argz");
const argv = try argz.parse(
\\ usage customtypes [options...]
\\. -h, --help print this help and exit
\\. --maybe=<yesno> sets maybe to yes or no
, .{
.yesno = argz.parseEnum(YesNo),
}, init);
std.debug.print("{any}\n", .{argv});
}
const YesNo = enum { yes, no };
Advanced API
argz needs an arena and an io, parse() gets them from the std.process.Init parameter.
If your program defines its own io and allocator, use the parseMinimal() function.
If you want full control, define an Argz variable and call parse() on it.