summaryrefslogtreecommitdiff
path: root/clap.zig
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--clap.zig211
1 files changed, 71 insertions, 140 deletions
diff --git a/clap.zig b/clap.zig
index b31cd1d..49d0670 100644
--- a/clap.zig
+++ b/clap.zig
@@ -26,9 +26,9 @@ pub const Names = struct {
26 26
27/// Whether a param takes no value (a flag), one value, or can be specified multiple times. 27/// Whether a param takes no value (a flag), one value, or can be specified multiple times.
28pub const Values = enum { 28pub const Values = enum {
29 None, 29 none,
30 One, 30 one,
31 Many, 31 many,
32}; 32};
33 33
34/// Represents a parameter for the command line. 34/// Represents a parameter for the command line.
@@ -56,7 +56,7 @@ pub fn Param(comptime Id: type) type {
56 return struct { 56 return struct {
57 id: Id = Id{}, 57 id: Id = Id{},
58 names: Names = Names{}, 58 names: Names = Names{},
59 takes_value: Values = .None, 59 takes_value: Values = .none,
60 }; 60 };
61} 61}
62 62
@@ -110,8 +110,8 @@ fn parseParamRest(line: []const u8) Param(Help) {
110 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk; 110 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk;
111 const takes_many = mem.startsWith(u8, line[len + 1 ..], "..."); 111 const takes_many = mem.startsWith(u8, line[len + 1 ..], "...");
112 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many); 112 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many);
113 return Param(Help){ 113 return .{
114 .takes_value = if (takes_many) .Many else .One, 114 .takes_value = if (takes_many) .many else .one,
115 .id = .{ 115 .id = .{
116 .msg = mem.trim(u8, line[help_start..], " \t"), 116 .msg = mem.trim(u8, line[help_start..], " \t"),
117 .value = line[1..len], 117 .value = line[1..len],
@@ -119,7 +119,7 @@ fn parseParamRest(line: []const u8) Param(Help) {
119 }; 119 };
120 } 120 }
121 121
122 return Param(Help){ .id = .{ .msg = mem.trim(u8, line, " \t") } }; 122 return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } };
123} 123}
124 124
125fn expectParam(expect: Param(Help), actual: Param(Help)) void { 125fn expectParam(expect: Param(Help), actual: Param(Help)) void {
@@ -136,114 +136,60 @@ fn expectParam(expect: Param(Help), actual: Param(Help)) void {
136 136
137test "parseParam" { 137test "parseParam" {
138 expectParam(Param(Help){ 138 expectParam(Param(Help){
139 .id = Help{ 139 .id = .{ .msg = "Help text", .value = "value" },
140 .msg = "Help text", 140 .names = .{ .short = 's', .long = "long" },
141 .value = "value", 141 .takes_value = .one,
142 },
143 .names = Names{
144 .short = 's',
145 .long = "long",
146 },
147 .takes_value = .One,
148 }, try parseParam("-s, --long <value> Help text")); 142 }, try parseParam("-s, --long <value> Help text"));
143
149 expectParam(Param(Help){ 144 expectParam(Param(Help){
150 .id = Help{ 145 .id = .{ .msg = "Help text", .value = "value" },
151 .msg = "Help text", 146 .names = .{ .short = 's', .long = "long" },
152 .value = "value", 147 .takes_value = .many,
153 },
154 .names = Names{
155 .short = 's',
156 .long = "long",
157 },
158 .takes_value = .Many,
159 }, try parseParam("-s, --long <value>... Help text")); 148 }, try parseParam("-s, --long <value>... Help text"));
149
160 expectParam(Param(Help){ 150 expectParam(Param(Help){
161 .id = Help{ 151 .id = .{ .msg = "Help text", .value = "value" },
162 .msg = "Help text", 152 .names = .{ .long = "long" },
163 .value = "value", 153 .takes_value = .one,
164 },
165 .names = Names{
166 .short = null,
167 .long = "long",
168 },
169 .takes_value = .One,
170 }, try parseParam("--long <value> Help text")); 154 }, try parseParam("--long <value> Help text"));
155
171 expectParam(Param(Help){ 156 expectParam(Param(Help){
172 .id = Help{ 157 .id = .{ .msg = "Help text", .value = "value" },
173 .msg = "Help text", 158 .names = .{ .short = 's' },
174 .value = "value", 159 .takes_value = .one,
175 },
176 .names = Names{
177 .short = 's',
178 .long = null,
179 },
180 .takes_value = .One,
181 }, try parseParam("-s <value> Help text")); 160 }, try parseParam("-s <value> Help text"));
161
182 expectParam(Param(Help){ 162 expectParam(Param(Help){
183 .id = Help{ 163 .id = .{ .msg = "Help text" },
184 .msg = "Help text", 164 .names = .{ .short = 's', .long = "long" },
185 .value = "",
186 },
187 .names = Names{
188 .short = 's',
189 .long = "long",
190 },
191 .takes_value = .None,
192 }, try parseParam("-s, --long Help text")); 165 }, try parseParam("-s, --long Help text"));
166
193 expectParam(Param(Help){ 167 expectParam(Param(Help){
194 .id = Help{ 168 .id = .{ .msg = "Help text" },
195 .msg = "Help text", 169 .names = .{ .short = 's' },
196 .value = "",
197 },
198 .names = Names{
199 .short = 's',
200 .long = null,
201 },
202 .takes_value = .None,
203 }, try parseParam("-s Help text")); 170 }, try parseParam("-s Help text"));
171
204 expectParam(Param(Help){ 172 expectParam(Param(Help){
205 .id = Help{ 173 .id = .{ .msg = "Help text" },
206 .msg = "Help text", 174 .names = .{ .long = "long" },
207 .value = "",
208 },
209 .names = Names{
210 .short = null,
211 .long = "long",
212 },
213 .takes_value = .None,
214 }, try parseParam("--long Help text")); 175 }, try parseParam("--long Help text"));
176
215 expectParam(Param(Help){ 177 expectParam(Param(Help){
216 .id = Help{ 178 .id = .{ .msg = "Help text", .value = "A | B" },
217 .msg = "Help text", 179 .names = .{ .long = "long" },
218 .value = "A | B", 180 .takes_value = .one,
219 },
220 .names = Names{
221 .short = null,
222 .long = "long",
223 },
224 .takes_value = .One,
225 }, try parseParam("--long <A | B> Help text")); 181 }, try parseParam("--long <A | B> Help text"));
182
226 expectParam(Param(Help){ 183 expectParam(Param(Help){
227 .id = Help{ 184 .id = .{ .msg = "Help text", .value = "A" },
228 .msg = "Help text", 185 .names = .{},
229 .value = "A", 186 .takes_value = .one,
230 },
231 .names = Names{
232 .short = null,
233 .long = null,
234 },
235 .takes_value = .One,
236 }, try parseParam("<A> Help text")); 187 }, try parseParam("<A> Help text"));
188
237 expectParam(Param(Help){ 189 expectParam(Param(Help){
238 .id = Help{ 190 .id = .{ .msg = "Help text", .value = "A" },
239 .msg = "Help text", 191 .names = .{},
240 .value = "A", 192 .takes_value = .many,
241 },
242 .names = Names{
243 .short = null,
244 .long = null,
245 },
246 .takes_value = .Many,
247 }, try parseParam("<A>... Help text")); 193 }, try parseParam("<A>... Help text"));
248 194
249 testing.expectError(error.TrailingComma, parseParam("--long, Help")); 195 testing.expectError(error.TrailingComma, parseParam("--long, Help"));
@@ -284,7 +230,7 @@ pub const Diagnostic = struct {
284fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) void { 230fn testDiag(diag: Diagnostic, err: anyerror, expected: []const u8) void {
285 var buf: [1024]u8 = undefined; 231 var buf: [1024]u8 = undefined;
286 var slice_stream = io.fixedBufferStream(&buf); 232 var slice_stream = io.fixedBufferStream(&buf);
287 diag.report(slice_stream.outStream(), err) catch unreachable; 233 diag.report(slice_stream.writer(), err) catch unreachable;
288 testing.expectEqualStrings(expected, slice_stream.getWritten()); 234 testing.expectEqualStrings(expected, slice_stream.getWritten());
289} 235}
290 236
@@ -392,10 +338,10 @@ pub fn helpFull(
392 const max_spacing = blk: { 338 const max_spacing = blk: {
393 var res: usize = 0; 339 var res: usize = 0;
394 for (params) |param| { 340 for (params) |param| {
395 var counting_stream = io.countingOutStream(io.null_out_stream); 341 var cs = io.countingOutStream(io.null_out_stream);
396 try printParam(counting_stream.outStream(), Id, param, Error, context, valueText); 342 try printParam(cs.writer(), Id, param, Error, context, valueText);
397 if (res < counting_stream.bytes_written) 343 if (res < cs.bytes_written)
398 res = @intCast(usize, counting_stream.bytes_written); 344 res = @intCast(usize, cs.bytes_written);
399 } 345 }
400 346
401 break :blk res; 347 break :blk res;
@@ -405,10 +351,10 @@ pub fn helpFull(
405 if (param.names.short == null and param.names.long == null) 351 if (param.names.short == null and param.names.long == null)
406 continue; 352 continue;
407 353
408 var counting_stream = io.countingOutStream(stream); 354 var cs = io.countingOutStream(stream);
409 try stream.print("\t", .{}); 355 try stream.print("\t", .{});
410 try printParam(counting_stream.outStream(), Id, param, Error, context, valueText); 356 try printParam(cs.writer(), Id, param, Error, context, valueText);
411 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, counting_stream.bytes_written)); 357 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written));
412 try stream.print("\t{}\n", .{try helpText(context, param)}); 358 try stream.print("\t{}\n", .{try helpText(context, param)});
413 } 359 }
414} 360}
@@ -437,9 +383,9 @@ fn printParam(
437 } 383 }
438 384
439 switch (param.takes_value) { 385 switch (param.takes_value) {
440 .None => {}, 386 .none => {},
441 .One => try stream.print(" <{}>", .{valueText(context, param)}), 387 .one => try stream.print(" <{}>", .{valueText(context, param)}),
442 .Many => try stream.print(" <{}>...", .{valueText(context, param)}), 388 .many => try stream.print(" <{}>...", .{valueText(context, param)}),
443 } 389 }
444} 390}
445 391
@@ -503,21 +449,16 @@ test "clap.help" {
503 449
504 @setEvalBranchQuota(10000); 450 @setEvalBranchQuota(10000);
505 try help( 451 try help(
506 slice_stream.outStream(), 452 slice_stream.writer(),
507 comptime &[_]Param(Help){ 453 comptime &[_]Param(Help){
508 parseParam("-a Short flag. ") catch unreachable, 454 parseParam("-a Short flag.") catch unreachable,
509 parseParam("-b <V1> Short option.") catch unreachable, 455 parseParam("-b <V1> Short option.") catch unreachable,
510 parseParam("--aa Long flag. ") catch unreachable, 456 parseParam("--aa Long flag.") catch unreachable,
511 parseParam("--bb <V2> Long option. ") catch unreachable, 457 parseParam("--bb <V2> Long option.") catch unreachable,
512 parseParam("-c, --cc Both flag. ") catch unreachable, 458 parseParam("-c, --cc Both flag.") catch unreachable,
513 parseParam("-d, --dd <V3> Both option. ") catch unreachable, 459 parseParam("-d, --dd <V3> Both option.") catch unreachable,
514 parseParam("-d, --dd <V3>... Both repeated option. ") catch unreachable, 460 parseParam("-d, --dd <V3>... Both repeated option.") catch unreachable,
515 Param(Help){ 461 parseParam("<P> Positional. This should not appear in the help message.") catch unreachable,
516 .id = Help{
517 .msg = "Positional. This should not appear in the help message.",
518 },
519 .takes_value = .One,
520 },
521 }, 462 },
522 ); 463 );
523 464
@@ -547,10 +488,10 @@ pub fn usageFull(
547 valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8, 488 valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
548) !void { 489) !void {
549 var cos = io.countingOutStream(stream); 490 var cos = io.countingOutStream(stream);
550 const cs = cos.outStream(); 491 const cs = cos.writer();
551 for (params) |param| { 492 for (params) |param| {
552 const name = param.names.short orelse continue; 493 const name = param.names.short orelse continue;
553 if (param.takes_value != .None) 494 if (param.takes_value != .none)
554 continue; 495 continue;
555 496
556 if (cos.bytes_written == 0) 497 if (cos.bytes_written == 0)
@@ -562,7 +503,7 @@ pub fn usageFull(
562 503
563 var positional: ?Param(Id) = null; 504 var positional: ?Param(Id) = null;
564 for (params) |param| { 505 for (params) |param| {
565 if (param.takes_value == .None and param.names.short != null) 506 if (param.takes_value == .none and param.names.short != null)
566 continue; 507 continue;
567 508
568 const prefix = if (param.names.short) |_| "-" else "--"; 509 const prefix = if (param.names.short) |_| "-" else "--";
@@ -578,9 +519,9 @@ pub fn usageFull(
578 519
579 try cs.print("[{}{}", .{ prefix, name }); 520 try cs.print("[{}{}", .{ prefix, name });
580 switch (param.takes_value) { 521 switch (param.takes_value) {
581 .None => {}, 522 .none => {},
582 .One => try cs.print(" <{}>", .{try valueText(context, param)}), 523 .one => try cs.print(" <{}>", .{try valueText(context, param)}),
583 .Many => try cs.print(" <{}>...", .{try valueText(context, param)}), 524 .many => try cs.print(" <{}>...", .{try valueText(context, param)}),
584 } 525 }
585 526
586 try cs.writeByte(']'); 527 try cs.writeByte(']');
@@ -627,7 +568,7 @@ pub fn usage(stream: anytype, params: []const Param(Help)) !void {
627fn testUsage(expected: []const u8, params: []const Param(Help)) !void { 568fn testUsage(expected: []const u8, params: []const Param(Help)) !void {
628 var buf: [1024]u8 = undefined; 569 var buf: [1024]u8 = undefined;
629 var fbs = io.fixedBufferStream(&buf); 570 var fbs = io.fixedBufferStream(&buf);
630 try usage(fbs.outStream(), params); 571 try usage(fbs.writer(), params);
631 testing.expectEqualStrings(expected, fbs.getWritten()); 572 testing.expectEqualStrings(expected, fbs.getWritten());
632} 573}
633 574
@@ -650,12 +591,7 @@ test "usage" {
650 parseParam("--b <v>") catch unreachable, 591 parseParam("--b <v>") catch unreachable,
651 }); 592 });
652 try testUsage("<file>", comptime &[_]Param(Help){ 593 try testUsage("<file>", comptime &[_]Param(Help){
653 Param(Help){ 594 parseParam("<file>") catch unreachable,
654 .id = Help{
655 .value = "file",
656 },
657 .takes_value = .One,
658 },
659 }); 595 });
660 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", comptime &[_]Param(Help){ 596 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", comptime &[_]Param(Help){
661 parseParam("-a") catch unreachable, 597 parseParam("-a") catch unreachable,
@@ -667,11 +603,6 @@ test "usage" {
667 parseParam("--g <value>") catch unreachable, 603 parseParam("--g <value>") catch unreachable,
668 parseParam("--h <v>") catch unreachable, 604 parseParam("--h <v>") catch unreachable,
669 parseParam("-i <v>...") catch unreachable, 605 parseParam("-i <v>...") catch unreachable,
670 Param(Help){ 606 parseParam("<file>") catch unreachable,
671 .id = Help{
672 .value = "file",
673 },
674 .takes_value = .One,
675 },
676 }); 607 });
677} 608}