diff options
Diffstat (limited to 'clap.zig')
| -rw-r--r-- | clap.zig | 141 |
1 files changed, 76 insertions, 65 deletions
| @@ -57,10 +57,14 @@ pub fn Param(comptime Id: type) type { | |||
| 57 | /// Takes a string and parses it to a Param(Help). | 57 | /// Takes a string and parses it to a Param(Help). |
| 58 | /// This is the reverse of 'help' but for at single parameter only. | 58 | /// This is the reverse of 'help' but for at single parameter only. |
| 59 | pub fn parseParam(line: []const u8) !Param(Help) { | 59 | pub fn parseParam(line: []const u8) !Param(Help) { |
| 60 | var z: usize = 0; | ||
| 60 | var res = Param(Help){ | 61 | var res = Param(Help){ |
| 61 | .id = Help{ | 62 | .id = Help{ |
| 62 | .msg = line[0..0], | 63 | // For testing, i want to be able to easily compare slices just by pointer, |
| 63 | .value = line[0..0], | 64 | // so I slice by a runtime value here, so that zig does not optimize this |
| 65 | // out. Maybe I should write the test better, geeh. | ||
| 66 | .msg = line[z..z], | ||
| 67 | .value = line[z..z], | ||
| 64 | }, | 68 | }, |
| 65 | }; | 69 | }; |
| 66 | 70 | ||
| @@ -119,6 +123,7 @@ pub fn parseParam(line: []const u8) !Param(Help) { | |||
| 119 | } | 123 | } |
| 120 | 124 | ||
| 121 | test "parseParam" { | 125 | test "parseParam" { |
| 126 | var z: usize = 0; | ||
| 122 | var text: []const u8 = "-s, --long <value> Help text"; | 127 | var text: []const u8 = "-s, --long <value> Help text"; |
| 123 | testing.expectEqual(Param(Help){ | 128 | testing.expectEqual(Param(Help){ |
| 124 | .id = Help{ | 129 | .id = Help{ |
| @@ -162,7 +167,7 @@ test "parseParam" { | |||
| 162 | testing.expectEqual(Param(Help){ | 167 | testing.expectEqual(Param(Help){ |
| 163 | .id = Help{ | 168 | .id = Help{ |
| 164 | .msg = find(text, "Help text"), | 169 | .msg = find(text, "Help text"), |
| 165 | .value = text[0..0], | 170 | .value = text[z..z], |
| 166 | }, | 171 | }, |
| 167 | .names = Names{ | 172 | .names = Names{ |
| 168 | .short = 's', | 173 | .short = 's', |
| @@ -175,7 +180,7 @@ test "parseParam" { | |||
| 175 | testing.expectEqual(Param(Help){ | 180 | testing.expectEqual(Param(Help){ |
| 176 | .id = Help{ | 181 | .id = Help{ |
| 177 | .msg = find(text, "Help text"), | 182 | .msg = find(text, "Help text"), |
| 178 | .value = text[0..0], | 183 | .value = text[z..z], |
| 179 | }, | 184 | }, |
| 180 | .names = Names{ | 185 | .names = Names{ |
| 181 | .short = 's', | 186 | .short = 's', |
| @@ -188,7 +193,7 @@ test "parseParam" { | |||
| 188 | testing.expectEqual(Param(Help){ | 193 | testing.expectEqual(Param(Help){ |
| 189 | .id = Help{ | 194 | .id = Help{ |
| 190 | .msg = find(text, "Help text"), | 195 | .msg = find(text, "Help text"), |
| 191 | .value = text[0..0], | 196 | .value = text[z..z], |
| 192 | }, | 197 | }, |
| 193 | .names = Names{ | 198 | .names = Names{ |
| 194 | .short = null, | 199 | .short = null, |
| @@ -276,14 +281,14 @@ pub fn helpFull( | |||
| 276 | params: []const Param(Id), | 281 | params: []const Param(Id), |
| 277 | comptime Error: type, | 282 | comptime Error: type, |
| 278 | context: var, | 283 | context: var, |
| 279 | helpText: fn (@typeOf(context), Param(Id)) Error![]const u8, | 284 | helpText: fn (@TypeOf(context), Param(Id)) Error![]const u8, |
| 280 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, | 285 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, |
| 281 | ) !void { | 286 | ) !void { |
| 282 | const max_spacing = blk: { | 287 | const max_spacing = blk: { |
| 283 | var res: usize = 0; | 288 | var res: usize = 0; |
| 284 | for (params) |param| { | 289 | for (params) |param| { |
| 285 | var counting_stream = io.CountingOutStream(io.NullOutStream.Error).init(io.null_out_stream); | 290 | var counting_stream = io.countingOutStream(io.null_out_stream); |
| 286 | try printParam(&counting_stream.stream, Id, param, Error, context, valueText); | 291 | try printParam(counting_stream.outStream(), Id, param, Error, context, valueText); |
| 287 | if (res < counting_stream.bytes_written) | 292 | if (res < counting_stream.bytes_written) |
| 288 | res = counting_stream.bytes_written; | 293 | res = counting_stream.bytes_written; |
| 289 | } | 294 | } |
| @@ -295,11 +300,11 @@ pub fn helpFull( | |||
| 295 | if (param.names.short == null and param.names.long == null) | 300 | if (param.names.short == null and param.names.long == null) |
| 296 | continue; | 301 | continue; |
| 297 | 302 | ||
| 298 | var counting_stream = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); | 303 | var counting_stream = io.countingOutStream(stream); |
| 299 | try stream.print("\t"); | 304 | try stream.print("\t", .{}); |
| 300 | try printParam(&counting_stream.stream, Id, param, Error, context, valueText); | 305 | try printParam(counting_stream.outStream(), Id, param, Error, context, valueText); |
| 301 | try stream.writeByteNTimes(' ', max_spacing - counting_stream.bytes_written); | 306 | try stream.writeByteNTimes(' ', max_spacing - counting_stream.bytes_written); |
| 302 | try stream.print("\t{}\n", try helpText(context, param)); | 307 | try stream.print("\t{}\n", .{try helpText(context, param)}); |
| 303 | } | 308 | } |
| 304 | } | 309 | } |
| 305 | 310 | ||
| @@ -309,24 +314,24 @@ fn printParam( | |||
| 309 | param: Param(Id), | 314 | param: Param(Id), |
| 310 | comptime Error: type, | 315 | comptime Error: type, |
| 311 | context: var, | 316 | context: var, |
| 312 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, | 317 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, |
| 313 | ) @typeOf(stream.*).Error!void { | 318 | ) !void { |
| 314 | if (param.names.short) |s| { | 319 | if (param.names.short) |s| { |
| 315 | try stream.print("-{c}", s); | 320 | try stream.print("-{c}", .{s}); |
| 316 | } else { | 321 | } else { |
| 317 | try stream.print(" "); | 322 | try stream.print(" ", .{}); |
| 318 | } | 323 | } |
| 319 | if (param.names.long) |l| { | 324 | if (param.names.long) |l| { |
| 320 | if (param.names.short) |_| { | 325 | if (param.names.short) |_| { |
| 321 | try stream.print(", "); | 326 | try stream.print(", ", .{}); |
| 322 | } else { | 327 | } else { |
| 323 | try stream.print(" "); | 328 | try stream.print(" ", .{}); |
| 324 | } | 329 | } |
| 325 | 330 | ||
| 326 | try stream.print("--{}", l); | 331 | try stream.print("--{}", .{l}); |
| 327 | } | 332 | } |
| 328 | if (param.takes_value) | 333 | if (param.takes_value) |
| 329 | try stream.print(" <{}>", valueText(context, param)); | 334 | try stream.print(" <{}>", .{valueText(context, param)}); |
| 330 | } | 335 | } |
| 331 | 336 | ||
| 332 | /// A wrapper around helpFull for simple helpText and valueText functions that | 337 | /// A wrapper around helpFull for simple helpText and valueText functions that |
| @@ -385,10 +390,12 @@ fn getValueSimple(param: Param(Help)) []const u8 { | |||
| 385 | 390 | ||
| 386 | test "clap.help" { | 391 | test "clap.help" { |
| 387 | var buf: [1024]u8 = undefined; | 392 | var buf: [1024]u8 = undefined; |
| 388 | var slice_stream = io.SliceOutStream.init(buf[0..]); | 393 | var slice_stream = io.fixedBufferStream(&buf); |
| 394 | |||
| 395 | @setEvalBranchQuota(10000); | ||
| 389 | try help( | 396 | try help( |
| 390 | &slice_stream.stream, | 397 | slice_stream.outStream(), |
| 391 | comptime [_]Param(Help){ | 398 | comptime &[_]Param(Help){ |
| 392 | parseParam("-a Short flag. ") catch unreachable, | 399 | parseParam("-a Short flag. ") catch unreachable, |
| 393 | parseParam("-b <V1> Short option.") catch unreachable, | 400 | parseParam("-b <V1> Short option.") catch unreachable, |
| 394 | parseParam("--aa Long flag. ") catch unreachable, | 401 | parseParam("--aa Long flag. ") catch unreachable, |
| @@ -414,18 +421,18 @@ test "clap.help" { | |||
| 414 | 421 | ||
| 415 | const actual = slice_stream.getWritten(); | 422 | const actual = slice_stream.getWritten(); |
| 416 | if (!mem.eql(u8, actual, expected)) { | 423 | if (!mem.eql(u8, actual, expected)) { |
| 417 | debug.warn("\n============ Expected ============\n"); | 424 | debug.warn("\n============ Expected ============\n", .{}); |
| 418 | debug.warn("{}", expected); | 425 | debug.warn("{}", .{expected}); |
| 419 | debug.warn("============= Actual =============\n"); | 426 | debug.warn("============= Actual =============\n", .{}); |
| 420 | debug.warn("{}", actual); | 427 | debug.warn("{}", .{actual}); |
| 421 | 428 | ||
| 422 | var buffer: [1024 * 2]u8 = undefined; | 429 | var buffer: [1024 * 2]u8 = undefined; |
| 423 | var fba = std.heap.FixedBufferAllocator.init(&buffer); | 430 | var fba = std.heap.FixedBufferAllocator.init(&buffer); |
| 424 | 431 | ||
| 425 | debug.warn("============ Expected (escaped) ============\n"); | 432 | debug.warn("============ Expected (escaped) ============\n", .{}); |
| 426 | debug.warn("{x}\n", expected); | 433 | debug.warn("{x}\n", .{expected}); |
| 427 | debug.warn("============ Actual (escaped) ============\n"); | 434 | debug.warn("============ Actual (escaped) ============\n", .{}); |
| 428 | debug.warn("{x}\n", actual); | 435 | debug.warn("{x}\n", .{actual}); |
| 429 | testing.expect(false); | 436 | testing.expect(false); |
| 430 | } | 437 | } |
| 431 | } | 438 | } |
| @@ -441,20 +448,21 @@ pub fn usageFull( | |||
| 441 | params: []const Param(Id), | 448 | params: []const Param(Id), |
| 442 | comptime Error: type, | 449 | comptime Error: type, |
| 443 | context: var, | 450 | context: var, |
| 444 | valueText: fn (@typeOf(context), Param(Id)) Error![]const u8, | 451 | valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, |
| 445 | ) !void { | 452 | ) !void { |
| 446 | var cs = io.CountingOutStream(@typeOf(stream.*).Error).init(stream); | 453 | var cos = io.countingOutStream(stream); |
| 454 | const cs = cos.outStream(); | ||
| 447 | for (params) |param| { | 455 | for (params) |param| { |
| 448 | const name = param.names.short orelse continue; | 456 | const name = param.names.short orelse continue; |
| 449 | if (param.takes_value) | 457 | if (param.takes_value) |
| 450 | continue; | 458 | continue; |
| 451 | 459 | ||
| 452 | if (cs.bytes_written == 0) | 460 | if (cos.bytes_written == 0) |
| 453 | try stream.write("[-"); | 461 | try stream.writeAll("[-"); |
| 454 | try cs.stream.write([_]u8{name}); | 462 | try cs.writeByte(name); |
| 455 | } | 463 | } |
| 456 | if (cs.bytes_written != 0) | 464 | if (cos.bytes_written != 0) |
| 457 | try cs.stream.write("]"); | 465 | try cs.writeByte(']'); |
| 458 | 466 | ||
| 459 | var positional: ?Param(Id) = null; | 467 | var positional: ?Param(Id) = null; |
| 460 | for (params) |param| { | 468 | for (params) |param| { |
| @@ -462,24 +470,27 @@ pub fn usageFull( | |||
| 462 | continue; | 470 | continue; |
| 463 | 471 | ||
| 464 | const prefix = if (param.names.short) |_| "-" else "--"; | 472 | 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 { | 473 | |
| 474 | // Seems the zig compiler is being a little wierd. I doesn't allow me to write | ||
| 475 | // @as(*const [1]u8, s) VVVVVVVVVVVVVVVVVVVVVVVVVVVVVV | ||
| 476 | const name = if (param.names.short) |*s| @ptrCast([*]const u8, s)[0..1] else param.names.long orelse { | ||
| 466 | positional = param; | 477 | positional = param; |
| 467 | continue; | 478 | continue; |
| 468 | }; | 479 | }; |
| 469 | if (cs.bytes_written != 0) | 480 | if (cos.bytes_written != 0) |
| 470 | try cs.stream.write(" "); | 481 | try cs.writeByte(' '); |
| 471 | 482 | ||
| 472 | try cs.stream.print("[{}{}", prefix, name); | 483 | try cs.print("[{}{}", .{ prefix, name }); |
| 473 | if (param.takes_value) | 484 | if (param.takes_value) |
| 474 | try cs.stream.print(" <{}>", try valueText(context, param)); | 485 | try cs.print(" <{}>", .{try valueText(context, param)}); |
| 475 | 486 | ||
| 476 | try cs.stream.write("]"); | 487 | try cs.writeByte(']'); |
| 477 | } | 488 | } |
| 478 | 489 | ||
| 479 | if (positional) |p| { | 490 | if (positional) |p| { |
| 480 | if (cs.bytes_written != 0) | 491 | if (cos.bytes_written != 0) |
| 481 | try cs.stream.write(" "); | 492 | try cs.writeByte(' '); |
| 482 | try cs.stream.print("<{}>", try valueText(context, p)); | 493 | try cs.print("<{}>", .{try valueText(context, p)}); |
| 483 | } | 494 | } |
| 484 | } | 495 | } |
| 485 | 496 | ||
| @@ -516,46 +527,46 @@ pub fn usage(stream: var, params: []const Param(Help)) !void { | |||
| 516 | 527 | ||
| 517 | fn testUsage(expected: []const u8, params: []const Param(Help)) !void { | 528 | fn testUsage(expected: []const u8, params: []const Param(Help)) !void { |
| 518 | var buf: [1024]u8 = undefined; | 529 | var buf: [1024]u8 = undefined; |
| 519 | var slice_stream = io.SliceOutStream.init(buf[0..]); | 530 | var fbs = io.fixedBufferStream(&buf); |
| 520 | try usage(&slice_stream.stream, params); | 531 | try usage(fbs.outStream(), params); |
| 521 | 532 | ||
| 522 | const actual = slice_stream.getWritten(); | 533 | const actual = fbs.getWritten(); |
| 523 | if (!mem.eql(u8, actual, expected)) { | 534 | if (!mem.eql(u8, actual, expected)) { |
| 524 | debug.warn("\n============ Expected ============\n"); | 535 | debug.warn("\n============ Expected ============\n", .{}); |
| 525 | debug.warn("{}\n", expected); | 536 | debug.warn("{}\n", .{expected}); |
| 526 | debug.warn("============= Actual =============\n"); | 537 | debug.warn("============= Actual =============\n", .{}); |
| 527 | debug.warn("{}\n", actual); | 538 | debug.warn("{}\n", .{actual}); |
| 528 | 539 | ||
| 529 | var buffer: [1024 * 2]u8 = undefined; | 540 | var buffer: [1024 * 2]u8 = undefined; |
| 530 | var fba = std.heap.FixedBufferAllocator.init(&buffer); | 541 | var fba = std.heap.FixedBufferAllocator.init(&buffer); |
| 531 | 542 | ||
| 532 | debug.warn("============ Expected (escaped) ============\n"); | 543 | debug.warn("============ Expected (escaped) ============\n", .{}); |
| 533 | debug.warn("{x}\n", expected); | 544 | debug.warn("{x}\n", .{expected}); |
| 534 | debug.warn("============ Actual (escaped) ============\n"); | 545 | debug.warn("============ Actual (escaped) ============\n", .{}); |
| 535 | debug.warn("{x}\n", actual); | 546 | debug.warn("{x}\n", .{actual}); |
| 536 | testing.expect(false); | 547 | testing.expect(false); |
| 537 | } | 548 | } |
| 538 | } | 549 | } |
| 539 | 550 | ||
| 540 | test "usage" { | 551 | test "usage" { |
| 541 | @setEvalBranchQuota(100000); | 552 | @setEvalBranchQuota(100000); |
| 542 | try testUsage("[-ab]", comptime [_]Param(Help){ | 553 | try testUsage("[-ab]", comptime &[_]Param(Help){ |
| 543 | parseParam("-a") catch unreachable, | 554 | parseParam("-a") catch unreachable, |
| 544 | parseParam("-b") catch unreachable, | 555 | parseParam("-b") catch unreachable, |
| 545 | }); | 556 | }); |
| 546 | try testUsage("[-a <value>] [-b <v>]", comptime [_]Param(Help){ | 557 | try testUsage("[-a <value>] [-b <v>]", comptime &[_]Param(Help){ |
| 547 | parseParam("-a <value>") catch unreachable, | 558 | parseParam("-a <value>") catch unreachable, |
| 548 | parseParam("-b <v>") catch unreachable, | 559 | parseParam("-b <v>") catch unreachable, |
| 549 | }); | 560 | }); |
| 550 | try testUsage("[--a] [--b]", comptime [_]Param(Help){ | 561 | try testUsage("[--a] [--b]", comptime &[_]Param(Help){ |
| 551 | parseParam("--a") catch unreachable, | 562 | parseParam("--a") catch unreachable, |
| 552 | parseParam("--b") catch unreachable, | 563 | parseParam("--b") catch unreachable, |
| 553 | }); | 564 | }); |
| 554 | try testUsage("[--a <value>] [--b <v>]", comptime [_]Param(Help){ | 565 | try testUsage("[--a <value>] [--b <v>]", comptime &[_]Param(Help){ |
| 555 | parseParam("--a <value>") catch unreachable, | 566 | parseParam("--a <value>") catch unreachable, |
| 556 | parseParam("--b <v>") catch unreachable, | 567 | parseParam("--b <v>") catch unreachable, |
| 557 | }); | 568 | }); |
| 558 | try testUsage("<file>", comptime [_]Param(Help){ | 569 | try testUsage("<file>", comptime &[_]Param(Help){ |
| 559 | Param(Help){ | 570 | Param(Help){ |
| 560 | .id = Help{ | 571 | .id = Help{ |
| 561 | .value = "file", | 572 | .value = "file", |
| @@ -563,7 +574,7 @@ test "usage" { | |||
| 563 | .takes_value = true, | 574 | .takes_value = true, |
| 564 | }, | 575 | }, |
| 565 | }); | 576 | }); |
| 566 | try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] <file>", comptime [_]Param(Help){ | 577 | try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] <file>", comptime &[_]Param(Help){ |
| 567 | parseParam("-a") catch unreachable, | 578 | parseParam("-a") catch unreachable, |
| 568 | parseParam("-b") catch unreachable, | 579 | parseParam("-b") catch unreachable, |
| 569 | parseParam("-c <value>") catch unreachable, | 580 | parseParam("-c <value>") catch unreachable, |