summaryrefslogtreecommitdiff
path: root/clap.zig
diff options
context:
space:
mode:
authorGravatar Asherah Connor2020-08-23 13:48:12 +1000
committerGravatar Jimmi Holst Christensen2020-09-06 17:31:27 +0200
commit140ace899ae4ecfafc9b635609b656050c9c355d (patch)
treec8f6e57a5ed51a1040fe1fd88892a0f73d6a50b1 /clap.zig
parentparse multiple options (diff)
downloadzig-clap-140ace899ae4ecfafc9b635609b656050c9c355d.tar.gz
zig-clap-140ace899ae4ecfafc9b635609b656050c9c355d.tar.xz
zig-clap-140ace899ae4ecfafc9b635609b656050c9c355d.zip
parse and validate multiple option
Diffstat (limited to 'clap.zig')
-rw-r--r--clap.zig106
1 files changed, 72 insertions, 34 deletions
diff --git a/clap.zig b/clap.zig
index 588fa47..1a3659b 100644
--- a/clap.zig
+++ b/clap.zig
@@ -25,6 +25,12 @@ pub const Names = struct {
25 long: ?[]const u8 = null, 25 long: ?[]const u8 = null,
26}; 26};
27 27
28pub const Values = enum {
29 None,
30 One,
31 Many,
32};
33
28/// Represents a parameter for the command line. 34/// Represents a parameter for the command line.
29/// Parameters come in three kinds: 35/// Parameters come in three kinds:
30/// * Short ("-a"): Should be used for the most commonly used parameters in your program. 36/// * Short ("-a"): Should be used for the most commonly used parameters in your program.
@@ -50,7 +56,7 @@ pub fn Param(comptime Id: type) type {
50 return struct { 56 return struct {
51 id: Id = Id{}, 57 id: Id = Id{},
52 names: Names = Names{}, 58 names: Names = Names{},
53 takes_value: bool = false, 59 takes_value: Values = .None,
54 }; 60 };
55} 61}
56 62
@@ -86,8 +92,13 @@ pub fn parseParam(line: []const u8) !Param(Help) {
86 const start = mem.indexOfScalar(u8, help_msg, '<').? + 1; 92 const start = mem.indexOfScalar(u8, help_msg, '<').? + 1;
87 const len = mem.indexOfScalar(u8, help_msg[start..], '>') orelse break :blk; 93 const len = mem.indexOfScalar(u8, help_msg[start..], '>') orelse break :blk;
88 res.id.value = help_msg[start..][0..len]; 94 res.id.value = help_msg[start..][0..len];
89 res.takes_value = true; 95 if (mem.startsWith(u8, help_msg[start + len + 1 ..], "...")) {
90 help_msg = help_msg[start + len + 1 ..]; 96 res.takes_value = .Many;
97 help_msg = help_msg[start + len + 1 + 3 ..];
98 } else {
99 res.takes_value = .One;
100 help_msg = help_msg[start + len + 1 ..];
101 }
91 } 102 }
92 } 103 }
93 104
@@ -110,8 +121,13 @@ pub fn parseParam(line: []const u8) !Param(Help) {
110 const start = mem.indexOfScalar(u8, help_msg, '<').? + 1; 121 const start = mem.indexOfScalar(u8, help_msg, '<').? + 1;
111 const len = mem.indexOfScalar(u8, help_msg[start..], '>') orelse break :blk; 122 const len = mem.indexOfScalar(u8, help_msg[start..], '>') orelse break :blk;
112 res.id.value = help_msg[start..][0..len]; 123 res.id.value = help_msg[start..][0..len];
113 res.takes_value = true; 124 if (mem.startsWith(u8, help_msg[start + len + 1 ..], "...")) {
114 help_msg = help_msg[start + len + 1 ..]; 125 res.takes_value = .Many;
126 help_msg = help_msg[start + len + 1 + 3 ..];
127 } else {
128 res.takes_value = .One;
129 help_msg = help_msg[start + len + 1 ..];
130 }
115 } 131 }
116 } 132 }
117 133
@@ -134,7 +150,20 @@ test "parseParam" {
134 .short = 's', 150 .short = 's',
135 .long = find(text, "long"), 151 .long = find(text, "long"),
136 }, 152 },
137 .takes_value = true, 153 .takes_value = .One,
154 }, try parseParam(text));
155
156 text = "-s, --long <value>... Help text";
157 testing.expectEqual(Param(Help){
158 .id = Help{
159 .msg = find(text, "Help text"),
160 .value = find(text, "value"),
161 },
162 .names = Names{
163 .short = 's',
164 .long = find(text, "long"),
165 },
166 .takes_value = .Many,
138 }, try parseParam(text)); 167 }, try parseParam(text));
139 168
140 text = "--long <value> Help text"; 169 text = "--long <value> Help text";
@@ -147,7 +176,7 @@ test "parseParam" {
147 .short = null, 176 .short = null,
148 .long = find(text, "long"), 177 .long = find(text, "long"),
149 }, 178 },
150 .takes_value = true, 179 .takes_value = .One,
151 }, try parseParam(text)); 180 }, try parseParam(text));
152 181
153 text = "-s <value> Help text"; 182 text = "-s <value> Help text";
@@ -160,7 +189,7 @@ test "parseParam" {
160 .short = 's', 189 .short = 's',
161 .long = null, 190 .long = null,
162 }, 191 },
163 .takes_value = true, 192 .takes_value = .One,
164 }, try parseParam(text)); 193 }, try parseParam(text));
165 194
166 text = "-s, --long Help text"; 195 text = "-s, --long Help text";
@@ -173,7 +202,7 @@ test "parseParam" {
173 .short = 's', 202 .short = 's',
174 .long = find(text, "long"), 203 .long = find(text, "long"),
175 }, 204 },
176 .takes_value = false, 205 .takes_value = .None,
177 }, try parseParam(text)); 206 }, try parseParam(text));
178 207
179 text = "-s Help text"; 208 text = "-s Help text";
@@ -186,7 +215,7 @@ test "parseParam" {
186 .short = 's', 215 .short = 's',
187 .long = null, 216 .long = null,
188 }, 217 },
189 .takes_value = false, 218 .takes_value = .None,
190 }, try parseParam(text)); 219 }, try parseParam(text));
191 220
192 text = "--long Help text"; 221 text = "--long Help text";
@@ -199,7 +228,7 @@ test "parseParam" {
199 .short = null, 228 .short = null,
200 .long = find(text, "long"), 229 .long = find(text, "long"),
201 }, 230 },
202 .takes_value = false, 231 .takes_value = .None,
203 }, try parseParam(text)); 232 }, try parseParam(text));
204 233
205 text = "--long <A | B> Help text"; 234 text = "--long <A | B> Help text";
@@ -212,7 +241,7 @@ test "parseParam" {
212 .short = null, 241 .short = null,
213 .long = find(text, "long"), 242 .long = find(text, "long"),
214 }, 243 },
215 .takes_value = true, 244 .takes_value = .One,
216 }, try parseParam(text)); 245 }, try parseParam(text));
217 246
218 testing.expectError(error.NoParamFound, parseParam("Help")); 247 testing.expectError(error.NoParamFound, parseParam("Help"));
@@ -334,8 +363,11 @@ fn printParam(
334 363
335 try stream.print("--{}", .{l}); 364 try stream.print("--{}", .{l});
336 } 365 }
337 if (param.takes_value) 366 switch (param.takes_value) {
338 try stream.print(" <{}>", .{valueText(context, param)}); 367 .None => {},
368 .One => try stream.print(" <{}>", .{valueText(context, param)}),
369 .Many => try stream.print(" <{}>...", .{valueText(context, param)}),
370 }
339} 371}
340 372
341/// A wrapper around helpFull for simple helpText and valueText functions that 373/// A wrapper around helpFull for simple helpText and valueText functions that
@@ -400,28 +432,30 @@ test "clap.help" {
400 try help( 432 try help(
401 slice_stream.outStream(), 433 slice_stream.outStream(),
402 comptime &[_]Param(Help){ 434 comptime &[_]Param(Help){
403 parseParam("-a Short flag. ") catch unreachable, 435 parseParam("-a Short flag. ") catch unreachable,
404 parseParam("-b <V1> Short option.") catch unreachable, 436 parseParam("-b <V1> Short option.") catch unreachable,
405 parseParam("--aa Long flag. ") catch unreachable, 437 parseParam("--aa Long flag. ") catch unreachable,
406 parseParam("--bb <V2> Long option. ") catch unreachable, 438 parseParam("--bb <V2> Long option. ") catch unreachable,
407 parseParam("-c, --cc Both flag. ") catch unreachable, 439 parseParam("-c, --cc Both flag. ") catch unreachable,
408 parseParam("-d, --dd <V3> Both option. ") catch unreachable, 440 parseParam("-d, --dd <V3> Both option. ") catch unreachable,
441 parseParam("-d, --dd <V3>... Both repeated option. ") catch unreachable,
409 Param(Help){ 442 Param(Help){
410 .id = Help{ 443 .id = Help{
411 .msg = "Positional. This should not appear in the help message.", 444 .msg = "Positional. This should not appear in the help message.",
412 }, 445 },
413 .takes_value = true, 446 .takes_value = .One,
414 }, 447 },
415 }, 448 },
416 ); 449 );
417 450
418 const expected = "" ++ 451 const expected = "" ++
419 "\t-a \tShort flag.\n" ++ 452 "\t-a \tShort flag.\n" ++
420 "\t-b <V1> \tShort option.\n" ++ 453 "\t-b <V1> \tShort option.\n" ++
421 "\t --aa \tLong flag.\n" ++ 454 "\t --aa \tLong flag.\n" ++
422 "\t --bb <V2>\tLong option.\n" ++ 455 "\t --bb <V2> \tLong option.\n" ++
423 "\t-c, --cc \tBoth flag.\n" ++ 456 "\t-c, --cc \tBoth flag.\n" ++
424 "\t-d, --dd <V3>\tBoth option.\n"; 457 "\t-d, --dd <V3> \tBoth option.\n" ++
458 "\t-d, --dd <V3>...\tBoth repeated option.\n";
425 459
426 const actual = slice_stream.getWritten(); 460 const actual = slice_stream.getWritten();
427 if (!mem.eql(u8, actual, expected)) { 461 if (!mem.eql(u8, actual, expected)) {
@@ -458,7 +492,7 @@ pub fn usageFull(
458 const cs = cos.outStream(); 492 const cs = cos.outStream();
459 for (params) |param| { 493 for (params) |param| {
460 const name = param.names.short orelse continue; 494 const name = param.names.short orelse continue;
461 if (param.takes_value) 495 if (param.takes_value != .None)
462 continue; 496 continue;
463 497
464 if (cos.bytes_written == 0) 498 if (cos.bytes_written == 0)
@@ -470,7 +504,7 @@ pub fn usageFull(
470 504
471 var positional: ?Param(Id) = null; 505 var positional: ?Param(Id) = null;
472 for (params) |param| { 506 for (params) |param| {
473 if (!param.takes_value and param.names.short != null) 507 if (param.takes_value == .None and param.names.short != null)
474 continue; 508 continue;
475 509
476 const prefix = if (param.names.short) |_| "-" else "--"; 510 const prefix = if (param.names.short) |_| "-" else "--";
@@ -485,8 +519,11 @@ pub fn usageFull(
485 try cs.writeByte(' '); 519 try cs.writeByte(' ');
486 520
487 try cs.print("[{}{}", .{ prefix, name }); 521 try cs.print("[{}{}", .{ prefix, name });
488 if (param.takes_value) 522 switch (param.takes_value) {
489 try cs.print(" <{}>", .{try valueText(context, param)}); 523 .None => {},
524 .One => try cs.print(" <{}>", .{try valueText(context, param)}),
525 .Many => try cs.print(" <{}>...", .{try valueText(context, param)}),
526 }
490 527
491 try cs.writeByte(']'); 528 try cs.writeByte(']');
492 } 529 }
@@ -575,10 +612,10 @@ test "usage" {
575 .id = Help{ 612 .id = Help{
576 .value = "file", 613 .value = "file",
577 }, 614 },
578 .takes_value = true, 615 .takes_value = .One,
579 }, 616 },
580 }); 617 });
581 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] <file>", comptime &[_]Param(Help){ 618 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", comptime &[_]Param(Help){
582 parseParam("-a") catch unreachable, 619 parseParam("-a") catch unreachable,
583 parseParam("-b") catch unreachable, 620 parseParam("-b") catch unreachable,
584 parseParam("-c <value>") catch unreachable, 621 parseParam("-c <value>") catch unreachable,
@@ -587,11 +624,12 @@ test "usage" {
587 parseParam("--f") catch unreachable, 624 parseParam("--f") catch unreachable,
588 parseParam("--g <value>") catch unreachable, 625 parseParam("--g <value>") catch unreachable,
589 parseParam("--h <v>") catch unreachable, 626 parseParam("--h <v>") catch unreachable,
627 parseParam("-i <v>...") catch unreachable,
590 Param(Help){ 628 Param(Help){
591 .id = Help{ 629 .id = Help{
592 .value = "file", 630 .value = "file",
593 }, 631 },
594 .takes_value = true, 632 .takes_value = .One,
595 }, 633 },
596 }); 634 });
597} 635}