A simple flag parser for Zig programs.
- Formatted printing
- Simple interface
- Returns argv list without flags
- 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.
- allowDashAsFirstCharInArgForArg: I admit this needs a better name. It allows argumentative type flags (meaning flags that hold a string/arg) to hold strings that begin with "-". Default is true.
- errOnNoArgs: Outputs an error if there are no arguments except argv[0]. Default is false
- Fetch with zig and add as module in build.zig
zig fetch --save https://github.com/koeir/flagparse/archive/refs/tags/v0.7.0.tar.gz // build.zig
const flagparse = b.dependency("flagparse", .{
.target = target,
.optimize = optimize,
});
const exe = b.addExecutable(.{
.name = "example",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
})
});
exe.root_module.addImport("flagparse", flagparse.module("flagparse"));
b.installArtifact(exe);- Declare a list of flags with the built-in structs
const initflags: flagparse.Type.Flags = .{
.list = &[_] flagparse.Type.Flag
{
.{
.name = "recursive",
.tag = "Switches",
.long = "recursive", // long flags and short flags are maybe values;
.short = 'r', // if both are missing then they can't... be set
.value = .{ .Switch = false },
.desc = "Recurse into directories",
},
.{
.name = "force",
.tag = "Switches",
.long = "force",
.short = 'f',
.value = .{ .Switch = false },
.desc = "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 = .{ .Argumentative = null },
.desc = "Path to file",
},
}
};- Initialize args, allocators, and error pointer for handling
const std = @import("std");
const flagparse = @import("flagparse");
pub fn main(init: std.process.Init) !void {
const io = init.io;
const min = init.minimal;
var gpa = std.heap.DebugAllocator(.{}){};
var arena: std.heap.ArenaAllocator = .init(gpa.allocator());
defer arena.deinit();
// points to last arg on error
// not necessarily the arg that caused the error
var errptr: ?[*:0]const u8 = null;
...
- Parse
const std = @import("std");
const flagparse = @import("flagparse");
pub fn main() !void {
...
// returns a tuple of Flags and resulting args
// resulting args is a maybe value
const result = flagparse.parse(arena.allocator(), min.args, initflags, &errptr, .{}) catch |err| {
const arg_error = errptr.?;
// handle err
return;
};
...
- Use
...
// retrieve tuple values
const flags: flagparse.Type.Flags = result.flags;
const flagless_args: ?[][:0]const u8 = result.argv;
const recursive: bool = try flags.value("recursive", flagparse.Type.Switch);
const file: ?[:0]const u8 = try flags.value("file", flagparse.Type.Argumentative);
if (recursive) // do stuff
if (flagless_args) |args| {
// do stuff
}
...The Flags struct has a method usage() that prints all flags with their respective tags. Tags that appear first in the array of the init flags are printed first. Whether the flags without tags are printed first or last can be change with the config option untaggedFirst: bool.
Switches:
-r, --recursive Recurse into directories
-f, --force Skip confirmation prompts
Input:
-p <file>, --path <file> Path to fileIndividual flags: Type.Flag can also be printed with their format() method via {f} print format. The left-padding and the padding between the flags and their descriptions can be changed with the .padding variable in the Type.Flag struct.
// e.g.
// This affects the printing of `Type.Flags.usage()` too
flagparse.Type.Flag.padding = {
.left = 1,
.center = 20,
} -r, --recursive Recurse into directoriespub 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-argumentative flag treated as an argumentative
DuplicateFlag, // flag appears twice in arg list; can be ignored with config
ArgNoArg, // no argument given to argumentative flag
OutOfMemory, // something's size exceeds its buffers len
NoWriter, // no writer given when verbose is true
TypeMismatch, // a more general FlagNotSwitch/FlagNotArg
}