summaryrefslogtreecommitdiff
path: root/clap.zig
diff options
context:
space:
mode:
Diffstat (limited to 'clap.zig')
-rw-r--r--clap.zig141
1 files changed, 76 insertions, 65 deletions
diff --git a/clap.zig b/clap.zig
index 40e76f4..ff423cc 100644
--- a/clap.zig
+++ b/clap.zig
@@ -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.
59pub fn parseParam(line: []const u8) !Param(Help) { 59pub 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
121test "parseParam" { 125test "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
386test "clap.help" { 391test "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
517fn testUsage(expected: []const u8, params: []const Param(Help)) !void { 528fn 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
540test "usage" { 551test "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,