diff options
| author | 2020-03-05 23:24:36 +0100 | |
|---|---|---|
| committer | 2020-03-05 23:24:36 +0100 | |
| commit | cc056cf4232721b62b76428d1ada447eecd98421 (patch) | |
| tree | 56e859d0f91b372c50d6b35818877a4476ded5a1 /clap.zig | |
| parent | Add clap.parse as the simplest way of using the lib (diff) | |
| download | zig-clap-cc056cf4232721b62b76428d1ada447eecd98421.tar.gz zig-clap-cc056cf4232721b62b76428d1ada447eecd98421.tar.xz zig-clap-cc056cf4232721b62b76428d1ada447eecd98421.zip | |
Add clap.usage
Diffstat (limited to 'clap.zig')
| -rw-r--r-- | clap.zig | 193 |
1 files changed, 172 insertions, 21 deletions
| @@ -265,25 +265,25 @@ pub fn parse( | |||
| 265 | } | 265 | } |
| 266 | 266 | ||
| 267 | /// Will print a help message in the following format: | 267 | /// Will print a help message in the following format: |
| 268 | /// -s, --long <value_text> help_text | 268 | /// -s, --long <valueText> helpText |
| 269 | /// -s, help_text | 269 | /// -s, helpText |
| 270 | /// -s <value_text> help_text | 270 | /// -s <valueText> helpText |
| 271 | /// --long help_text | 271 | /// --long helpText |
| 272 | /// --long <value_text> help_text | 272 | /// --long <valueText> helpText |
| 273 | pub fn helpFull( | 273 | pub fn helpFull( |
| 274 | stream: var, | 274 | stream: var, |
| 275 | comptime Id: type, | 275 | comptime Id: type, |
| 276 | params: []const Param(Id), | 276 | params: []const Param(Id), |
| 277 | comptime Error: type, | 277 | comptime Error: type, |
| 278 | context: var, | 278 | context: var, |
| 279 | help_text: fn (@typeOf(context), Param(Id)) Error![]const u8, | 279 | helpText: fn (@typeOf(context), Param(Id)) Error![]const u8, |
| 280 | value_text: fn (@typeOf(context), Param(Id)) Error![]const u8, | 280 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, |
| 281 | ) !void { | 281 | ) !void { |
| 282 | const max_spacing = blk: { | 282 | const max_spacing = blk: { |
| 283 | var res: usize = 0; | 283 | var res: usize = 0; |
| 284 | for (params) |param| { | 284 | for (params) |param| { |
| 285 | var counting_stream = io.CountingOutStream(io.NullOutStream.Error).init(io.null_out_stream); | 285 | var counting_stream = io.CountingOutStream(io.NullOutStream.Error).init(io.null_out_stream); |
| 286 | try printParam(&counting_stream.stream, Id, param, Error, context, value_text); | 286 | try printParam(&counting_stream.stream, Id, param, Error, context, valueText); |
| 287 | if (res < counting_stream.bytes_written) | 287 | if (res < counting_stream.bytes_written) |
| 288 | res = counting_stream.bytes_written; | 288 | res = counting_stream.bytes_written; |
| 289 | } | 289 | } |
| @@ -297,9 +297,9 @@ pub fn helpFull( | |||
| 297 | 297 | ||
| 298 | var counting_stream = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); | 298 | var counting_stream = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); |
| 299 | try stream.print("\t"); | 299 | try stream.print("\t"); |
| 300 | try printParam(&counting_stream.stream, Id, param, Error, context, value_text); | 300 | try printParam(&counting_stream.stream, Id, param, Error, context, valueText); |
| 301 | try stream.writeByteNTimes(' ', max_spacing - counting_stream.bytes_written); | 301 | try stream.writeByteNTimes(' ', max_spacing - counting_stream.bytes_written); |
| 302 | try stream.print("\t{}\n", try help_text(context, param)); | 302 | try stream.print("\t{}\n", try helpText(context, param)); |
| 303 | } | 303 | } |
| 304 | } | 304 | } |
| 305 | 305 | ||
| @@ -309,7 +309,7 @@ fn printParam( | |||
| 309 | param: Param(Id), | 309 | param: Param(Id), |
| 310 | comptime Error: type, | 310 | comptime Error: type, |
| 311 | context: var, | 311 | context: var, |
| 312 | value_text: fn (@typeOf(context), Param(Id)) Error![]const u8, | 312 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, |
| 313 | ) @typeOf(stream.*).Error!void { | 313 | ) @typeOf(stream.*).Error!void { |
| 314 | if (param.names.short) |s| { | 314 | if (param.names.short) |s| { |
| 315 | try stream.print("-{c}", s); | 315 | try stream.print("-{c}", s); |
| @@ -326,28 +326,28 @@ fn printParam( | |||
| 326 | try stream.print("--{}", l); | 326 | try stream.print("--{}", l); |
| 327 | } | 327 | } |
| 328 | if (param.takes_value) | 328 | if (param.takes_value) |
| 329 | try stream.print(" <{}>", value_text(context, param)); | 329 | try stream.print(" <{}>", valueText(context, param)); |
| 330 | } | 330 | } |
| 331 | 331 | ||
| 332 | /// A wrapper around helpFull for simple help_text and value_text functions that | 332 | /// A wrapper around helpFull for simple helpText and valueText functions that |
| 333 | /// cant return an error or take a context. | 333 | /// cant return an error or take a context. |
| 334 | pub fn helpEx( | 334 | pub fn helpEx( |
| 335 | stream: var, | 335 | stream: var, |
| 336 | comptime Id: type, | 336 | comptime Id: type, |
| 337 | params: []const Param(Id), | 337 | params: []const Param(Id), |
| 338 | help_text: fn (Param(Id)) []const u8, | 338 | helpText: fn (Param(Id)) []const u8, |
| 339 | value_text: fn (Param(Id)) []const u8, | 339 | valueText: fn (Param(Id)) []const u8, |
| 340 | ) !void { | 340 | ) !void { |
| 341 | const Context = struct { | 341 | const Context = struct { |
| 342 | help_text: fn (Param(Id)) []const u8, | 342 | helpText: fn (Param(Id)) []const u8, |
| 343 | value_text: fn (Param(Id)) []const u8, | 343 | valueText: fn (Param(Id)) []const u8, |
| 344 | 344 | ||
| 345 | pub fn help(c: @This(), p: Param(Id)) error{}![]const u8 { | 345 | pub fn help(c: @This(), p: Param(Id)) error{}![]const u8 { |
| 346 | return c.help_text(p); | 346 | return c.helpText(p); |
| 347 | } | 347 | } |
| 348 | 348 | ||
| 349 | pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 { | 349 | pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 { |
| 350 | return c.value_text(p); | 350 | return c.valueText(p); |
| 351 | } | 351 | } |
| 352 | }; | 352 | }; |
| 353 | 353 | ||
| @@ -357,8 +357,8 @@ pub fn helpEx( | |||
| 357 | params, | 357 | params, |
| 358 | error{}, | 358 | error{}, |
| 359 | Context{ | 359 | Context{ |
| 360 | .help_text = help_text, | 360 | .helpText = helpText, |
| 361 | .value_text = value_text, | 361 | .valueText = valueText, |
| 362 | }, | 362 | }, |
| 363 | Context.help, | 363 | Context.help, |
| 364 | Context.value, | 364 | Context.value, |
| @@ -429,3 +429,154 @@ test "clap.help" { | |||
| 429 | testing.expect(false); | 429 | testing.expect(false); |
| 430 | } | 430 | } |
| 431 | } | 431 | } |
| 432 | |||
| 433 | /// Will print a usage message in the following format: | ||
| 434 | /// [-abc] [--longa] [-d <valueText>] [--longb <valueText>] <valueText> | ||
| 435 | /// | ||
| 436 | /// First all none value taking parameters, which have a short name are | ||
| 437 | /// printed, then non positional parameters and finally the positinal. | ||
| 438 | pub fn usageFull( | ||
| 439 | stream: var, | ||
| 440 | comptime Id: type, | ||
| 441 | params: []const Param(Id), | ||
| 442 | comptime Error: type, | ||
| 443 | context: var, | ||
| 444 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, | ||
| 445 | ) !void { | ||
| 446 | var cs = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); | ||
| 447 | for (params) |param| { | ||
| 448 | const name = param.names.short orelse continue; | ||
| 449 | if (param.takes_value) | ||
| 450 | continue; | ||
| 451 | |||
| 452 | if (cs.bytes_written == 0) | ||
| 453 | try stream.write("[-"); | ||
| 454 | try cs.stream.write([_]u8{name}); | ||
| 455 | } | ||
| 456 | if (cs.bytes_written != 0) | ||
| 457 | try cs.stream.write("]"); | ||
| 458 | |||
| 459 | var positional: ?Param(Id) = null; | ||
| 460 | for (params) |param| { | ||
| 461 | if (!param.takes_value and param.names.short != null) | ||
| 462 | continue; | ||
| 463 | |||
| 464 | const prefix = if (param.names.short) |_| "-" else "--"; | ||
| 465 | const name = if (param.names.short) |*s| (*const [1]u8)(s)[0..] else param.names.long orelse { | ||
| 466 | positional = param; | ||
| 467 | continue; | ||
| 468 | }; | ||
| 469 | if (cs.bytes_written != 0) | ||
| 470 | try cs.stream.write(" "); | ||
| 471 | |||
| 472 | try cs.stream.print("[{}{}", prefix, name); | ||
| 473 | if (param.takes_value) | ||
| 474 | try cs.stream.print(" <{}>", try valueText(context, param)); | ||
| 475 | |||
| 476 | try cs.stream.write("]"); | ||
| 477 | } | ||
| 478 | |||
| 479 | if (positional) |p| { | ||
| 480 | if (cs.bytes_written != 0) | ||
| 481 | try cs.stream.write(" "); | ||
| 482 | try cs.stream.print("<{}>", try valueText(context, p)); | ||
| 483 | } | ||
| 484 | } | ||
| 485 | |||
| 486 | /// A wrapper around usageFull for a simple valueText functions that | ||
| 487 | /// cant return an error or take a context. | ||
| 488 | pub fn usageEx( | ||
| 489 | stream: var, | ||
| 490 | comptime Id: type, | ||
| 491 | params: []const Param(Id), | ||
| 492 | valueText: fn (Param(Id)) []const u8, | ||
| 493 | ) !void { | ||
| 494 | const Context = struct { | ||
| 495 | valueText: fn (Param(Id)) []const u8, | ||
| 496 | |||
| 497 | pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 { | ||
| 498 | return c.valueText(p); | ||
| 499 | } | ||
| 500 | }; | ||
| 501 | |||
| 502 | return usageFull( | ||
| 503 | stream, | ||
| 504 | Id, | ||
| 505 | params, | ||
| 506 | error{}, | ||
| 507 | Context{ .valueText = valueText }, | ||
| 508 | Context.value, | ||
| 509 | ); | ||
| 510 | } | ||
| 511 | |||
| 512 | /// A wrapper around usageEx that takes a Param(Help). | ||
| 513 | pub fn usage(stream: var, params: []const Param(Help)) !void { | ||
| 514 | try usageEx(stream, Help, params, getValueSimple); | ||
| 515 | } | ||
| 516 | |||
| 517 | fn testUsage(expected: []const u8, params: []const Param(Help)) !void { | ||
| 518 | var buf: [1024]u8 = undefined; | ||
| 519 | var slice_stream = io.SliceOutStream.init(buf[0..]); | ||
| 520 | try usage(&slice_stream.stream, params); | ||
| 521 | |||
| 522 | const actual = slice_stream.getWritten(); | ||
| 523 | if (!mem.eql(u8, actual, expected)) { | ||
| 524 | debug.warn("\n============ Expected ============\n"); | ||
| 525 | debug.warn("{}\n", expected); | ||
| 526 | debug.warn("============= Actual =============\n"); | ||
| 527 | debug.warn("{}\n", actual); | ||
| 528 | |||
| 529 | var buffer: [1024 * 2]u8 = undefined; | ||
| 530 | var fba = std.heap.FixedBufferAllocator.init(&buffer); | ||
| 531 | |||
| 532 | debug.warn("============ Expected (escaped) ============\n"); | ||
| 533 | debug.warn("{x}\n", expected); | ||
| 534 | debug.warn("============ Actual (escaped) ============\n"); | ||
| 535 | debug.warn("{x}\n", actual); | ||
| 536 | testing.expect(false); | ||
| 537 | } | ||
| 538 | } | ||
| 539 | |||
| 540 | test "usage" { | ||
| 541 | @setEvalBranchQuota(100000); | ||
| 542 | try testUsage("[-ab]", comptime [_]Param(Help){ | ||
| 543 | parseParam("-a") catch unreachable, | ||
| 544 | parseParam("-b") catch unreachable, | ||
| 545 | }); | ||
| 546 | try testUsage("[-a <value>] [-b <v>]", comptime [_]Param(Help){ | ||
| 547 | parseParam("-a <value>") catch unreachable, | ||
| 548 | parseParam("-b <v>") catch unreachable, | ||
| 549 | }); | ||
| 550 | try testUsage("[--a] [--b]", comptime [_]Param(Help){ | ||
| 551 | parseParam("--a") catch unreachable, | ||
| 552 | parseParam("--b") catch unreachable, | ||
| 553 | }); | ||
| 554 | try testUsage("[--a <value>] [--b <v>]", comptime [_]Param(Help){ | ||
| 555 | parseParam("--a <value>") catch unreachable, | ||
| 556 | parseParam("--b <v>") catch unreachable, | ||
| 557 | }); | ||
| 558 | try testUsage("<file>", comptime [_]Param(Help){ | ||
| 559 | Param(Help){ | ||
| 560 | .id = Help{ | ||
| 561 | .value = "file", | ||
| 562 | }, | ||
| 563 | .takes_value = true, | ||
| 564 | }, | ||
| 565 | }); | ||
| 566 | try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] <file>", comptime [_]Param(Help){ | ||
| 567 | parseParam("-a") catch unreachable, | ||
| 568 | parseParam("-b") catch unreachable, | ||
| 569 | parseParam("-c <value>") catch unreachable, | ||
| 570 | parseParam("-d <v>") catch unreachable, | ||
| 571 | parseParam("--e") catch unreachable, | ||
| 572 | parseParam("--f") catch unreachable, | ||
| 573 | parseParam("--g <value>") catch unreachable, | ||
| 574 | parseParam("--h <v>") catch unreachable, | ||
| 575 | Param(Help){ | ||
| 576 | .id = Help{ | ||
| 577 | .value = "file", | ||
| 578 | }, | ||
| 579 | .takes_value = true, | ||
| 580 | }, | ||
| 581 | }); | ||
| 582 | } | ||