2
1
Fork
You've already forked argparser
0
A modified version of the argparser contained in the TigerBeetle repository.
  • Zig 89%
  • JSON 11%
2026年05月25日 04:56:38 +02:00
.flox chore: add flox environment 2026年05月24日 19:31:02 -07:00
src feat: add dash-name subcommand 2026年05月24日 19:52:18 -07:00
.gitignore feat: add argument parser 2026年05月24日 19:39:49 -07:00
build.zig feat: add argument parser 2026年05月24日 19:39:49 -07:00
build.zig.zon feat: add argument parser 2026年05月24日 19:39:49 -07:00
LICENSE Initial commit 2026年05月24日 23:08:05 +02:00
README.md chore: update README 2026年05月25日 04:56:38 +02:00

argparser

A modified version of the argparser contained in the TigerBeetle repository.

Guidelines

Here are the (slightly modified) guidelines from Tigerbeetle's flags.zig module:

The purpose of argparser is to define standard behavior for parsing CLI arguments and provide a specific parsing library, implementing this behavior.

These are TigerBeetle CLI guidelines:

  • The main principle is robustness --- make operator errors harder to make.
  • For production usage, avoid defaults.
  • Thoroughly validate options.
  • In particular, check that no options are repeated.
  • Use only long options (--addresses).
  • Exception: -h/--help is allowed.
  • Use --key=value syntax for an option with an argument. Don't use --key value, as that can be ambiguous (e.g., --key --verbose).
  • Use subcommand syntax when appropriate.
  • Use positional arguments when appropriate.

Design choices for this particular argparser library:

  • Be a 80% solution. Parsing arguments is a surprisingly vast topic: auto-generated help, bash completions, typo correction. Rather than providing a definitive solution, this is just one possible option. It is ok to re-implement arg parsing in a different way, as long as the CLI guidelines are observed.

  • No auto-generated help. Zig doesn't expose doc comments through @typeInfo, so it's hard to implement auto-help nicely. Additionally, fully hand-crafted --help message can be of higher quality.

  • Concise DSL. Most cli parsing is done for ad-hoc tools like benchmarking, where the ability to quickly add a new argument is valuable. As this is a 80% solution, production code may use more verbose approach if it gives better UX.

Changes to Tigerbeetle's flags.zig

  • Updated to be compatible with Zig 0.16.0.
  • Errors are propagated upwards rather than exiting the process immediately.
    • I changed this because it makes testing easier.
  • Parameterized by a std.Io.Writer for each of stdout and stderr.
    • I also changed this to make testing easier.
    • I also want to be able to capture and inspect output in tests of more than just the argument parser in some upcoming projects, so this is the first step towards that.
  • Uses ohsnap for snapshot testing instead of snaptest.zig in the Tigerbeetle repository.
    • ohsnap has better support for pretty-printing arbitrary data structures.
    • I also didn't want to maintain ports of Tigerbeetle's stdx.zig.
  • Updated the function names to follow Zig naming conventions e.g. camel case for function names.
  • Allow -h/--help on leaf-node subcommands.
    • Previously this was only allowed on subcommands that had also had subcommands as children.
    • The restriction here is that -h/--help must be the first argument.

Usage

There is an included demo CLI in main.zig. You can test it out via zig build run -- <args>.

If there's question as to whether something should work one way or another, consult the tests at the end of argparser.zig, they cover a ton of cases (thank you Tigerbeetle).

Most of this is spelled out in the Tigerbeetle guidelines above, but just to remind you:

  • Only long flags are allowed.
  • Flag arguments use --flag=<arg> syntax, not --flag <arg>.
  • Prefer subcommands to flags.
  • Subcommand names can't contain underscores (_).
  • For subcommand names that contain dashes, use @"my-cmd" as the enum tag.
  • Help text is written verbatim by the developer.

When parsing:

  • Call Args.parse to parse []const []const u8.
  • Call Args.parseZ to parse []const [*:0]const u8.
  • Errors are written to stderr and error.CliParseError is bubbled up.
  • null is returned when a user passes -h/--help.
  • You get your actual args type in all other cases.

For the lazy, this is what it looks like to define your arguments:

constCLI=union(enum){pubconsthelp=\\Usage:\\\\ demo [-h | --help]\\ demo subcmd\\ demo flags [--foo] [--bar=<u32>] [--baz=<str>]\\ demo positional <str> [<str>]\\ demo subcmd-varargs [--foo=<u32>] [-- <str> [<str>]]\\ demo dash-name\\\\Description:\\\\ A very simple CLI that demonstrates how to use this argument parser.\\\\Commands:\\\\ subcmd\\ A subcommand that takes no arguments.\\\\ flags\\ A subcommand with optional arguments.\\\\ positional\\ A subcommand that takes positional arguments.\\\\ varargs\\ A subcommand that takes both optional arguments\\ and trailing "variadic" arguments.\\\\ dash-name\\ A subcommand showing that it's possible for command names\\ to contain dashes.\\\\Options:\\\\ -h, --help\\ Print this help message and exit.\\;subcmd,flags:struct{pubconsthelp=\\Usage:\\\\ demo flags [--foo] [--bar=<u32>] [--baz=<str>]\\\\Description:\\\\ A subcommand that demonstrates how to use optional arguments.\\\\Options:\\\\ --foo [default: false]\\ An optional boolean flag.\\\\ --bar=<u32> [default: 0]\\ An optional flag that takes a single unsigned integer\\ as an argument.\\\\ --baz=<str> [default: null]\\ An optional flag that takes a string argument.\\;foo:bool=false,bar:u32=0,baz:?[]constu8=null,},positional:struct{pubconsthelp=\\Usage:\\\\ demo positional <str> [<str>]\\\\Description:\\\\ A subcommand that takes one required positional argument, and a\\ second optional positional argument. Prints the arguments that\\ are provided.\\;@"--":void,first:[]constu8,second:?[]constu8=null,},varargs:struct{pubconsthelp=\\Usage:\\\\ demo varargs [--flag=<str>] [-- <str> ...]\\\\Description:\\\\ A subcommand that takes an optional flag and optional trailing\\ "variadic" arguments like you would use to collect arguments to\\ another command to be invoked.\\\\Options:\\\\ --flag\\ An optional flag that takes a string argument. This solely\\ exists to show you that you can mix optional, positional,\\ and variadic arguments together in the same command.\\;flag:?[]constu8=null,@"--":void,// NOTE: This can't be optional, it will just be empty when no// trailing arguments are provided.trailing:[]const[]constu8,},@"dash-name",};