From 281090c23ca631d8811d7ccb2a0dcdf4ef72a7b1 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Thu, 24 Oct 2024 17:40:56 +0200 Subject: docs: Add subcommand example --- example/README.md.template | 11 +++++- example/subcommands.zig | 96 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+), 1 deletion(-) create mode 100644 example/subcommands.zig (limited to 'example') diff --git a/example/README.md.template b/example/README.md.template index d234dcd..bcd3774 100644 --- a/example/README.md.template +++ b/example/README.md.template @@ -63,7 +63,7 @@ The result will contain an `args` field and a `positionals` field. `args` will h 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 `psotionals` are typed. The type is based on the name of the value the +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 @@ -74,6 +74,15 @@ contains a parser that returns `usize`. You can pass in something other than {s} ``` +### 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 +{s} +``` + ### `streaming.Clap` The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an diff --git a/example/subcommands.zig b/example/subcommands.zig new file mode 100644 index 0000000..531a4d6 --- /dev/null +++ b/example/subcommands.zig @@ -0,0 +1,96 @@ +// 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"); -- cgit v1.2.3