A simple flag parser for Zig programs.
API documentation can be found here or made with zig build docs. If fetching from master, it is recommended do use zig build docs as the GitHub pages might not be updated.
See Type.ParseConfig
- allowDups: Don't error when duplicate flags are set. Default is false.
- verbose: Print out error messages when errors occur. Default is false.
- writer: Required when using verbose option. Doesn't really do anything without it. Default is null.
- prefix: Print out a custom string for verbose messages. Default is null.
- allowDashInput: Allow input type flags to hold strings that begin with "-". Default is true.
- errOnNoArgs: Outputs an error if there are no arguments except argv[0]. Default is false.
- exitFirstErr: Exit on first error found. Default is true.
- Fetch with zig and add as module in build.zig
// Specific tag
zig fetch --save https://github.com/koeir/zigflag/archive/refs/tags/v0.x.x.tar.gz
// Or master branch
zig fetch --save git+https://github.com/koeir/zigflag // build.zig
const zigflag = b.dependency("zigflag", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{...});
exe.root_module.addImport("zigflag", zigflag.module("zigflag"));const zigflag = @import("zigflag");
const SwitchFlag = zigflag.Type.SwitchFlag; // bool
const InputFlag = zigflag.Type.InputFlag; // ?[][:0]const u8
const Flags = zigflag.Type.Flags;
const Flag = zigflag.Type.Flag;
// Initialize flags and their default values
// name doesn't really matter as long as the
// members are all of type zigflag
pub const defaults: Flags = .{
.list = &[_]Flag
{
.{
.name = "recursive",
.tag = "Switches",
.long = "recursive",
.short = 'r',
.value = SwitchFlag,
.desc = "Recurse into directories",
},
.{
.name = "force",
.tag = "Switches",
.long = "force",
.short = 'f',
.vanity = "-[n|f], --[no-]force",
.value = SwitchFlag,
.desc = "Skip confirmation prompts",
},
.{ // by default, untagged flags will not be printed
.name = "no-force",
.long = "no-force",
.short = 'n',
.value = SwitchFlag,
.desc = "Do not skip confirmation prompts",
},
// Arguments will accept the next argv
// e.g. -prf noob
// "noob" will be accepted as the file
.{
.name = "file",
.tag = "Input",
.long = "path",
.short = 'p',
.value = InputFlag,
.desc = "Path to file",
},
}
};Input takes always takes the next argument after it, optionally erring if the next arg has flag syntax. Input supports storing multiple arguments by default, but can be avoided setting allowDups to false or just checking the array len.
const defaults = @import("./init_flags.zig").defaults;
const Flags = zigflag.StructFlags(defaults);
pub fn main(init: std.process.Init) !void {
...
// Make config
const parsecfg: zigflag.Type.ParseConfig = .{
.allowDashInput = true,
.allowDups = true,
.verbose = true,
.writer = stderr,
.prefix = "my-program: "
};
// points to erred flag
var errptr: ?[]const u8 = null;
// actual parse, returns a tuple of Flags and resulting args
const result = try zigflag.parse(init.gpa, min.args, defaults, &errptr, parsecfg);
defer result.deinit();
// retrieving values
const flags: Flags = result.flags;
const argv: [][:0]const u8 = result.argv;
...
}The flags are stored in a struct in which the fields are names of the flags. Each field will have their corresponding values (Switch/bool, Input/?[][:0]const u8). The struct also holds the allocator, inner arrays, and necessary components for deinit. gpa is used here, but it might be more convenient to use arena allocators.
const defaults = @import("./init_flags.zig").defaults;
const Flags = zigflag.StructFlags(defaults);
pub fn main(init: std.process.Init) !void {
...
const parsed: Flags = result.flags;
// arg list that has flags removed;
// also removes values that were taken in by flags
const flagless_args: [][:0]const u8 = result.args;
if (parsed.force) // whatever
const recursive: bool = parsed.recursive;
const files: ?[][:0]const u8 = parsed.files;
if (!recursive) //whatever
for (files orelse &.{}) |file| {
// whatever
}
...
} // warning:
//
// center padding is calculated by
// value - n of chars in "-<s>, --<long>"
// so make sure the padding is enough
zigflag.Type.Flag.fmt = .{
.padding = .{
.left = 5,
.center = 30,
},
.greyOutFiller = true,
.fillerStyle = '.',
};
Switches:
-r, --recursive.............. Recurse into directories
-[n|f], --[no-]force......... Skip confirmation prompts
Input:
-p <file>, --path <file>..... Path to filepub const FlagErrs = error {
NoArgs, // argc < 2
NoSuchFlag, // unrecognized flag in arg list
FlagNotSwitch, // non-switch/non-bool Flag treated as a switch/bool
FlagNotArg, // non-input type flag treated as an input type
DuplicateFlag, // flag appears twice in arg list; can be ignored with config
ArgNoArg, // no argument given to input type flag
NoWriter, // no writer given when verbose is true
TypeMismatch, // a more general FlagNotSwitch/FlagNotArg
}