diff options
Diffstat (limited to 'clap.zig')
| -rw-r--r-- | clap.zig | 106 |
1 files changed, 72 insertions, 34 deletions
| @@ -25,6 +25,12 @@ pub const Names = struct { | |||
| 25 | long: ?[]const u8 = null, | 25 | long: ?[]const u8 = null, |
| 26 | }; | 26 | }; |
| 27 | 27 | ||
| 28 | pub 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 | } |