Options & Flags ​
This guide covers how to define various types of command-line arguments in args.zig.
Flags (Boolean Options) ​
Flags are boolean options that don't require a value:
try parser.addFlag("verbose", .{
.short = 'v',
.help = "Enable verbose output",
});
try parser.addFlag("quiet", .{
.short = 'q',
.help = "Suppress output",
});Usage:
myapp -v # verbose = true
myapp --verbose # verbose = true
myapp -vq # verbose = true, quiet = true (clustered)Aliases ​
You can provide multiple names for the same option using aliases:
try parser.addArg(.{
.name = "verbose",
.long = "verbose",
.aliases = &[_][]const u8{ "loud", "debug" },
.action = .store_true,
.help = "Verbose output",
});Usage:
myapp --verbose
myapp --loud # Same as --verbose
myapp --debug # Same as --verboseNegated Long Flags ​
Boolean long flags support --no-<name> out of the box:
try parser.addFlag("cache", .{
.help = "Enable cache",
});Usage:
myapp --cache # cache = true
myapp --no-cache # cache = falseDisable this behavior in config when needed:
.config = .{ .allow_negated_flags = false }Inverse Boolean Flags ​
If your CLI uses disable-first semantics, use addFalseFlag.
try parser.addFalseFlag("color", .{
.help = "Disable color output",
});Usage:
myapp --color # stores falseCMD-Style Select And All ​
Many command tools support either selecting specific targets or all targets.
try parser.addSelectOrAll(.{
.select_short = 's',
.all_short = 'a',
.select_choices = &[_][]const u8{ "users", "groups", "logs" },
});Generated behavior:
myapp --select users
myapp --allThe pair is mutually exclusive by default.
CSV Select/All Strict Resolution ​
For CMD-style, GUI, or TUI flows where one option can target multiple items, use CSV select/all helpers:
try parser.addSelectOrAllCsv(.{
.select_short = 's',
.all_short = 'a',
});
var parsed = try parser.parseProcess();
defer parsed.deinit();
var resolved = try args.resolveSelectOrAllStrict(allocator, &parsed, .{
.choices = &[_][]const u8{ "users", "groups", "logs" },
.allow_prefix_match = true,
.dedupe = true,
});
defer resolved.deinit();Behavior summary:
--allreturnsresolved.all = truewith an empty selected list.--select users,gr,userscan normalize tousers,groupswith dedupe.- Invalid items return
error.InvalidChoicewhenchoicesare provided.
Question-Based Selection Flow ​
When users do not pass --select or --all, you can ask them interactively:
const decision = try args.resolveSelectOrAllWithPrompt(allocator, &parsed, .{
.question = "Select target",
.choices = &[_][]const u8{ "users", "groups", "logs" },
.default_choice = "users",
.allow_all = true,
});This supports:
- Choosing by number or name
- Unique prefix matches (e.g.
gr=>groups) - Optional
allchoice - Default fallback
- Retry attempts with validation
CSV Selections Utility ​
For CMD-style inputs like --select users,logs, parse values with:
const items = try args.parseCsvList(allocator, result.getString("select") orelse "");
defer args.deinitCsvList(allocator, items);This trims whitespace and skips empty entries.
Include/Exclude Filters ​
For common filter pipelines, use built-in helpers:
try parser.addIncludeExclude(.{ .include_short = 'i', .exclude_short = 'x' });
var parsed = try parser.parseProcess();
defer parsed.deinit();
var filters = try args.resolveIncludeExclude(allocator, &parsed, "include", "exclude");
defer filters.deinit();Accepted format is comma-separated values (e.g. --include users,groups).
Strict Include/Exclude Resolution ​
When you want canonical item names, duplicate suppression, and overlap validation:
var strict_filters = try args.resolveIncludeExcludeStrict(allocator, &parsed, .{
.choices = &[_][]const u8{ "users", "groups", "logs" },
.all_keyword = "all",
.allow_prefix_match = true,
.dedupe = true,
.fail_on_conflicts = true,
});
defer strict_filters.deinit();Behavior summary:
include=grcan resolve togroupswith prefix matching.- Duplicate values are collapsed when
dedupe = true. - Overlaps like
--include users --exclude usersreturnerror.IncludeExcludeConflict. all_keyword(default:all) enables full selection with selective exclusions.
File And Extension Support ​
For file-driven CLI tools, use dedicated helpers:
try parser.addFileOptionWithExtensions("input", &[_][]const u8{ "json", "yaml", "toml" }, .{
.short = 'i',
.must_exist = false,
});
try parser.addDirectoryOption("workspace", .{
.short = 'w',
.must_exist = false,
});Available helpers:
addPathOptionfor generic path values.addAbsolutePathOptionto enforce absolute path values.addFileOptionfor file-oriented options.addDirectoryOptionfor directory-oriented options.addFileOptionWithExtensionsfor extension checks with reusable validator logic.addFileNameOptionfor safe file-name-only values.addFileNameOptionWithExtensionsfor file-name plus extension checks.
Typed input helpers are also available for common API/configuration flows:
addEmailOptionaddUrlOptionaddIpv4OptionaddHostNameOptionaddPortOptionaddUuidOptionaddIsoDateOptionaddIsoDateTimeOptionaddYearOptionaddTimeOptionaddJsonOption
Use a one-call policy validator for the common case:
const output_name_validator = args.Validators.filePolicy(&[_][]const u8{"json"}, false, 3, 64);
try parser.addFileNameOption("output-name", .{
.short = 'o',
.validator = output_name_validator,
});For advanced cases, you can still compose validators:
const output_name_validator = args.Validators.all(&[_]args.ValidatorFn{
args.Validators.fileExt(&[_][]const u8{"json"}, false),
args.Validators.fileNameLength(3, 64),
});
try parser.addFileNameOption("output-name", .{
.short = 'o',
.validator = output_name_validator,
});Key-Value Pairs ​
You can automatically parse key=value strings:
try parser.addOption("define", .{
.short = 'D',
.value_type = .key_value,
.help = "Define a property",
});Usage:
myapp -D mode=releaseAccess:
if (result.getKeyValue("define")) |kv| {
// kv.key = "mode", kv.value = "release"
}Options (Value-Taking Arguments) ​
Options accept a value:
try parser.addOption("output", .{
.short = 'o',
.help = "Output file path",
});
try parser.addOption("count", .{
.short = 'n',
.help = "Number of iterations",
.value_type = .int,
.default = "10",
});Usage:
myapp -o file.txt
myapp --output file.txt
myapp --output=file.txt # inline value
myapp -n 5Positional Arguments ​
Arguments without flags:
try parser.addPositional("input", .{
.help = "Input file to process",
.required = true,
});
try parser.addPositional("output", .{
.help = "Output file (optional)",
.required = false,
.default = "output.txt",
});Usage:
myapp input.txt # input = "input.txt"
myapp input.txt result.txt # input = "input.txt", output = "result.txt"Value Types ​
args.zig supports multiple value types:
| Type | Description | Example |
|---|---|---|
.string | Text value (default) | --name Alice |
.int | Signed integer | --count -5 |
.uint | Unsigned integer | --port 8080 |
.float | Floating point | --rate 0.5 |
.bool | Boolean | --flag true |
.path | File/directory path | --config ./config.yml |
.counter | Incremented count | -v -v -v |
try parser.addOption("port", .{
.short = 'p',
.value_type = .int,
.help = "Server port",
});
try parser.addOption("rate", .{
.value_type = .float,
.help = "Processing rate",
});Counters ​
Count how many times a flag is used:
try parser.addCounter("verbose", .{
.short = 'v',
.help = "Increase verbosity (can be repeated)",
});Usage:
myapp -v # verbose = 1
myapp -vv # verbose = 2
myapp -vvv # verbose = 3Access:
const val = result.get("verbose").?;
const level = val.counter; // 0, 1, 2, 3, etc.Choices ​
Restrict values to a predefined set:
try parser.addOption("level", .{
.short = 'l',
.help = "Log level",
.choices = &[_][]const u8{ "debug", "info", "warn", "error" },
});
try parser.addOption("format", .{
.short = 'f',
.help = "Output format",
.choices = &[_][]const u8{ "json", "xml", "csv", "yaml" },
});Expected Values ​
Suggest values without strict enforcement (warns by default, configurable):
try parser.addOption("env", .{
.short = 'e',
.expect = &[_][]const u8{ "dev", "prod", "staging" },
.help = "Environment (warns if unknown)",
});Multiple Values ​
Accept multiple values for a single option:
// Accepts 1 to 3 integers: --numbers 1 2 3
try parser.addMultiple("numbers", .{
.short = 'n',
.help = "List of numbers",
.min = 1,
.max = 3,
});Appended Values ​
Collect same flag multiple times:
// --include path1 --include path2
try parser.addAppend("include", .{
.short = 'I',
.help = "Include path",
});Required vs Optional ​
// Required option
try parser.addOption("config", .{
.short = 'c',
.help = "Configuration file",
.required = true,
});
// Optional with default
try parser.addOption("timeout", .{
.help = "Timeout in seconds",
.value_type = .int,
.default = "30",
});Environment Variable Fallback ​
try parser.addOption("token", .{
.help = "API authentication token",
.env_var = "API_TOKEN",
.required = true,
});If --token is not provided, the parser checks $API_TOKEN.
Hidden Options ​
Options that don't appear in help text:
try parser.addOption("debug-internal", .{
.help = "Internal debugging",
.hidden = true,
});Deprecated Options ​
Mark options as deprecated:
try parser.addOption("old-format", .{
.help = "Use old format",
.deprecated = "Use --format instead",
});Complete Example ​
const std = @import("std");
const args = @import("args");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var parser = try args.ArgumentParser.init(allocator, .{
.name = "myapp",
.version = "1.0.0",
.description = "Example application",
});
defer parser.deinit();
// Flags
try parser.addFlag("verbose", .{ .short = 'v', .help = "Verbose output" });
try parser.addFlag("dry-run", .{ .short = 'n', .help = "Dry run mode" });
// Options
try parser.addOption("output", .{ .short = 'o', .help = "Output file" });
try parser.addOption("count", .{ .short = 'c', .value_type = .int, .default = "1" });
try parser.addOption("format", .{
.short = 'f',
.choices = &[_][]const u8{ "json", "csv" }
});
// Counter
try parser.addCounter("debug", .{ .short = 'd', .help = "Debug level" });
// Positional
try parser.addPositional("input", .{ .help = "Input file", .required = true });
var result = try parser.parseProcess();
defer result.deinit();
// Access values
const verbose = result.getBool("verbose") orelse false;
const count = result.getInt("count") orelse 1;
const input = result.getString("input").?;
std.debug.print("verbose={}, count={d}, input={s}\n", .{verbose, count, input});
}