NAME
zig-clap — Command line argument parsing library
SYNOPSIS
https://github.com/Hejsil/zig-clap/releasesINFO
DESCRIPTION
Command line argument parsing library
README
zig-clap
A simple and easy to use command line argument parser library for Zig.
Installation
Developers tend to either use
- The latest tagged release of Zig
- The latest build of Zigs master branch
Depending on which developer you are, you need to run different zig fetch commands:
# Version of zig-clap that works with a tagged release of Zig # Replace `<REPLACE ME>` with the version of zig-clap that you want to use # See: https://github.com/Hejsil/zig-clap/releases zig fetch --save https://github.com/Hejsil/zig-clap/archive/refs/tags/<REPLACE ME>.tar.gzVersion of zig-clap that works with latest build of Zigs master branch
zig fetch --save git+https://github.com/Hejsil/zig-clap
Then add the following to build.zig:
const clap = b.dependency("clap", .{});
exe.root_module.addImport("clap", clap.module("clap"));
Features
- Short arguments
-a- Chaining
-abcwhereaandbdo not take values. - Multiple specifications are tallied (e.g.
-v -v).
- Chaining
- Long arguments
--long - Supports both passing values using spacing and
=(-a 100,-a=100)- Short args also support passing values with no spacing or
=(-a100) - This all works with chaining (
-ba 100,-ba=100,-ba100)
- Short args also support passing values with no spacing or
- Supports options that can be specified multiple times (
-e 1 -e 2 -e 3) - Print help message from parameter specification.
- Parse help message to parameter specification.
API Reference
Automatically generated API Reference for the project can be found at https://Hejsil.github.io/zig-clap. Note that Zig autodoc is in beta; the website may be broken or incomplete.
Examples
clap.parse
The simplest way to use this library is to just call the clap.parse function.
pub fn main(init: std.process.Init) !void { // First we specify what parameters our program can take. // We can use `parseParamsComptime` to parse a string into an array of `Param(Help)`. const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-n, --number <usize> An option parameter, which takes a value. \\-s, --string <str>... An option parameter which can be specified multiple times. \\<str>... \\ );// Initialize our diagnostics, which can be used for reporting useful errors. // This is optional. You can also pass `.{}` to `clap.parse` if you don't // care about the extra information `Diagnostics` provides. var diag = clap.Diagnostic{}; var res = clap.parse(clap.Help, &params, clap.parsers.default, init.minimal.args, .{ .diagnostic = &diag, .allocator = init.gpa, }) catch |err| { // Report useful error and exit. try diag.reportToFile(init.io, .stderr(), err); return err; }; defer res.deinit(); if (res.args.help != 0) std.debug.print("--help\n", .{}); if (res.args.number) |n| std.debug.print("--number = {}\n", .{n}); for (res.args.string) |s| std.debug.print("--string = {s}\n", .{s}); for (res.positionals[0]) |pos| std.debug.print("{s}\n", .{pos});}
const clap = @import("clap"); const std = @import("std");
The result will contain an args field and a positionals field. args will have one field for
each non-positional parameter of your program. The name of the field will be the longest name of the
parameter. positionals will be a tuple with one field for each positional parameter.
The fields in args and positionals are typed. The type is based on the name of the value the
parameter takes. Since --number takes a usize the field res.args.number has the type usize.
Note that this is only the case because clap.parsers.default has a field called usize which
contains a parser that returns usize. You can pass in something other than clap.parsers.default
if you want some other mapping.
pub fn main(init: std.process.Init) !void { // First we specify what parameters our program can take. // We can use `parseParamsComptime` to parse a string into an array of `Param(Help)`. const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-n, --number <INT> An option parameter, which takes a value. \\-a, --answer <ANSWER> An option parameter which takes an enum. \\-s, --string <STR>... An option parameter which can be specified multiple times. \\<FILE>... \\ );// Declare our own parsers which are used to map the argument strings to other // types. const YesNo = enum { yes, no }; const parsers = comptime .{ .STR = clap.parsers.string, .FILE = clap.parsers.string, .INT = clap.parsers.int(usize, 10), .ANSWER = clap.parsers.enumeration(YesNo), }; var diag = clap.Diagnostic{}; var res = clap.parse(clap.Help, &params, parsers, init.minimal.args, .{ .diagnostic = &diag, .allocator = init.gpa, // The assignment separator can be configured. `--number=1` and `--number:1` is now // allowed. .assignment_separators = "=:", }) catch |err| { // Report useful error and exit. try diag.reportToFile(init.io, .stderr(), err); return err; }; defer res.deinit(); if (res.args.help != 0) std.debug.print("--help\n", .{}); if (res.args.number) |n| std.debug.print("--number = {}\n", .{n}); if (res.args.answer) |a| std.debug.print("--answer = {s}\n", .{@tagName(a)}); for (res.args.string) |s| std.debug.print("--string = {s}\n", .{s}); for (res.positionals[0]) |pos| std.debug.print("{s}\n", .{pos});}
const clap = @import("clap"); const std = @import("std");
Subcommands
There is an option for clap.parse and clap.parseEx called terminating_positional. It allows
for users of clap to implement subcommands in their cli application:
// These are our subcommands. const SubCommands = enum { help, math, };const main_parsers = .{ .command = clap.parsers.enumeration(SubCommands), };
// The parameters for
main. Parameters for the subcommands are specified further down. const main_params = clap.parseParamsComptime( \-h, --help Display this help and exit. \<command> \ );// To pass around arguments returned by clap,
clap.Resultandclap.ResultExcan be used to // get the return type ofclap.parseandclap.parseEx. const MainArgs = clap.ResultEx(clap.Help, &main_params, main_parsers);pub fn main(init: std.process.Init) !void { var iter = try init.minimal.args.iterateAllocator(init.gpa); defer iter.deinit();
_ = iter.next(); var diag = clap.Diagnostic{}; var res = clap.parseEx(clap.Help, &main_params, main_parsers, &iter, .{ .diagnostic = &diag, .allocator = init.gpa, // Terminate the parsing of arguments after parsing the first positional (0 is passed // here because parsed positionals are, like slices and arrays, indexed starting at 0). // // This will terminate the parsing after parsing the subcommand enum and leave `iter` // not fully consumed. It can then be reused to parse the arguments for subcommands. .terminating_positional = 0, }) catch |err| { try diag.reportToFile(init.io, .stderr(), err); return err; }; defer res.deinit(); if (res.args.help != 0) std.debug.print("--help\n", .{}); const command = res.positionals[0] orelse return error.MissingCommand; switch (command) { .help => std.debug.print("--help\n", .{}), .math => try mathMain(init.io, init.gpa, &iter, res), }}
fn mathMain(io: std.Io, gpa: std.mem.Allocator, iter: *std.process.Args.Iterator, main_args: MainArgs) !void { // The parent arguments are not used here, but there are cases where it might be useful, so // this example shows how to pass the arguments around. _ = main_args;
// The parameters for the subcommand. const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-a, --add Add the two numbers \\-s, --sub Subtract the two numbers \\<isize> \\<isize> \\ ); // Here we pass the partially parsed argument iterator. var diag = clap.Diagnostic{}; var res = clap.parseEx(clap.Help, &params, clap.parsers.default, iter, .{ .diagnostic = &diag, .allocator = gpa, }) catch |err| { try diag.reportToFile(io, .stderr(), err); return err; // propagate error }; defer res.deinit(); const a = res.positionals[0] orelse return error.MissingArg1; const b = res.positionals[1] orelse return error.MissingArg1; if (res.args.help != 0) std.debug.print("--help\n", .{}); if (res.args.add != 0) std.debug.print("added: {}\n", .{a + b}); if (res.args.sub != 0) std.debug.print("subtracted: {}\n", .{a - b});}
const clap = @import("clap"); const std = @import("std");
streaming.Clap
The streaming.Clap is the base of all the other parsers. It's a streaming parser that uses an
args.Iterator to provide it with arguments lazily.
pub fn main(init: std.process.Init) !void { // First we specify what parameters our program can take. const params = [_]clap.Param(u8){ .{ .id = 'h', .names = .{ .short = 'h', .long = "help" }, }, .{ .id = 'n', .names = .{ .short = 'n', .long = "number" }, .takes_value = .one, }, .{ .id = 'f', .takes_value = .one }, };var iter = try init.minimal.args.iterateAllocator(init.gpa); defer iter.deinit(); // Skip exe argument. _ = iter.next(); // Initialize our diagnostics, which can be used for reporting useful errors. // This is optional. You can also leave the `diagnostic` field unset if you // don't care about the extra information `Diagnostic` provides. var diag = clap.Diagnostic{}; var parser = clap.streaming.Clap(u8, std.process.Args.Iterator){ .params = &params, .iter = &iter, .diagnostic = &diag, }; // Because we use a streaming parser, we have to consume each argument parsed individually. while (parser.next() catch |err| { // Report useful error and exit. try diag.reportToFile(init.io, .stderr(), err); return err; }) |arg| { // arg.param will point to the parameter which matched the argument. switch (arg.param.id) { 'h' => std.debug.print("Help!\n", .{}), 'n' => std.debug.print("--number = {s}\n", .{arg.value.?}), // arg.value == null, if arg.param.takes_value == .none. // Otherwise, arg.value is the value passed with the argument, such as "-a=10" // or "-a 10". 'f' => std.debug.print("{s}\n", .{arg.value.?}), else => unreachable, } }}
const clap = @import("clap"); const std = @import("std");
$ zig-out/bin/streaming-clap --help --number=1 f=10
Help!
--number = 1
f=10
Currently, this parser is the only parser that allows an array of Param that is generated at
runtime.
help
help prints a simple list of all parameters the program can take. It expects the Id to have a
description method and a value method so that it can provide that in the output. HelpOptions
is passed to help to control how the help message is printed.
pub fn main(init: std.process.Init) !void { const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-v, --version Output version information and exit. \\ );var res = try clap.parse(clap.Help, &params, clap.parsers.default, init.minimal.args, .{ .allocator = init.gpa }); defer res.deinit(); // `clap.help` is a function that can print a simple help message. It can print any `Param` // where `Id` has a `description` and `value` method (`Param(Help)` is one such parameter). // The last argument contains options as to how `help` should print those parameters. Using // `.{}` means the default options. if (res.args.help != 0) return clap.helpToFile(init.io, .stderr(), clap.Help, &params, .{});}
const clap = @import("clap"); const std = @import("std");
$ zig-out/bin/help --help
-h, --help
Display this help and exit.
-v, --version
Output version information and exit.
usage
usage prints a small abbreviated version of the help message. It expects the Id to have a
value method so it can provide that in the output.
pub fn main(init: std.process.Init) !void { const params = comptime clap.parseParamsComptime( \\-h, --help Display this help and exit. \\-v, --version Output version information and exit. \\ --value <str> An option parameter, which takes a value. \\ );var res = try clap.parse(clap.Help, &params, clap.parsers.default, init.minimal.args, .{ .allocator = init.gpa }); defer res.deinit(); // `clap.usageToFile` is a function that can print a simple usage string. It can print any // `Param` where `Id` has a `value` method (`Param(Help)` is one such parameter). if (res.args.help != 0) return clap.usageToFile(init.io, .stdout(), clap.Help, &params);}
const clap = @import("clap"); const std = @import("std");
$ zig-out/bin/usage --help
[-hv] [--value <str>]