From 5166a15378a9c32d9b680417807000ba65d06141 Mon Sep 17 00:00:00 2001 From: Jimmi Holst Christensen Date: Wed, 23 Mar 2022 20:05:28 +0100 Subject: Add parseParams and friends --- README.md | 54 ++-- clap.zig | 730 ++++++++++++++++++++++++--------------------- example/README.md.template | 4 +- example/help.zig | 9 +- example/simple-ex.zig | 15 +- example/simple.zig | 15 +- example/usage.zig | 11 +- 7 files changed, 447 insertions(+), 391 deletions(-) diff --git a/README.md b/README.md index 55a43d9..5686d54 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,14 @@ const io = std.io; pub fn main() !void { // First we specify what parameters our program can take. - // We can use `parseParam` to parse a string to a `Param(Help)` - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, - clap.parseParam("-s, --string ... An option parameter which can be specified multiple times.") catch unreachable, - clap.parseParam("...") catch unreachable, - }; + // 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. + \\... + \\ + ); // Initalize our diagnostics, which can be used for reporting useful errors. // This is optional. You can also pass `.{}` to `clap.parse` if you don't @@ -73,8 +74,8 @@ The fields in `args` are typed. The type is based on the name of the value the p 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. +contains a parser that returns `usize`. You can pass in something other than +`clap.parsers.default` if you want some other mapping. ```zig const clap = @import("clap"); @@ -86,13 +87,14 @@ const process = std.process; pub fn main() !void { // First we specify what parameters our program can take. - // We can use `parseParam` to parse a string to a `Param(Help)` - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, - clap.parseParam("-s, --string ... An option parameter which can be specified multiple times.") catch unreachable, - clap.parseParam("...") catch unreachable, - }; + // 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. + \\... + \\ + ); // Declare our own parsers which are used to map the argument strings to other // types. @@ -205,10 +207,11 @@ const clap = @import("clap"); const std = @import("std"); pub fn main() !void { - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, - clap.parseParam("-v, --version Output version information and exit.") catch unreachable, - }; + 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, .{}); defer res.deinit(); @@ -238,11 +241,12 @@ const clap = @import("clap"); const std = @import("std"); pub fn main() !void { - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-v, --version Output version information and exit.") catch unreachable, - clap.parseParam(" --value An option parameter, which takes a value.") catch unreachable, - }; + 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, .{}); defer res.deinit(); diff --git a/clap.zig b/clap.zig index d55ccc2..0a08634 100644 --- a/clap.zig +++ b/clap.zig @@ -78,9 +78,76 @@ pub fn Param(comptime Id: type) type { }; } +/// Takes a string and parses it into many Param(Help). Returned is a newly allocated slice +/// containing all the parsed params. The caller is responsible for freeing the slice. +pub fn parseParams(allocator: mem.Allocator, str: []const u8) ![]Param(Help) { + var list = std.ArrayList(Param(Help)).init(allocator); + errdefer list.deinit(); + + try parseParamsIntoArrayList(&list, str); + return list.toOwnedSlice(); +} + +/// Takes a string and parses it into many Param(Help) at comptime. Returned is an array of +/// exactly the number of params that was parsed from `str`. A parse error becomes a compiler +/// error. +pub fn parseParamsComptime(comptime str: []const u8) [countParams(str)]Param(Help) { + var res: [countParams(str)]Param(Help) = undefined; + _ = parseParamsIntoSlice(&res, str) catch unreachable; + return res; +} + +fn countParams(str: []const u8) usize { + // See parseParam for reasoning. I would like to remove it from parseParam, but people depend + // on that function to still work conveniently at comptime, so leaving it for now. + @setEvalBranchQuota(std.math.maxInt(u32)); + + var res: usize = 0; + var it = mem.split(u8, str, "\n"); + while (it.next()) |line| { + const trimmed = mem.trimLeft(u8, line, " \t"); + if (mem.startsWith(u8, trimmed, "-") or + mem.startsWith(u8, trimmed, "<")) + { + res += 1; + } + } + + return res; +} + +/// Takes a string and parses it into many Param(Help), which are written to `slice`. A subslice +/// is returned, containing all the parameters parsed. This function will fail if the input slice +/// is to small. +pub fn parseParamsIntoSlice(slice: []Param(Help), str: []const u8) ![]Param(Help) { + var null_alloc = heap.FixedBufferAllocator.init(""); + var list = std.ArrayList(Param(Help)){ + .allocator = null_alloc.allocator(), + .items = slice[0..0], + .capacity = slice.len, + }; + + try parseParamsIntoArrayList(&list, str); + return list.items; +} + +/// Takes a string and parses it into many Param(Help), which are appended onto `list`. +pub fn parseParamsIntoArrayList(list: *std.ArrayList(Param(Help)), str: []const u8) !void { + var i: usize = 0; + while (i != str.len) { + var end: usize = undefined; + try list.append(try parseParamEx(str[i..], &end)); + i += end; + } +} + +pub fn parseParam(str: []const u8) !Param(Help) { + var end: usize = undefined; + return parseParamEx(str, &end); +} + /// Takes a string and parses it to a Param(Help). -/// This is the reverse of 'help' but for at single parameter only. -pub fn parseParam(line: []const u8) !Param(Help) { +pub fn parseParamEx(str: []const u8, end: *usize) !Param(Help) { // This function become a lot less ergonomic to use once you hit the eval branch quota. To // avoid this we pick a sane default. Sadly, the only sane default is the biggest possible // value. If we pick something a lot smaller and a user hits the quota after that, they have @@ -120,157 +187,177 @@ pub fn parseParam(line: []const u8) !Param(Help) { rest_of_description, rest_of_description_new_line, } = .start; - for (line) |c, i| switch (state) { - .start => switch (c) { - ' ', '\t', '\n' => {}, - '-' => state = .start_of_short_name, - '<' => state = .first_char_of_value, - else => return error.InvalidParameter, - }, + for (str) |c, i| { + errdefer end.* = i; + + switch (state) { + .start => switch (c) { + ' ', '\t', '\n' => {}, + '-' => state = .start_of_short_name, + '<' => state = .first_char_of_value, + else => return error.InvalidParameter, + }, - .start_of_short_name => switch (c) { - '-' => state = .first_char_of_long_name, - 'a'...'z', 'A'...'Z', '0'...'9' => { - res.names.short = c; - state = .end_of_short_name; + .start_of_short_name => switch (c) { + '-' => state = .first_char_of_long_name, + 'a'...'z', 'A'...'Z', '0'...'9' => { + res.names.short = c; + state = .end_of_short_name; + }, + else => return error.InvalidParameter, + }, + .end_of_short_name => switch (c) { + ' ', '\t' => state = .before_long_name_or_value_or_description, + '\n' => state = .before_description_new_line, + ',' => state = .before_long_name, + else => return error.InvalidParameter, }, - else => return error.InvalidParameter, - }, - .end_of_short_name => switch (c) { - ' ', '\t' => state = .before_long_name_or_value_or_description, - '\n' => state = .before_description_new_line, - ',' => state = .before_long_name, - else => return error.InvalidParameter, - }, - .before_long_name => switch (c) { - ' ', '\t' => {}, - '-' => state = .start_of_long_name, - else => return error.InvalidParameter, - }, - .start_of_long_name => switch (c) { - '-' => state = .first_char_of_long_name, - else => return error.InvalidParameter, - }, - .first_char_of_long_name => switch (c) { - 'a'...'z', 'A'...'Z', '0'...'9' => { - start = i; - state = .rest_of_long_name; + .before_long_name => switch (c) { + ' ', '\t' => {}, + '-' => state = .start_of_long_name, + else => return error.InvalidParameter, }, - else => return error.InvalidParameter, - }, - .rest_of_long_name => switch (c) { - 'a'...'z', 'A'...'Z', '0'...'9' => {}, - ' ', '\t' => { - res.names.long = line[start..i]; - state = .before_value_or_description; + .start_of_long_name => switch (c) { + '-' => state = .first_char_of_long_name, + else => return error.InvalidParameter, }, - '\n' => { - res.names.long = line[start..i]; - state = .before_description_new_line; + .first_char_of_long_name => switch (c) { + 'a'...'z', 'A'...'Z', '0'...'9' => { + start = i; + state = .rest_of_long_name; + }, + else => return error.InvalidParameter, + }, + .rest_of_long_name => switch (c) { + 'a'...'z', 'A'...'Z', '0'...'9' => {}, + ' ', '\t' => { + res.names.long = str[start..i]; + state = .before_value_or_description; + }, + '\n' => { + res.names.long = str[start..i]; + state = .before_description_new_line; + }, + else => return error.InvalidParameter, }, - else => return error.InvalidParameter, - }, - .before_long_name_or_value_or_description => switch (c) { - ' ', '\t' => {}, - ',' => state = .before_long_name, - '<' => state = .first_char_of_value, - else => { - start = i; - state = .rest_of_description; + .before_long_name_or_value_or_description => switch (c) { + ' ', '\t' => {}, + ',' => state = .before_long_name, + '<' => state = .first_char_of_value, + else => { + start = i; + state = .rest_of_description; + }, }, - }, - .before_value_or_description => switch (c) { - ' ', '\t' => {}, - '<' => state = .first_char_of_value, - else => { - start = i; - state = .rest_of_description; + .before_value_or_description => switch (c) { + ' ', '\t' => {}, + '<' => state = .first_char_of_value, + else => { + start = i; + state = .rest_of_description; + }, }, - }, - .first_char_of_value => switch (c) { - '>' => return error.InvalidParameter, - else => { - start = i; - state = .rest_of_value; + .first_char_of_value => switch (c) { + '>' => return error.InvalidParameter, + else => { + start = i; + state = .rest_of_value; + }, }, - }, - .rest_of_value => switch (c) { - '>' => { - res.takes_value = .one; - res.id.val = line[start..i]; - state = .end_of_one_value; + .rest_of_value => switch (c) { + '>' => { + res.takes_value = .one; + res.id.val = str[start..i]; + state = .end_of_one_value; + }, + else => {}, }, - else => {}, - }, - .end_of_one_value => switch (c) { - '.' => state = .second_dot_of_multi_value, - ' ', '\t' => state = .before_description, - '\n' => state = .before_description_new_line, - else => { - start = i; - state = .rest_of_description; + .end_of_one_value => switch (c) { + '.' => state = .second_dot_of_multi_value, + ' ', '\t' => state = .before_description, + '\n' => state = .before_description_new_line, + else => { + start = i; + state = .rest_of_description; + }, }, - }, - .second_dot_of_multi_value => switch (c) { - '.' => state = .third_dot_of_multi_value, - else => return error.InvalidParameter, - }, - .third_dot_of_multi_value => switch (c) { - '.' => { - res.takes_value = .many; - state = .before_description; + .second_dot_of_multi_value => switch (c) { + '.' => state = .third_dot_of_multi_value, + else => return error.InvalidParameter, + }, + .third_dot_of_multi_value => switch (c) { + '.' => { + res.takes_value = .many; + state = .before_description; + }, + else => return error.InvalidParameter, }, - else => return error.InvalidParameter, - }, - .before_description => switch (c) { - ' ', '\t' => {}, - '\n' => state = .before_description_new_line, - else => { - start = i; - state = .rest_of_description; + .before_description => switch (c) { + ' ', '\t' => {}, + '\n' => state = .before_description_new_line, + else => { + start = i; + state = .rest_of_description; + }, }, - }, - .before_description_new_line => switch (c) { - ' ', '\t', '\n' => {}, - '-' => break, - else => { - start = i; - state = .rest_of_description; + .before_description_new_line => switch (c) { + ' ', '\t', '\n' => {}, + '-', '<' => { + end.* = i; + break; + }, + else => { + start = i; + state = .rest_of_description; + }, }, - }, - .rest_of_description => switch (c) { - '\n' => state = .rest_of_description_new_line, - else => {}, - }, - .rest_of_description_new_line => switch (c) { - ' ', '\t', '\n' => {}, - '-' => { - res.id.desc = mem.trimRight(u8, line[start..i], " \t\n\r"); - break; + .rest_of_description => switch (c) { + '\n' => state = .rest_of_description_new_line, + else => {}, }, - else => state = .rest_of_description, - }, - } else switch (state) { - .rest_of_description, .rest_of_description_new_line => { - res.id.desc = mem.trimRight(u8, line[start..], " \t\n\r"); - }, - .rest_of_long_name => res.names.long = line[start..], - .end_of_short_name, - .end_of_one_value, - .before_value_or_description, - .before_description, - .before_description_new_line, - => {}, - else => return error.InvalidParameter, + .rest_of_description_new_line => switch (c) { + ' ', '\t', '\n' => {}, + '-', '<' => { + res.id.desc = mem.trimRight(u8, str[start..i], " \t\n\r"); + end.* = i; + break; + }, + else => state = .rest_of_description, + }, + } + } else { + end.* = str.len; + switch (state) { + .rest_of_description, .rest_of_description_new_line => { + res.id.desc = mem.trimRight(u8, str[start..], " \t\n\r"); + }, + .rest_of_long_name => res.names.long = str[start..], + .end_of_short_name, + .end_of_one_value, + .before_value_or_description, + .before_description, + .before_description_new_line, + => {}, + else => return error.InvalidParameter, + } } return res; } +fn testParseParams(str: []const u8, expected_params: []const Param(Help)) !void { + const actual_params = try parseParams(testing.allocator, str); + defer testing.allocator.free(actual_params); + + try testing.expectEqual(expected_params.len, actual_params.len); + for (expected_params) |_, i| + try expectParam(expected_params[i], actual_params[i]); +} + fn expectParam(expect: Param(Help), actual: Param(Help)) !void { try testing.expectEqualStrings(expect.id.desc, actual.id.desc); try testing.expectEqualStrings(expect.id.val, actual.id.val); @@ -283,157 +370,112 @@ fn expectParam(expect: Param(Help), actual: Param(Help)) !void { } } -test "parseParam" { - try expectParam(.{ - .id = .{}, - .names = .{ .short = 's' }, - }, try parseParam("-s")); - - try expectParam(.{ - .id = .{}, - .names = .{ .long = "str" }, - }, try parseParam("--str")); - - try expectParam(.{ - .id = .{}, - .names = .{ .short = 's', .long = "str" }, - }, try parseParam("-s, --str")); - - try expectParam(.{ - .id = .{ .val = "str" }, - .names = .{ .long = "str" }, - .takes_value = .one, - }, try parseParam("--str ")); - - try expectParam(.{ - .id = .{ .val = "str" }, - .names = .{ .short = 's', .long = "str" }, - .takes_value = .one, - }, try parseParam("-s, --str ")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "val" }, - .names = .{ .short = 's', .long = "long" }, - .takes_value = .one, - }, try parseParam("-s, --long Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "val" }, - .names = .{ .short = 's', .long = "long" }, - .takes_value = .many, - }, try parseParam("-s, --long ... Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "val" }, - .names = .{ .long = "long" }, - .takes_value = .one, - }, try parseParam("--long Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "val" }, - .names = .{ .short = 's' }, - .takes_value = .one, - }, try parseParam("-s Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text" }, - .names = .{ .short = 's', .long = "long" }, - }, try parseParam("-s, --long Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text" }, - .names = .{ .short = 's' }, - }, try parseParam("-s Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text" }, - .names = .{ .long = "long" }, - }, try parseParam("--long Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "A | B" }, - .names = .{ .long = "long" }, - .takes_value = .one, - }, try parseParam("--long Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "A" }, - .names = .{}, - .takes_value = .one, - }, try parseParam(" Help text")); - - try expectParam(.{ - .id = .{ .desc = "Help text", .val = "A" }, - .names = .{}, - .takes_value = .many, - }, try parseParam("... Help text")); - - try expectParam(.{ - .id = .{ - .desc = - \\This is - \\ help spanning multiple - \\ lines - , - }, - .names = .{ .long = "aa" }, - .takes_value = .none, - }, try parseParam( +test "parseParams" { + try testParseParams( + \\-s + \\--str + \\-s, --str + \\--str + \\-s, --str + \\-s, --long Help text + \\-s, --long ... Help text + \\--long Help text + \\-s Help text + \\-s, --long Help text + \\-s Help text + \\--long Help text + \\--long Help text + \\ Help text + \\... Help text \\--aa This is \\ help spanning multiple \\ lines \\ - )); - - try expectParam(.{ - .id = .{ .desc = "This msg should end and the newline cause of new param" }, - .names = .{ .long = "aa" }, - .takes_value = .none, - }, try parseParam( \\--aa This msg should end and the newline cause of new param - \\--bb This should not end up being parsed - \\ - )); - - try expectParam(.{ - .id = .{}, - .names = .{ .short = 'a' }, - .takes_value = .none, - }, try parseParam( - \\-a - \\--bb - \\ - )); - - try expectParam(.{ - .id = .{}, - .names = .{ .long = "aa" }, - .takes_value = .none, - }, try parseParam( - \\--aa - \\--bb + \\--bb This should be a new param \\ - )); - - try expectParam(.{ - .id = .{ .val = "q" }, - .names = .{ .short = 'a' }, - .takes_value = .one, - }, try parseParam( - \\-a - \\--bb - \\ - )); - - try expectParam(.{ - .id = .{ .val = "q" }, - .names = .{ .short = 'a' }, - .takes_value = .many, - }, try parseParam( - \\-a ... - \\--bb - \\ - )); + , &.{ + .{ .names = .{ .short = 's' } }, + .{ .names = .{ .long = "str" } }, + .{ .names = .{ .short = 's', .long = "str" } }, + .{ + .id = .{ .val = "str" }, + .names = .{ .long = "str" }, + .takes_value = .one, + }, + .{ + .id = .{ .val = "str" }, + .names = .{ .short = 's', .long = "str" }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text", .val = "val" }, + .names = .{ .short = 's', .long = "long" }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text", .val = "val" }, + .names = .{ .short = 's', .long = "long" }, + .takes_value = .many, + }, + .{ + .id = .{ .desc = "Help text", .val = "val" }, + .names = .{ .long = "long" }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text", .val = "val" }, + .names = .{ .short = 's' }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text" }, + .names = .{ .short = 's', .long = "long" }, + }, + .{ + .id = .{ .desc = "Help text" }, + .names = .{ .short = 's' }, + }, + .{ + .id = .{ .desc = "Help text" }, + .names = .{ .long = "long" }, + }, + .{ + .id = .{ .desc = "Help text", .val = "A | B" }, + .names = .{ .long = "long" }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text", .val = "A" }, + .takes_value = .one, + }, + .{ + .id = .{ .desc = "Help text", .val = "A" }, + .names = .{}, + .takes_value = .many, + }, + .{ + .id = .{ + .desc = + \\This is + \\ help spanning multiple + \\ lines + , + }, + .names = .{ .long = "aa" }, + .takes_value = .none, + }, + .{ + .id = .{ .desc = "This msg should end and the newline cause of new param" }, + .names = .{ .long = "aa" }, + .takes_value = .none, + }, + .{ + .id = .{ .desc = "This should be a new param" }, + .names = .{ .long = "bb" }, + .takes_value = .none, + }, + }); try testing.expectError(error.InvalidParameter, parseParam("--long, Help")); try testing.expectError(error.InvalidParameter, parseParam("-s, Help")); @@ -821,33 +863,35 @@ fn Arguments( } test "str and u64" { - const params = comptime &.{ - parseParam("--str ") catch unreachable, - parseParam("--num ") catch unreachable, - }; + const params = comptime parseParamsComptime( + \\--str + \\--num + \\ + ); var iter = args.SliceIterator{ .args = &.{ "--num", "10", "--str", "cooley_rec_inp_ptr" }, }; - var res = try parseEx(Help, params, parsers.default, &iter, .{ + var res = try parseEx(Help, ¶ms, parsers.default, &iter, .{ .allocator = testing.allocator, }); defer res.deinit(); } test "" { - const params = comptime &.{ - parseParam("-a, --aa") catch unreachable, - parseParam("-b, --bb") catch unreachable, - parseParam("-c, --cc ") catch unreachable, - parseParam("-d, --dd ...") catch unreachable, - parseParam("") catch unreachable, - }; + const params = comptime parseParamsComptime( + \\-a, --aa + \\-b, --bb + \\-c, --cc + \\-d, --dd ... + \\ + \\ + ); var iter = args.SliceIterator{ .args = &.{ "-a", "-c", "0", "something", "-d", "1", "--dd", "2" }, }; - var res = try parseEx(Help, params, parsers.default, &iter, .{ + var res = try parseEx(Help, ¶ms, parsers.default, &iter, .{ .allocator = testing.allocator, }); defer res.deinit(); @@ -888,10 +932,11 @@ fn testErr( } test "errors" { - const params = comptime [_]Param(Help){ - parseParam("-a, --aa") catch unreachable, - parseParam("-c, --cc ") catch unreachable, - }; + const params = comptime parseParamsComptime( + \\-a, --aa + \\-c, --cc + \\ + ); try testErr(¶ms, &.{"q"}, "Invalid argument 'q'\n"); try testErr(¶ms, &.{"-q"}, "Invalid argument '-q'\n"); @@ -957,7 +1002,7 @@ pub fn help(stream: anytype, comptime Id: type, params: []const Param(Id)) !void try stream.writeByteNTimes(' ', max_spacing); } try stream.writeAll("\t"); - try stream.writeAll(line); + try stream.writeAll(mem.trimLeft(u8, line, " \t")); try stream.writeAll("\n"); } } @@ -998,25 +1043,22 @@ test "clap.help" { var buf: [1024]u8 = undefined; var slice_stream = io.fixedBufferStream(&buf); - @setEvalBranchQuota(10000); - try help( - slice_stream.writer(), - Help, - comptime &.{ - parseParam("-a Short flag.") catch unreachable, - parseParam("-b Short option.") catch unreachable, - parseParam("--aa Long flag.") catch unreachable, - parseParam("--bb Long option.") catch unreachable, - parseParam("-c, --cc Both flag.") catch unreachable, - parseParam("--complicate Flag with a complicated and\nvery long description that\nspans multiple lines.") catch unreachable, - parseParam("-d, --dd Both option.") catch unreachable, - parseParam("-d, --dd ... Both repeated option.") catch unreachable, - parseParam( - "

Positional. This should not appear in the help message.", - ) catch unreachable, - }, + const params = comptime parseParamsComptime( + \\-a Short flag. + \\-b Short option. + \\--aa Long flag. + \\--bb Long option. + \\-c, --cc Both flag. + \\--complicate Flag with a complicated and + \\ very long description that + \\ spans multiple lines. + \\-d, --dd Both option. + \\-d, --dd ... Both repeated option. + \\

Positional. This should not appear in the help message. + \\ ); + try help(slice_stream.writer(), Help, ¶ms); const expected = "" ++ "\t-a \tShort flag.\n" ++ "\t-b \tShort option.\n" ++ @@ -1105,38 +1147,44 @@ fn testUsage(expected: []const u8, params: []const Param(Help)) !void { test "usage" { @setEvalBranchQuota(100000); - try testUsage("[-ab]", &.{ - try parseParam("-a"), - try parseParam("-b"), - }); - try testUsage("[-a ] [-b ]", &.{ - try parseParam("-a "), - try parseParam("-b "), - }); - try testUsage("[--a] [--b]", &.{ - try parseParam("--a"), - try parseParam("--b"), - }); - try testUsage("[--a ] [--b ]", &.{ - try parseParam("--a "), - try parseParam("--b "), - }); - try testUsage("", &.{ - try parseParam(""), - }); + try testUsage("[-ab]", &comptime parseParamsComptime( + \\-a + \\-b + \\ + )); + try testUsage("[-a ] [-b ]", &comptime parseParamsComptime( + \\-a + \\-b + \\ + )); + try testUsage("[--a] [--b]", &comptime parseParamsComptime( + \\--a + \\--b + \\ + )); + try testUsage("[--a ] [--b ]", &comptime parseParamsComptime( + \\--a + \\--b + \\ + )); + try testUsage("", &comptime parseParamsComptime( + \\ + \\ + )); try testUsage( "[-ab] [-c ] [-d ] [--e] [--f] [--g ] [--h ] [-i ...] ", - &.{ - try parseParam("-a"), - try parseParam("-b"), - try parseParam("-c "), - try parseParam("-d "), - try parseParam("--e"), - try parseParam("--f"), - try parseParam("--g "), - try parseParam("--h "), - try parseParam("-i ..."), - try parseParam(""), - }, + &comptime parseParamsComptime( + \\-a + \\-b + \\-c + \\-d + \\--e + \\--f + \\--g + \\--h + \\-i ... + \\ + \\ + ), ); } diff --git a/example/README.md.template b/example/README.md.template index 9fbc1cc..c19c1cd 100644 --- a/example/README.md.template +++ b/example/README.md.template @@ -35,8 +35,8 @@ The fields in `args` are typed. The type is based on the name of the value the p 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. +contains a parser that returns `usize`. You can pass in something other than +`clap.parsers.default` if you want some other mapping. ```zig {s} diff --git a/example/help.zig b/example/help.zig index f3edb58..64d1709 100644 --- a/example/help.zig +++ b/example/help.zig @@ -2,10 +2,11 @@ const clap = @import("clap"); const std = @import("std"); pub fn main() !void { - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, - clap.parseParam("-v, --version Output version information and exit.") catch unreachable, - }; + 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, .{}); defer res.deinit(); diff --git a/example/simple-ex.zig b/example/simple-ex.zig index 6cb4c3f..d0d214d 100644 --- a/example/simple-ex.zig +++ b/example/simple-ex.zig @@ -7,13 +7,14 @@ const process = std.process; pub fn main() !void { // First we specify what parameters our program can take. - // We can use `parseParam` to parse a string to a `Param(Help)` - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, - clap.parseParam("-s, --string ... An option parameter which can be specified multiple times.") catch unreachable, - clap.parseParam("...") catch unreachable, - }; + // 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. + \\... + \\ + ); // Declare our own parsers which are used to map the argument strings to other // types. diff --git a/example/simple.zig b/example/simple.zig index 11e975e..1ac7de5 100644 --- a/example/simple.zig +++ b/example/simple.zig @@ -6,13 +6,14 @@ const io = std.io; pub fn main() !void { // First we specify what parameters our program can take. - // We can use `parseParam` to parse a string to a `Param(Help)` - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-n, --number An option parameter, which takes a value.") catch unreachable, - clap.parseParam("-s, --string ... An option parameter which can be specified multiple times.") catch unreachable, - clap.parseParam("...") catch unreachable, - }; + // 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. + \\... + \\ + ); // Initalize our diagnostics, which can be used for reporting useful errors. // This is optional. You can also pass `.{}` to `clap.parse` if you don't diff --git a/example/usage.zig b/example/usage.zig index 20d4736..f57b07c 100644 --- a/example/usage.zig +++ b/example/usage.zig @@ -2,11 +2,12 @@ const clap = @import("clap"); const std = @import("std"); pub fn main() !void { - const params = comptime [_]clap.Param(clap.Help){ - clap.parseParam("-h, --help Display this help and exit.") catch unreachable, - clap.parseParam("-v, --version Output version information and exit.") catch unreachable, - clap.parseParam(" --value An option parameter, which takes a value.") catch unreachable, - }; + 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, .{}); defer res.deinit(); -- cgit v1.2.3