Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/Hejsil/zig-clap
Command line argument parsing library
https://github.com/Hejsil/zig-clap
argument-parser argument-parsing command-line command-line-parser parsed-arguments zig-package ziglang
Last synced: 3 months ago
JSON representation
Command line argument parsing library
- Host: GitHub
- URL: https://github.com/Hejsil/zig-clap
- Owner: Hejsil
- License: mit
- Created: 2018-03-14T13:06:16.000Z (almost 7 years ago)
- Default Branch: master
- Last Pushed: 2024-10-24T15:48:36.000Z (3 months ago)
- Last Synced: 2024-10-25T21:30:21.638Z (3 months ago)
- Topics: argument-parser, argument-parsing, command-line, command-line-parser, parsed-arguments, zig-package, ziglang
- Language: Zig
- Homepage:
- Size: 387 KB
- Stars: 929
- Watchers: 14
- Forks: 67
- Open Issues: 3
-
Metadata Files:
- Readme: README.md
- Funding: .github/FUNDING.yml
- License: LICENSE
Awesome Lists containing this project
- awesome-zig - Hejsil/zig-clap
- awesome-zig - zig-clap🗒️Simple 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 branchDepending on which developer you are, you need to run different `zig fetch` commands:
```sh
# Version of zig-clap that works with a tagged release of Zig
# Replace `` 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/.tar.gz# Version 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`:
```zig
const clap = b.dependency("clap", .{});
exe.root_module.addImport("clap", clap.module("clap"));
```## Features
* Short arguments `-a`
* Chaining `-abc` where `a` and `b` does not take values.
* Multiple specifications are tallied (e.g. `-v -v`).
* 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`)
* 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.
```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();// 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 An option parameter, which takes a value.
\\-s, --string ... An option parameter which can be specified multiple times.
\\...
\\
);// 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, ¶ms, clap.parsers.default, .{
.diagnostic = &diag,
.allocator = gpa.allocator(),
}) catch |err| {
// Report useful error and exit
diag.report(std.io.getStdErr().writer(), err) catch {};
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 none positional parameter of your program. The name of the field will be the longest
name of the parameter. `positionals` be a tuple with one field for each positional parameter.The fields in `args` and `postionals` 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.```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();// 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 An option parameter, which takes a value.
\\-a, --answer An option parameter which takes an enum.
\\-s, --string ... An option parameter which can be specified multiple times.
\\...
\\
);// 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, ¶ms, parsers, .{
.diagnostic = &diag,
.allocator = gpa.allocator(),
// The assignment separator can be configured. `--number=1` and `--number:1` is now
// allowed.
.assignment_separators = "=:",
}) catch |err| {
diag.report(std.io.getStdErr().writer(), err) catch {};
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:```zig
// 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.
\\
\\
);// To pass around arguments returned by clap, `clap.Result` and `clap.ResultEx` can be used to
// get the return type of `clap.parse` and `clap.parseEx`
const MainArgs = clap.ResultEx(clap.Help, &main_params, main_parsers);pub fn main() !void {
var gpa_state = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = gpa_state.allocator();
defer _ = gpa_state.deinit();var iter = try std.process.ArgIterator.initWithAllocator(gpa);
defer iter.deinit();_ = iter.next();
var diag = clap.Diagnostic{};
var res = clap.parseEx(clap.Help, &main_params, main_parsers, &iter, .{
.diagnostic = &diag,
.allocator = 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| {
diag.report(std.io.getStdErr().writer(), err) catch {};
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(gpa, &iter, res),
}
}fn mathMain(gpa: std.mem.Allocator, iter: *std.process.ArgIterator, main_args: MainArgs) !void {
// The parent arguments are not used it, but there are cases where it might be useful, so
// the 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
\\
\\
\\
);// Here we pass the partially parsed argument iterator
var diag = clap.Diagnostic{};
var res = clap.parseEx(clap.Help, ¶ms, clap.parsers.default, iter, .{
.diagnostic = &diag,
.allocator = gpa,
}) catch |err| {
diag.report(std.io.getStdErr().writer(), err) catch {};
return err;
};
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.```zig
pub fn main() !void {
const allocator = std.heap.page_allocator;// 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 std.process.ArgIterator.initWithAllocator(allocator);
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.ArgIterator){
.params = ¶ms,
.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
diag.report(std.io.getStdErr().writer(), err) catch {};
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");```
Currently, this parser is the only parser that allows an array of `Param` that
is generated at runtime.### `help`
The `help` prints a simple list of all parameters the program can take. It expects the
`Id` to have a `description` method and an `value` method so that it can provide that
in the output. `HelpOptions` is passed to `help` to control how the help message is
printed.```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();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, ¶ms, clap.parsers.default, .{
.allocator = gpa.allocator(),
});
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.help(std.io.getStdErr().writer(), clap.Help, ¶ms, .{});
}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`
The `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.```zig
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();const params = comptime clap.parseParamsComptime(
\\-h, --help Display this help and exit.
\\-v, --version Output version information and exit.
\\ --value An option parameter, which takes a value.
\\
);var res = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{
.allocator = gpa.allocator(),
});
defer res.deinit();// `clap.usage` is a function that can print a simple help message. It can print any `Param`
// where `Id` has a `value` method (`Param(Help)` is one such parameter).
if (res.args.help != 0)
return clap.usage(std.io.getStdErr().writer(), clap.Help, ¶ms);
}const clap = @import("clap");
const std = @import("std");```
```
$ zig-out/bin/usage --help
[-hv] [--value ]
```