summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--README.md48
-rw-r--r--clap.zig265
-rw-r--r--clap/comptime.zig237
-rw-r--r--clap/streaming.zig12
-rw-r--r--clap/untyped.zig394
-rw-r--r--example/README.md.template4
-rw-r--r--example/help.zig10
-rw-r--r--example/simple-ex.zig20
-rw-r--r--example/simple.zig20
-rw-r--r--example/streaming-clap.zig2
-rw-r--r--example/usage.zig12
11 files changed, 506 insertions, 518 deletions
diff --git a/README.md b/README.md
index 75b4b19..347250e 100644
--- a/README.md
+++ b/README.md
@@ -34,30 +34,30 @@ pub fn main() !void {
34 // First we specify what parameters our program can take. 34 // First we specify what parameters our program can take.
35 // We can use `parseParam` to parse a string to a `Param(Help)` 35 // We can use `parseParam` to parse a string to a `Param(Help)`
36 const params = comptime [_]clap.Param(clap.Help){ 36 const params = comptime [_]clap.Param(clap.Help){
37 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 37 clap.untyped.parseParam("-h, --help Display this help and exit.") catch unreachable,
38 clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, 38 clap.untyped.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable,
39 clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, 39 clap.untyped.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable,
40 clap.parseParam("<POS>...") catch unreachable, 40 clap.untyped.parseParam("<POS>...") catch unreachable,
41 }; 41 };
42 42
43 // Initalize our diagnostics, which can be used for reporting useful errors. 43 // Initalize our diagnostics, which can be used for reporting useful errors.
44 // This is optional. You can also pass `.{}` to `clap.parse` if you don't 44 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
45 // care about the extra information `Diagnostics` provides. 45 // care about the extra information `Diagnostics` provides.
46 var diag = clap.Diagnostic{}; 46 var diag = clap.Diagnostic{};
47 var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| { 47 var res = clap.untyped.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| {
48 // Report useful error and exit 48 // Report useful error and exit
49 diag.report(io.getStdErr().writer(), err) catch {}; 49 diag.report(io.getStdErr().writer(), err) catch {};
50 return err; 50 return err;
51 }; 51 };
52 defer args.deinit(); 52 defer res.deinit();
53 53
54 if (args.flag("--help")) 54 if (res.args.help)
55 debug.print("--help\n", .{}); 55 debug.print("--help\n", .{});
56 if (args.option("--number")) |n| 56 if (res.args.number) |n|
57 debug.print("--number = {s}\n", .{n}); 57 debug.print("--number = {s}\n", .{n});
58 for (args.options("--string")) |s| 58 for (res.args.string) |s|
59 debug.print("--string = {s}\n", .{s}); 59 debug.print("--string = {s}\n", .{s});
60 for (args.positionals()) |pos| 60 for (res.positionals) |pos|
61 debug.print("{s}\n", .{pos}); 61 debug.print("{s}\n", .{pos});
62} 62}
63 63
@@ -100,9 +100,9 @@ zig-clap/example/simple-error.zig:16:18: note: called from here
100 100
101There is also a `parseEx` variant that takes an argument iterator. 101There is also a `parseEx` variant that takes an argument iterator.
102 102
103### `StreamingClap` 103### `streaming.Clap`
104 104
105The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an 105The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an
106`args.Iterator` to provide it with arguments lazily. 106`args.Iterator` to provide it with arguments lazily.
107 107
108```zig 108```zig
@@ -140,7 +140,7 @@ pub fn main() !void {
140 // This is optional. You can also leave the `diagnostic` field unset if you 140 // This is optional. You can also leave the `diagnostic` field unset if you
141 // don't care about the extra information `Diagnostic` provides. 141 // don't care about the extra information `Diagnostic` provides.
142 var diag = clap.Diagnostic{}; 142 var diag = clap.Diagnostic{};
143 var parser = clap.StreamingClap(u8, process.ArgIterator){ 143 var parser = clap.streaming.Clap(u8, process.ArgIterator){
144 .params = &params, 144 .params = &params,
145 .iter = &iter, 145 .iter = &iter,
146 .diagnostic = &diag, 146 .diagnostic = &diag,
@@ -182,17 +182,17 @@ const std = @import("std");
182 182
183pub fn main() !void { 183pub fn main() !void {
184 const params = comptime [_]clap.Param(clap.Help){ 184 const params = comptime [_]clap.Param(clap.Help){
185 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 185 clap.untyped.parseParam("-h, --help Display this help and exit. ") catch unreachable,
186 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 186 clap.untyped.parseParam("-v, --version Output version information and exit.") catch unreachable,
187 }; 187 };
188 188
189 var args = try clap.parse(clap.Help, &params, .{}); 189 var res = try clap.untyped.parse(clap.Help, &params, .{});
190 defer args.deinit(); 190 defer res.deinit();
191 191
192 // clap.help is a function that can print a simple help message, given a 192 // clap.help is a function that can print a simple help message, given a
193 // slice of Param(Help). There is also a helpEx, which can print a 193 // slice of Param(Help). There is also a helpEx, which can print a
194 // help message for any Param, but it is more verbose to call. 194 // help message for any Param, but it is more verbose to call.
195 if (args.flag("--help")) 195 if (res.args.help)
196 return clap.help(std.io.getStdErr().writer(), &params); 196 return clap.help(std.io.getStdErr().writer(), &params);
197} 197}
198 198
@@ -224,18 +224,18 @@ const std = @import("std");
224 224
225pub fn main() !void { 225pub fn main() !void {
226 const params = comptime [_]clap.Param(clap.Help){ 226 const params = comptime [_]clap.Param(clap.Help){
227 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 227 clap.untyped.parseParam("-h, --help Display this help and exit.") catch unreachable,
228 clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, 228 clap.untyped.parseParam("-v, --version Output version information and exit.") catch unreachable,
229 clap.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable, 229 clap.untyped.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable,
230 }; 230 };
231 231
232 var args = try clap.parse(clap.Help, &params, .{}); 232 var res = try clap.untyped.parse(clap.Help, &params, .{});
233 defer args.deinit(); 233 defer res.deinit();
234 234
235 // clap.usage is a function that can print a simple usage message, given a 235 // clap.usage is a function that can print a simple usage message, given a
236 // slice of Param(Help). There is also a usageEx, which can print a 236 // slice of Param(Help). There is also a usageEx, which can print a
237 // usage message for any Param, but it is more verbose to call. 237 // usage message for any Param, but it is more verbose to call.
238 if (args.flag("--help")) 238 if (res.args.help)
239 return clap.usage(std.io.getStdErr().writer(), &params); 239 return clap.usage(std.io.getStdErr().writer(), &params);
240} 240}
241 241
diff --git a/clap.zig b/clap.zig
index 39bbef2..f10b61b 100644
--- a/clap.zig
+++ b/clap.zig
@@ -13,8 +13,8 @@ test "clap" {
13 testing.refAllDecls(@This()); 13 testing.refAllDecls(@This());
14} 14}
15 15
16pub const ComptimeClap = @import("clap/comptime.zig").ComptimeClap; 16pub const streaming = @import("clap/streaming.zig");
17pub const StreamingClap = @import("clap/streaming.zig").StreamingClap; 17pub const untyped = @import("clap/untyped.zig");
18 18
19/// The names a ::Param can have. 19/// The names a ::Param can have.
20pub const Names = struct { 20pub const Names = struct {
@@ -23,6 +23,23 @@ pub const Names = struct {
23 23
24 /// '--' prefix 24 /// '--' prefix
25 long: ?[]const u8 = null, 25 long: ?[]const u8 = null,
26
27 pub fn longest(names: *const Names) Longest {
28 if (names.long) |long|
29 return .{ .kind = .long, .name = long };
30 if (names.short) |*short| {
31 // TODO: Zig cannot figure out @as(*const [1]u8, short) in the ano literal
32 const casted: *const [1]u8 = short;
33 return .{ .kind = .short, .name = casted };
34 }
35
36 return .{ .kind = .positinal, .name = "" };
37 }
38
39 pub const Longest = struct {
40 kind: enum { long, short, positinal },
41 name: []const u8,
42 };
26}; 43};
27 44
28/// Whether a param takes no value (a flag), one value, or can be specified multiple times. 45/// Whether a param takes no value (a flag), one value, or can be specified multiple times.
@@ -61,155 +78,6 @@ pub fn Param(comptime Id: type) type {
61 }; 78 };
62} 79}
63 80
64/// Takes a string and parses it to a Param(Help).
65/// This is the reverse of 'help' but for at single parameter only.
66pub fn parseParam(line: []const u8) !Param(Help) {
67 // This function become a lot less ergonomic to use once you hit the eval branch quota. To
68 // avoid this we pick a sane default. Sadly, the only sane default is the biggest possible
69 // value. If we pick something a lot smaller and a user hits the quota after that, they have
70 // no way of overriding it, since we set it here.
71 // We can recosider this again if:
72 // * We get parseParams: https://github.com/Hejsil/zig-clap/issues/39
73 // * We get a larger default branch quota in the zig compiler (stage 2).
74 // * Someone points out how this is a really bad idea.
75 @setEvalBranchQuota(std.math.maxInt(u32));
76
77 var found_comma = false;
78 var it = mem.tokenize(u8, line, " \t");
79 var param_str = it.next() orelse return error.NoParamFound;
80
81 const short_name = if (!mem.startsWith(u8, param_str, "--") and
82 mem.startsWith(u8, param_str, "-"))
83 blk: {
84 found_comma = param_str[param_str.len - 1] == ',';
85 if (found_comma)
86 param_str = param_str[0 .. param_str.len - 1];
87
88 if (param_str.len != 2)
89 return error.InvalidShortParam;
90
91 const short_name = param_str[1];
92 if (!found_comma) {
93 var res = parseParamRest(it.rest());
94 res.names.short = short_name;
95 return res;
96 }
97
98 param_str = it.next() orelse return error.NoParamFound;
99 break :blk short_name;
100 } else null;
101
102 const long_name = if (mem.startsWith(u8, param_str, "--")) blk: {
103 if (param_str[param_str.len - 1] == ',')
104 return error.TrailingComma;
105
106 break :blk param_str[2..];
107 } else if (found_comma) {
108 return error.TrailingComma;
109 } else if (short_name == null) {
110 return parseParamRest(mem.trimLeft(u8, line, " \t"));
111 } else null;
112
113 var res = parseParamRest(it.rest());
114 res.names.long = long_name;
115 res.names.short = short_name;
116 return res;
117}
118
119fn parseParamRest(line: []const u8) Param(Help) {
120 if (mem.startsWith(u8, line, "<")) blk: {
121 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk;
122 const takes_many = mem.startsWith(u8, line[len + 1 ..], "...");
123 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many);
124 return .{
125 .takes_value = if (takes_many) .many else .one,
126 .id = .{
127 .msg = mem.trim(u8, line[help_start..], " \t"),
128 .value = line[1..len],
129 },
130 };
131 }
132
133 return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } };
134}
135
136fn expectParam(expect: Param(Help), actual: Param(Help)) !void {
137 try testing.expectEqualStrings(expect.id.msg, actual.id.msg);
138 try testing.expectEqualStrings(expect.id.value, actual.id.value);
139 try testing.expectEqual(expect.names.short, actual.names.short);
140 try testing.expectEqual(expect.takes_value, actual.takes_value);
141 if (expect.names.long) |long| {
142 try testing.expectEqualStrings(long, actual.names.long.?);
143 } else {
144 try testing.expectEqual(@as(?[]const u8, null), actual.names.long);
145 }
146}
147
148test "parseParam" {
149 try expectParam(Param(Help){
150 .id = .{ .msg = "Help text", .value = "value" },
151 .names = .{ .short = 's', .long = "long" },
152 .takes_value = .one,
153 }, try parseParam("-s, --long <value> Help text"));
154
155 try expectParam(Param(Help){
156 .id = .{ .msg = "Help text", .value = "value" },
157 .names = .{ .short = 's', .long = "long" },
158 .takes_value = .many,
159 }, try parseParam("-s, --long <value>... Help text"));
160
161 try expectParam(Param(Help){
162 .id = .{ .msg = "Help text", .value = "value" },
163 .names = .{ .long = "long" },
164 .takes_value = .one,
165 }, try parseParam("--long <value> Help text"));
166
167 try expectParam(Param(Help){
168 .id = .{ .msg = "Help text", .value = "value" },
169 .names = .{ .short = 's' },
170 .takes_value = .one,
171 }, try parseParam("-s <value> Help text"));
172
173 try expectParam(Param(Help){
174 .id = .{ .msg = "Help text" },
175 .names = .{ .short = 's', .long = "long" },
176 }, try parseParam("-s, --long Help text"));
177
178 try expectParam(Param(Help){
179 .id = .{ .msg = "Help text" },
180 .names = .{ .short = 's' },
181 }, try parseParam("-s Help text"));
182
183 try expectParam(Param(Help){
184 .id = .{ .msg = "Help text" },
185 .names = .{ .long = "long" },
186 }, try parseParam("--long Help text"));
187
188 try expectParam(Param(Help){
189 .id = .{ .msg = "Help text", .value = "A | B" },
190 .names = .{ .long = "long" },
191 .takes_value = .one,
192 }, try parseParam("--long <A | B> Help text"));
193
194 try expectParam(Param(Help){
195 .id = .{ .msg = "Help text", .value = "A" },
196 .names = .{},
197 .takes_value = .one,
198 }, try parseParam("<A> Help text"));
199
200 try expectParam(Param(Help){
201 .id = .{ .msg = "Help text", .value = "A" },
202 .names = .{},
203 .takes_value = .many,
204 }, try parseParam("<A>... Help text"));
205
206 try testing.expectError(error.TrailingComma, parseParam("--long, Help"));
207 try testing.expectError(error.TrailingComma, parseParam("-s, Help"));
208 try testing.expectError(error.InvalidShortParam, parseParam("-ss Help"));
209 try testing.expectError(error.InvalidShortParam, parseParam("-ss <value> Help"));
210 try testing.expectError(error.InvalidShortParam, parseParam("- Help"));
211}
212
213/// Optional diagnostics used for reporting useful errors 81/// Optional diagnostics used for reporting useful errors
214pub const Diagnostic = struct { 82pub const Diagnostic = struct {
215 arg: []const u8 = "", 83 arg: []const u8 = "",
@@ -306,7 +174,7 @@ test "Diagnostic.report" {
306pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type { 174pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type {
307 return struct { 175 return struct {
308 arena: std.heap.ArenaAllocator, 176 arena: std.heap.ArenaAllocator,
309 clap: ComptimeClap(Id, params), 177 clap: untyped.Clap(Id, params),
310 exe_arg: ?[]const u8, 178 exe_arg: ?[]const u8,
311 179
312 pub fn deinit(a: *@This()) void { 180 pub fn deinit(a: *@This()) void {
@@ -342,43 +210,6 @@ pub const ParseOptions = struct {
342 diagnostic: ?*Diagnostic = null, 210 diagnostic: ?*Diagnostic = null,
343}; 211};
344 212
345/// Same as `parseEx` but uses the `args.OsIterator` by default.
346pub fn parse(
347 comptime Id: type,
348 comptime params: []const Param(Id),
349 opt: ParseOptions,
350) !Args(Id, params) {
351 var arena = heap.ArenaAllocator.init(opt.allocator);
352 errdefer arena.deinit();
353
354 var iter = try process.ArgIterator.initWithAllocator(arena.allocator());
355 const exe_arg = iter.next();
356
357 const clap = try parseEx(Id, params, &iter, .{
358 // Let's reuse the arena from the `OSIterator` since we already have it.
359 .allocator = arena.allocator(),
360 .diagnostic = opt.diagnostic,
361 });
362
363 return Args(Id, params){
364 .exe_arg = exe_arg,
365 .arena = arena,
366 .clap = clap,
367 };
368}
369
370/// Parses the command line arguments passed into the program based on an
371/// array of `Param`s.
372pub fn parseEx(
373 comptime Id: type,
374 comptime params: []const Param(Id),
375 iter: anytype,
376 opt: ParseOptions,
377) !ComptimeClap(Id, params) {
378 const Clap = ComptimeClap(Id, params);
379 return try Clap.parse(iter, opt);
380}
381
382/// Will print a help message in the following format: 213/// Will print a help message in the following format:
383/// -s, --long <valueText> helpText 214/// -s, --long <valueText> helpText
384/// -s, helpText 215/// -s, helpText
@@ -526,15 +357,15 @@ test "clap.help" {
526 try help( 357 try help(
527 slice_stream.writer(), 358 slice_stream.writer(),
528 comptime &.{ 359 comptime &.{
529 parseParam("-a Short flag.") catch unreachable, 360 untyped.parseParam("-a Short flag.") catch unreachable,
530 parseParam("-b <V1> Short option.") catch unreachable, 361 untyped.parseParam("-b <V1> Short option.") catch unreachable,
531 parseParam("--aa Long flag.") catch unreachable, 362 untyped.parseParam("--aa Long flag.") catch unreachable,
532 parseParam("--bb <V2> Long option.") catch unreachable, 363 untyped.parseParam("--bb <V2> Long option.") catch unreachable,
533 parseParam("-c, --cc Both flag.") catch unreachable, 364 untyped.parseParam("-c, --cc Both flag.") catch unreachable,
534 parseParam("--complicate Flag with a complicated and\nvery long description that\nspans multiple lines.") catch unreachable, 365 untyped.parseParam("--complicate Flag with a complicated and\nvery long description that\nspans multiple lines.") catch unreachable,
535 parseParam("-d, --dd <V3> Both option.") catch unreachable, 366 untyped.parseParam("-d, --dd <V3> Both option.") catch unreachable,
536 parseParam("-d, --dd <V3>... Both repeated option.") catch unreachable, 367 untyped.parseParam("-d, --dd <V3>... Both repeated option.") catch unreachable,
537 parseParam( 368 untyped.parseParam(
538 "<P> Positional. This should not appear in the help message.", 369 "<P> Positional. This should not appear in the help message.",
539 ) catch unreachable, 370 ) catch unreachable,
540 }, 371 },
@@ -667,37 +498,37 @@ fn testUsage(expected: []const u8, params: []const Param(Help)) !void {
667test "usage" { 498test "usage" {
668 @setEvalBranchQuota(100000); 499 @setEvalBranchQuota(100000);
669 try testUsage("[-ab]", &.{ 500 try testUsage("[-ab]", &.{
670 try parseParam("-a"), 501 try untyped.parseParam("-a"),
671 try parseParam("-b"), 502 try untyped.parseParam("-b"),
672 }); 503 });
673 try testUsage("[-a <value>] [-b <v>]", &.{ 504 try testUsage("[-a <value>] [-b <v>]", &.{
674 try parseParam("-a <value>"), 505 try untyped.parseParam("-a <value>"),
675 try parseParam("-b <v>"), 506 try untyped.parseParam("-b <v>"),
676 }); 507 });
677 try testUsage("[--a] [--b]", &.{ 508 try testUsage("[--a] [--b]", &.{
678 try parseParam("--a"), 509 try untyped.parseParam("--a"),
679 try parseParam("--b"), 510 try untyped.parseParam("--b"),
680 }); 511 });
681 try testUsage("[--a <value>] [--b <v>]", &.{ 512 try testUsage("[--a <value>] [--b <v>]", &.{
682 try parseParam("--a <value>"), 513 try untyped.parseParam("--a <value>"),
683 try parseParam("--b <v>"), 514 try untyped.parseParam("--b <v>"),
684 }); 515 });
685 try testUsage("<file>", &.{ 516 try testUsage("<file>", &.{
686 try parseParam("<file>"), 517 try untyped.parseParam("<file>"),
687 }); 518 });
688 try testUsage( 519 try testUsage(
689 "[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", 520 "[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>",
690 &.{ 521 &.{
691 try parseParam("-a"), 522 try untyped.parseParam("-a"),
692 try parseParam("-b"), 523 try untyped.parseParam("-b"),
693 try parseParam("-c <value>"), 524 try untyped.parseParam("-c <value>"),
694 try parseParam("-d <v>"), 525 try untyped.parseParam("-d <v>"),
695 try parseParam("--e"), 526 try untyped.parseParam("--e"),
696 try parseParam("--f"), 527 try untyped.parseParam("--f"),
697 try parseParam("--g <value>"), 528 try untyped.parseParam("--g <value>"),
698 try parseParam("--h <v>"), 529 try untyped.parseParam("--h <v>"),
699 try parseParam("-i <v>..."), 530 try untyped.parseParam("-i <v>..."),
700 try parseParam("<file>"), 531 try untyped.parseParam("<file>"),
701 }, 532 },
702 ); 533 );
703} 534}
diff --git a/clap/comptime.zig b/clap/comptime.zig
deleted file mode 100644
index b440004..0000000
--- a/clap/comptime.zig
+++ /dev/null
@@ -1,237 +0,0 @@
1const clap = @import("../clap.zig");
2const std = @import("std");
3
4const debug = std.debug;
5const heap = std.heap;
6const io = std.io;
7const mem = std.mem;
8const testing = std.testing;
9
10/// Deprecated: Use `parseEx` instead
11pub fn ComptimeClap(
12 comptime Id: type,
13 comptime params: []const clap.Param(Id),
14) type {
15 comptime var flags: usize = 0;
16 comptime var single_options: usize = 0;
17 comptime var multi_options: usize = 0;
18 comptime var converted_params: []const clap.Param(usize) = &.{};
19 for (params) |param| {
20 var index: usize = 0;
21 if (param.names.long != null or param.names.short != null) {
22 const ptr = switch (param.takes_value) {
23 .none => &flags,
24 .one => &single_options,
25 .many => &multi_options,
26 };
27 index = ptr.*;
28 ptr.* += 1;
29 }
30
31 converted_params = converted_params ++ [_]clap.Param(usize){.{
32 .id = index,
33 .names = param.names,
34 .takes_value = param.takes_value,
35 }};
36 }
37
38 return struct {
39 multi_options: [multi_options][]const []const u8,
40 single_options: [single_options][]const u8,
41 single_options_is_set: std.PackedIntArray(u1, single_options),
42 flags: std.PackedIntArray(u1, flags),
43 pos: []const []const u8,
44 allocator: mem.Allocator,
45
46 pub fn parse(iter: anytype, opt: clap.ParseOptions) !@This() {
47 const allocator = opt.allocator;
48 var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options;
49 for (multis) |*multi|
50 multi.* = std.ArrayList([]const u8).init(allocator);
51
52 var pos = std.ArrayList([]const u8).init(allocator);
53
54 var res = @This(){
55 .multi_options = .{undefined} ** multi_options,
56 .single_options = .{undefined} ** single_options,
57 .single_options_is_set = std.PackedIntArray(u1, single_options).init(
58 .{0} ** single_options,
59 ),
60 .flags = std.PackedIntArray(u1, flags).init(.{0} ** flags),
61 .pos = undefined,
62 .allocator = allocator,
63 };
64
65 var stream = clap.StreamingClap(usize, @typeInfo(@TypeOf(iter)).Pointer.child){
66 .params = converted_params,
67 .iter = iter,
68 .diagnostic = opt.diagnostic,
69 };
70 while (try stream.next()) |arg| {
71 const param = arg.param;
72 if (param.names.long == null and param.names.short == null) {
73 try pos.append(arg.value.?);
74 } else if (param.takes_value == .one) {
75 debug.assert(res.single_options.len != 0);
76 if (res.single_options.len != 0) {
77 res.single_options[param.id] = arg.value.?;
78 res.single_options_is_set.set(param.id, 1);
79 }
80 } else if (param.takes_value == .many) {
81 debug.assert(multis.len != 0);
82 if (multis.len != 0)
83 try multis[param.id].append(arg.value.?);
84 } else {
85 debug.assert(res.flags.len != 0);
86 if (res.flags.len != 0)
87 res.flags.set(param.id, 1);
88 }
89 }
90
91 for (multis) |*multi, i|
92 res.multi_options[i] = multi.toOwnedSlice();
93 res.pos = pos.toOwnedSlice();
94
95 return res;
96 }
97
98 pub fn deinit(parser: @This()) void {
99 for (parser.multi_options) |o|
100 parser.allocator.free(o);
101 parser.allocator.free(parser.pos);
102 }
103
104 pub fn flag(parser: @This(), comptime name: []const u8) bool {
105 const param = comptime findParam(name);
106 if (param.takes_value != .none)
107 @compileError(name ++ " is an option and not a flag.");
108
109 return parser.flags.get(param.id) != 0;
110 }
111
112 pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 {
113 const param = comptime findParam(name);
114 if (param.takes_value == .none)
115 @compileError(name ++ " is a flag and not an option.");
116 if (param.takes_value == .many)
117 @compileError(name ++ " takes many options, not one.");
118 if (parser.single_options_is_set.get(param.id) == 0)
119 return null;
120 return parser.single_options[param.id];
121 }
122
123 pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 {
124 const param = comptime findParam(name);
125 if (param.takes_value == .none)
126 @compileError(name ++ " is a flag and not an option.");
127 if (param.takes_value == .one)
128 @compileError(name ++ " takes one option, not multiple.");
129
130 return parser.multi_options[param.id];
131 }
132
133 pub fn positionals(parser: @This()) []const []const u8 {
134 return parser.pos;
135 }
136
137 fn findParam(comptime name: []const u8) clap.Param(usize) {
138 comptime {
139 for (converted_params) |param| {
140 if (param.names.short) |s| {
141 if (mem.eql(u8, name, "-" ++ [_]u8{s}))
142 return param;
143 }
144 if (param.names.long) |l| {
145 if (mem.eql(u8, name, "--" ++ l))
146 return param;
147 }
148 }
149
150 @compileError(name ++ " is not a parameter.");
151 }
152 }
153 };
154}
155
156test "" {
157 const params = comptime &.{
158 clap.parseParam("-a, --aa") catch unreachable,
159 clap.parseParam("-b, --bb") catch unreachable,
160 clap.parseParam("-c, --cc <V>") catch unreachable,
161 clap.parseParam("-d, --dd <V>...") catch unreachable,
162 clap.parseParam("<P>") catch unreachable,
163 };
164
165 var iter = clap.args.SliceIterator{
166 .args = &.{
167 "-a", "-c", "0", "something", "-d", "a", "--dd", "b",
168 },
169 };
170 var args = try clap.parseEx(clap.Help, params, &iter, .{ .allocator = testing.allocator });
171 defer args.deinit();
172
173 try testing.expect(args.flag("-a"));
174 try testing.expect(args.flag("--aa"));
175 try testing.expect(!args.flag("-b"));
176 try testing.expect(!args.flag("--bb"));
177 try testing.expectEqualStrings("0", args.option("-c").?);
178 try testing.expectEqualStrings("0", args.option("--cc").?);
179 try testing.expectEqual(@as(usize, 1), args.positionals().len);
180 try testing.expectEqualStrings("something", args.positionals()[0]);
181 try testing.expectEqualSlices([]const u8, &.{ "a", "b" }, args.options("-d"));
182 try testing.expectEqualSlices([]const u8, &.{ "a", "b" }, args.options("--dd"));
183}
184
185test "empty" {
186 var iter = clap.args.SliceIterator{ .args = &.{} };
187 var args = try clap.parseEx(u8, &.{}, &iter, .{ .allocator = testing.allocator });
188 defer args.deinit();
189}
190
191fn testErr(
192 comptime params: []const clap.Param(u8),
193 args_strings: []const []const u8,
194 expected: []const u8,
195) !void {
196 var diag = clap.Diagnostic{};
197 var iter = clap.args.SliceIterator{ .args = args_strings };
198 _ = clap.parseEx(u8, params, &iter, .{
199 .allocator = testing.allocator,
200 .diagnostic = &diag,
201 }) catch |err| {
202 var buf: [1024]u8 = undefined;
203 var fbs = io.fixedBufferStream(&buf);
204 diag.report(fbs.writer(), err) catch return error.TestFailed;
205 try testing.expectEqualStrings(expected, fbs.getWritten());
206 return;
207 };
208
209 try testing.expect(false);
210}
211
212test "errors" {
213 const params = [_]clap.Param(u8){
214 .{
215 .id = 0,
216 .names = .{ .short = 'a', .long = "aa" },
217 },
218 .{
219 .id = 1,
220 .names = .{ .short = 'c', .long = "cc" },
221 .takes_value = .one,
222 },
223 };
224
225 try testErr(&params, &.{"q"}, "Invalid argument 'q'\n");
226 try testErr(&params, &.{"-q"}, "Invalid argument '-q'\n");
227 try testErr(&params, &.{"--q"}, "Invalid argument '--q'\n");
228 try testErr(&params, &.{"--q=1"}, "Invalid argument '--q'\n");
229 try testErr(&params, &.{"-a=1"}, "The argument '-a' does not take a value\n");
230 try testErr(&params, &.{"--aa=1"}, "The argument '--aa' does not take a value\n");
231 try testErr(&params, &.{"-c"}, "The argument '-c' requires a value but none was supplied\n");
232 try testErr(
233 &params,
234 &.{"--cc"},
235 "The argument '--cc' requires a value but none was supplied\n",
236 );
237}
diff --git a/clap/streaming.zig b/clap/streaming.zig
index 8eca51a..42b1912 100644
--- a/clap/streaming.zig
+++ b/clap/streaming.zig
@@ -10,7 +10,7 @@ const mem = std.mem;
10const os = std.os; 10const os = std.os;
11const testing = std.testing; 11const testing = std.testing;
12 12
13/// The result returned from StreamingClap.next 13/// The result returned from Clap.next
14pub fn Arg(comptime Id: type) type { 14pub fn Arg(comptime Id: type) type {
15 return struct { 15 return struct {
16 const Self = @This(); 16 const Self = @This();
@@ -21,9 +21,9 @@ pub fn Arg(comptime Id: type) type {
21} 21}
22 22
23/// A command line argument parser which, given an ArgIterator, will parse arguments according 23/// A command line argument parser which, given an ArgIterator, will parse arguments according
24/// to the params. StreamingClap parses in an iterating manner, so you have to use a loop 24/// to the params. Clap parses in an iterating manner, so you have to use a loop
25/// together with StreamingClap.next to parse all the arguments of your program. 25/// together with Clap.next to parse all the arguments of your program.
26pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { 26pub fn Clap(comptime Id: type, comptime ArgIterator: type) type {
27 return struct { 27 return struct {
28 const State = union(enum) { 28 const State = union(enum) {
29 normal, 29 normal,
@@ -209,7 +209,7 @@ fn testNoErr(
209 results: []const Arg(u8), 209 results: []const Arg(u8),
210) !void { 210) !void {
211 var iter = args.SliceIterator{ .args = args_strings }; 211 var iter = args.SliceIterator{ .args = args_strings };
212 var c = StreamingClap(u8, args.SliceIterator){ 212 var c = Clap(u8, args.SliceIterator){
213 .params = params, 213 .params = params,
214 .iter = &iter, 214 .iter = &iter,
215 }; 215 };
@@ -236,7 +236,7 @@ fn testErr(
236) !void { 236) !void {
237 var diag: clap.Diagnostic = undefined; 237 var diag: clap.Diagnostic = undefined;
238 var iter = args.SliceIterator{ .args = args_strings }; 238 var iter = args.SliceIterator{ .args = args_strings };
239 var c = StreamingClap(u8, args.SliceIterator){ 239 var c = Clap(u8, args.SliceIterator){
240 .params = params, 240 .params = params,
241 .iter = &iter, 241 .iter = &iter,
242 .diagnostic = &diag, 242 .diagnostic = &diag,
diff --git a/clap/untyped.zig b/clap/untyped.zig
new file mode 100644
index 0000000..c9b6621
--- /dev/null
+++ b/clap/untyped.zig
@@ -0,0 +1,394 @@
1const clap = @import("../clap.zig");
2const std = @import("std");
3
4const builtin = std.builtin;
5const debug = std.debug;
6const heap = std.heap;
7const io = std.io;
8const mem = std.mem;
9const process = std.process;
10const testing = std.testing;
11
12/// Same as `parseEx` but uses the `args.OsIterator` by default.
13pub fn parse(
14 comptime Id: type,
15 comptime params: []const clap.Param(Id),
16 opt: clap.ParseOptions,
17) !Result(Arguments(Id, params, []const []const u8, &[_][]const u8{})) {
18 var arena = heap.ArenaAllocator.init(opt.allocator);
19 errdefer arena.deinit();
20
21 var iter = try process.ArgIterator.initWithAllocator(arena.allocator());
22 const exe_arg = iter.next();
23
24 const result = try parseEx(Id, params, &iter, .{
25 // Let's reuse the arena from the `OSIterator` since we already have it.
26 .allocator = arena.allocator(),
27 .diagnostic = opt.diagnostic,
28 });
29
30 return Result(Arguments(Id, params, []const []const u8, &.{})){
31 .args = result.args,
32 .positionals = result.positionals,
33 .exe_arg = exe_arg,
34 .arena = arena,
35 };
36}
37
38pub fn Result(comptime Args: type) type {
39 return struct {
40 args: Args,
41 positionals: []const []const u8,
42 exe_arg: ?[]const u8,
43 arena: std.heap.ArenaAllocator,
44
45 pub fn deinit(result: @This()) void {
46 result.arena.deinit();
47 }
48 };
49}
50
51/// Parses the command line arguments passed into the program based on an
52/// array of `Param`s.
53pub fn parseEx(
54 comptime Id: type,
55 comptime params: []const clap.Param(Id),
56 iter: anytype,
57 opt: clap.ParseOptions,
58) !ResultEx(Arguments(Id, params, []const []const u8, &.{})) {
59 const allocator = opt.allocator;
60 var positionals = std.ArrayList([]const u8).init(allocator);
61 var args = Arguments(Id, params, std.ArrayListUnmanaged([]const u8), .{}){};
62 errdefer deinitArgs(allocator, &args);
63
64 var stream = clap.streaming.Clap(Id, @typeInfo(@TypeOf(iter)).Pointer.child){
65 .params = params,
66 .iter = iter,
67 .diagnostic = opt.diagnostic,
68 };
69 while (try stream.next()) |arg| {
70 inline for (params) |*param| {
71 if (param == arg.param) {
72 const longest = comptime param.names.longest();
73 switch (longest.kind) {
74 .short, .long => switch (param.takes_value) {
75 .none => @field(args, longest.name) = true,
76 .one => @field(args, longest.name) = arg.value.?,
77 .many => try @field(args, longest.name).append(allocator, arg.value.?),
78 },
79 .positinal => try positionals.append(arg.value.?),
80 }
81 }
82 }
83 }
84
85 var result_args = Arguments(Id, params, []const []const u8, &.{}){};
86 inline for (@typeInfo(@TypeOf(args)).Struct.fields) |field| {
87 if (field.field_type == std.ArrayListUnmanaged([]const u8)) {
88 const slice = @field(args, field.name).toOwnedSlice(allocator);
89 @field(result_args, field.name) = slice;
90 } else {
91 @field(result_args, field.name) = @field(args, field.name);
92 }
93 }
94
95 return ResultEx(@TypeOf(result_args)){
96 .args = result_args,
97 .positionals = positionals.toOwnedSlice(),
98 .allocator = allocator,
99 };
100}
101
102pub fn ResultEx(comptime Args: type) type {
103 return struct {
104 args: Args,
105 positionals: []const []const u8,
106 allocator: mem.Allocator,
107
108 pub fn deinit(result: *@This()) void {
109 deinitArgs(result.allocator, &result.args);
110 result.allocator.free(result.positionals);
111 }
112 };
113}
114
115fn deinitArgs(allocator: mem.Allocator, args: anytype) void {
116 const Args = @TypeOf(args.*);
117 inline for (@typeInfo(Args).Struct.fields) |field| {
118 if (field.field_type == []const []const u8)
119 allocator.free(@field(args, field.name));
120 if (field.field_type == std.ArrayListUnmanaged([]const u8))
121 @field(args, field.name).deinit(allocator);
122 }
123}
124
125fn Arguments(
126 comptime Id: type,
127 comptime params: []const clap.Param(Id),
128 comptime MultiArgsType: type,
129 comptime multi_args_default: MultiArgsType,
130) type {
131 var fields: [params.len]builtin.TypeInfo.StructField = undefined;
132
133 var i: usize = 0;
134 for (params) |param| {
135 const longest = param.names.longest();
136 if (longest.kind == .positinal)
137 continue;
138
139 const field_type = switch (param.takes_value) {
140 .none => bool,
141 .one => ?[]const u8,
142 .many => MultiArgsType,
143 };
144 fields[i] = .{
145 .name = longest.name,
146 .field_type = field_type,
147 .default_value = switch (param.takes_value) {
148 .none => &false,
149 .one => &@as(?[]const u8, null),
150 .many => &multi_args_default,
151 },
152 .is_comptime = false,
153 .alignment = @alignOf(field_type),
154 };
155 i += 1;
156 }
157
158 return @Type(.{ .Struct = .{
159 .layout = .Auto,
160 .fields = fields[0..i],
161 .decls = &.{},
162 .is_tuple = false,
163 } });
164}
165
166test "" {
167 const params = comptime &.{
168 parseParam("-a, --aa") catch unreachable,
169 parseParam("-b, --bb") catch unreachable,
170 parseParam("-c, --cc <V>") catch unreachable,
171 parseParam("-d, --dd <V>...") catch unreachable,
172 parseParam("<P>") catch unreachable,
173 };
174
175 var iter = clap.args.SliceIterator{
176 .args = &.{
177 "-a", "-c", "0", "something", "-d", "a", "--dd", "b",
178 },
179 };
180 var res = try clap.untyped.parseEx(clap.Help, params, &iter, .{
181 .allocator = testing.allocator,
182 });
183 defer res.deinit();
184
185 try testing.expect(res.args.aa);
186 try testing.expect(!res.args.bb);
187 try testing.expectEqualStrings("0", res.args.cc.?);
188 try testing.expectEqual(@as(usize, 1), res.positionals.len);
189 try testing.expectEqualStrings("something", res.positionals[0]);
190 try testing.expectEqualSlices([]const u8, &.{ "a", "b" }, res.args.dd);
191}
192
193test "empty" {
194 var iter = clap.args.SliceIterator{ .args = &.{} };
195 var res = try clap.untyped.parseEx(u8, &.{}, &iter, .{ .allocator = testing.allocator });
196 defer res.deinit();
197}
198
199fn testErr(
200 comptime params: []const clap.Param(u8),
201 args_strings: []const []const u8,
202 expected: []const u8,
203) !void {
204 var diag = clap.Diagnostic{};
205 var iter = clap.args.SliceIterator{ .args = args_strings };
206 _ = clap.untyped.parseEx(u8, params, &iter, .{
207 .allocator = testing.allocator,
208 .diagnostic = &diag,
209 }) catch |err| {
210 var buf: [1024]u8 = undefined;
211 var fbs = io.fixedBufferStream(&buf);
212 diag.report(fbs.writer(), err) catch return error.TestFailed;
213 try testing.expectEqualStrings(expected, fbs.getWritten());
214 return;
215 };
216
217 try testing.expect(false);
218}
219
220test "errors" {
221 const params = [_]clap.Param(u8){
222 .{
223 .id = 0,
224 .names = .{ .short = 'a', .long = "aa" },
225 },
226 .{
227 .id = 1,
228 .names = .{ .short = 'c', .long = "cc" },
229 .takes_value = .one,
230 },
231 };
232
233 try testErr(&params, &.{"q"}, "Invalid argument 'q'\n");
234 try testErr(&params, &.{"-q"}, "Invalid argument '-q'\n");
235 try testErr(&params, &.{"--q"}, "Invalid argument '--q'\n");
236 try testErr(&params, &.{"--q=1"}, "Invalid argument '--q'\n");
237 try testErr(&params, &.{"-a=1"}, "The argument '-a' does not take a value\n");
238 try testErr(&params, &.{"--aa=1"}, "The argument '--aa' does not take a value\n");
239 try testErr(&params, &.{"-c"}, "The argument '-c' requires a value but none was supplied\n");
240 try testErr(
241 &params,
242 &.{"--cc"},
243 "The argument '--cc' requires a value but none was supplied\n",
244 );
245}
246
247/// Takes a string and parses it to a Param(clap.Help).
248/// This is the reverse of 'help' but for at single parameter only.
249pub fn parseParam(line: []const u8) !clap.Param(clap.Help) {
250 // This function become a lot less ergonomic to use once you hit the eval branch quota. To
251 // avoid this we pick a sane default. Sadly, the only sane default is the biggest possible
252 // value. If we pick something a lot smaller and a user hits the quota after that, they have
253 // no way of overriding it, since we set it here.
254 // We can recosider this again if:
255 // * We get parseParams: https://github.com/Hejsil/zig-clap/issues/39
256 // * We get a larger default branch quota in the zig compiler (stage 2).
257 // * Someone points out how this is a really bad idea.
258 @setEvalBranchQuota(std.math.maxInt(u32));
259
260 var found_comma = false;
261 var it = mem.tokenize(u8, line, " \t");
262 var param_str = it.next() orelse return error.NoParamFound;
263
264 const short_name = if (!mem.startsWith(u8, param_str, "--") and
265 mem.startsWith(u8, param_str, "-"))
266 blk: {
267 found_comma = param_str[param_str.len - 1] == ',';
268 if (found_comma)
269 param_str = param_str[0 .. param_str.len - 1];
270
271 if (param_str.len != 2)
272 return error.InvalidShortParam;
273
274 const short_name = param_str[1];
275 if (!found_comma) {
276 var res = parseParamRest(it.rest());
277 res.names.short = short_name;
278 return res;
279 }
280
281 param_str = it.next() orelse return error.NoParamFound;
282 break :blk short_name;
283 } else null;
284
285 const long_name = if (mem.startsWith(u8, param_str, "--")) blk: {
286 if (param_str[param_str.len - 1] == ',')
287 return error.TrailingComma;
288
289 break :blk param_str[2..];
290 } else if (found_comma) {
291 return error.TrailingComma;
292 } else if (short_name == null) {
293 return parseParamRest(mem.trimLeft(u8, line, " \t"));
294 } else null;
295
296 var res = parseParamRest(it.rest());
297 res.names.long = long_name;
298 res.names.short = short_name;
299 return res;
300}
301
302fn parseParamRest(line: []const u8) clap.Param(clap.Help) {
303 if (mem.startsWith(u8, line, "<")) blk: {
304 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk;
305 const takes_many = mem.startsWith(u8, line[len + 1 ..], "...");
306 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many);
307 return .{
308 .takes_value = if (takes_many) .many else .one,
309 .id = .{
310 .msg = mem.trim(u8, line[help_start..], " \t"),
311 .value = line[1..len],
312 },
313 };
314 }
315
316 return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } };
317}
318
319fn expectParam(expect: clap.Param(clap.Help), actual: clap.Param(clap.Help)) !void {
320 try testing.expectEqualStrings(expect.id.msg, actual.id.msg);
321 try testing.expectEqualStrings(expect.id.value, actual.id.value);
322 try testing.expectEqual(expect.names.short, actual.names.short);
323 try testing.expectEqual(expect.takes_value, actual.takes_value);
324 if (expect.names.long) |long| {
325 try testing.expectEqualStrings(long, actual.names.long.?);
326 } else {
327 try testing.expectEqual(@as(?[]const u8, null), actual.names.long);
328 }
329}
330
331test "parseParam" {
332 try expectParam(clap.Param(clap.Help){
333 .id = .{ .msg = "Help text", .value = "value" },
334 .names = .{ .short = 's', .long = "long" },
335 .takes_value = .one,
336 }, try parseParam("-s, --long <value> Help text"));
337
338 try expectParam(clap.Param(clap.Help){
339 .id = .{ .msg = "Help text", .value = "value" },
340 .names = .{ .short = 's', .long = "long" },
341 .takes_value = .many,
342 }, try parseParam("-s, --long <value>... Help text"));
343
344 try expectParam(clap.Param(clap.Help){
345 .id = .{ .msg = "Help text", .value = "value" },
346 .names = .{ .long = "long" },
347 .takes_value = .one,
348 }, try parseParam("--long <value> Help text"));
349
350 try expectParam(clap.Param(clap.Help){
351 .id = .{ .msg = "Help text", .value = "value" },
352 .names = .{ .short = 's' },
353 .takes_value = .one,
354 }, try parseParam("-s <value> Help text"));
355
356 try expectParam(clap.Param(clap.Help){
357 .id = .{ .msg = "Help text" },
358 .names = .{ .short = 's', .long = "long" },
359 }, try parseParam("-s, --long Help text"));
360
361 try expectParam(clap.Param(clap.Help){
362 .id = .{ .msg = "Help text" },
363 .names = .{ .short = 's' },
364 }, try parseParam("-s Help text"));
365
366 try expectParam(clap.Param(clap.Help){
367 .id = .{ .msg = "Help text" },
368 .names = .{ .long = "long" },
369 }, try parseParam("--long Help text"));
370
371 try expectParam(clap.Param(clap.Help){
372 .id = .{ .msg = "Help text", .value = "A | B" },
373 .names = .{ .long = "long" },
374 .takes_value = .one,
375 }, try parseParam("--long <A | B> Help text"));
376
377 try expectParam(clap.Param(clap.Help){
378 .id = .{ .msg = "Help text", .value = "A" },
379 .names = .{},
380 .takes_value = .one,
381 }, try parseParam("<A> Help text"));
382
383 try expectParam(clap.Param(clap.Help){
384 .id = .{ .msg = "Help text", .value = "A" },
385 .names = .{},
386 .takes_value = .many,
387 }, try parseParam("<A>... Help text"));
388
389 try testing.expectError(error.TrailingComma, parseParam("--long, Help"));
390 try testing.expectError(error.TrailingComma, parseParam("-s, Help"));
391 try testing.expectError(error.InvalidShortParam, parseParam("-ss Help"));
392 try testing.expectError(error.InvalidShortParam, parseParam("-ss <value> Help"));
393 try testing.expectError(error.InvalidShortParam, parseParam("- Help"));
394}
diff --git a/example/README.md.template b/example/README.md.template
index 7f5c545..d792152 100644
--- a/example/README.md.template
+++ b/example/README.md.template
@@ -51,9 +51,9 @@ zig-clap/example/simple-error.zig:16:18: note: called from here
51 51
52There is also a `parseEx` variant that takes an argument iterator. 52There is also a `parseEx` variant that takes an argument iterator.
53 53
54### `StreamingClap` 54### `streaming.Clap`
55 55
56The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an 56The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an
57`args.Iterator` to provide it with arguments lazily. 57`args.Iterator` to provide it with arguments lazily.
58 58
59```zig 59```zig
diff --git a/example/help.zig b/example/help.zig
index de3b707..2e7ca2b 100644
--- a/example/help.zig
+++ b/example/help.zig
@@ -3,16 +3,16 @@ const std = @import("std");
3 3
4pub fn main() !void { 4pub fn main() !void {
5 const params = comptime [_]clap.Param(clap.Help){ 5 const params = comptime [_]clap.Param(clap.Help){
6 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 6 clap.untyped.parseParam("-h, --help Display this help and exit. ") catch unreachable,
7 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 7 clap.untyped.parseParam("-v, --version Output version information and exit.") catch unreachable,
8 }; 8 };
9 9
10 var args = try clap.parse(clap.Help, &params, .{}); 10 var res = try clap.untyped.parse(clap.Help, &params, .{});
11 defer args.deinit(); 11 defer res.deinit();
12 12
13 // clap.help is a function that can print a simple help message, given a 13 // clap.help is a function that can print a simple help message, given a
14 // slice of Param(Help). There is also a helpEx, which can print a 14 // slice of Param(Help). There is also a helpEx, which can print a
15 // help message for any Param, but it is more verbose to call. 15 // help message for any Param, but it is more verbose to call.
16 if (args.flag("--help")) 16 if (res.args.help)
17 return clap.help(std.io.getStdErr().writer(), &params); 17 return clap.help(std.io.getStdErr().writer(), &params);
18} 18}
diff --git a/example/simple-ex.zig b/example/simple-ex.zig
index d2dc77e..f1a958d 100644
--- a/example/simple-ex.zig
+++ b/example/simple-ex.zig
@@ -11,10 +11,10 @@ pub fn main() !void {
11 // First we specify what parameters our program can take. 11 // First we specify what parameters our program can take.
12 // We can use `parseParam` to parse a string to a `Param(Help)` 12 // We can use `parseParam` to parse a string to a `Param(Help)`
13 const params = comptime [_]clap.Param(clap.Help){ 13 const params = comptime [_]clap.Param(clap.Help){
14 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 14 clap.untyped.parseParam("-h, --help Display this help and exit.") catch unreachable,
15 clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, 15 clap.untyped.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable,
16 clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, 16 clap.untyped.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable,
17 clap.parseParam("<POS>...") catch unreachable, 17 clap.untyped.parseParam("<POS>...") catch unreachable,
18 }; 18 };
19 19
20 var iter = try process.ArgIterator.initWithAllocator(allocator); 20 var iter = try process.ArgIterator.initWithAllocator(allocator);
@@ -27,7 +27,7 @@ pub fn main() !void {
27 // This is optional. You can also pass `.{}` to `clap.parse` if you don't 27 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
28 // care about the extra information `Diagnostics` provides. 28 // care about the extra information `Diagnostics` provides.
29 var diag = clap.Diagnostic{}; 29 var diag = clap.Diagnostic{};
30 var args = clap.parseEx(clap.Help, &params, &iter, .{ 30 var res = clap.untyped.parseEx(clap.Help, &params, &iter, .{
31 .allocator = allocator, 31 .allocator = allocator,
32 .diagnostic = &diag, 32 .diagnostic = &diag,
33 }) catch |err| { 33 }) catch |err| {
@@ -35,14 +35,14 @@ pub fn main() !void {
35 diag.report(io.getStdErr().writer(), err) catch {}; 35 diag.report(io.getStdErr().writer(), err) catch {};
36 return err; 36 return err;
37 }; 37 };
38 defer args.deinit(); 38 defer res.deinit();
39 39
40 if (args.flag("--help")) 40 if (res.args.help)
41 debug.print("--help\n", .{}); 41 debug.print("--help\n", .{});
42 if (args.option("--number")) |n| 42 if (res.args.number) |n|
43 debug.print("--number = {s}\n", .{n}); 43 debug.print("--number = {s}\n", .{n});
44 for (args.options("--string")) |s| 44 for (res.args.string) |s|
45 debug.print("--string = {s}\n", .{s}); 45 debug.print("--string = {s}\n", .{s});
46 for (args.positionals()) |pos| 46 for (res.positionals) |pos|
47 debug.print("{s}\n", .{pos}); 47 debug.print("{s}\n", .{pos});
48} 48}
diff --git a/example/simple.zig b/example/simple.zig
index ff6d301..c37e896 100644
--- a/example/simple.zig
+++ b/example/simple.zig
@@ -8,29 +8,29 @@ pub fn main() !void {
8 // First we specify what parameters our program can take. 8 // First we specify what parameters our program can take.
9 // We can use `parseParam` to parse a string to a `Param(Help)` 9 // We can use `parseParam` to parse a string to a `Param(Help)`
10 const params = comptime [_]clap.Param(clap.Help){ 10 const params = comptime [_]clap.Param(clap.Help){
11 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 11 clap.untyped.parseParam("-h, --help Display this help and exit.") catch unreachable,
12 clap.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable, 12 clap.untyped.parseParam("-n, --number <NUM> An option parameter, which takes a value.") catch unreachable,
13 clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable, 13 clap.untyped.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable,
14 clap.parseParam("<POS>...") catch unreachable, 14 clap.untyped.parseParam("<POS>...") catch unreachable,
15 }; 15 };
16 16
17 // Initalize our diagnostics, which can be used for reporting useful errors. 17 // Initalize our diagnostics, which can be used for reporting useful errors.
18 // This is optional. You can also pass `.{}` to `clap.parse` if you don't 18 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
19 // care about the extra information `Diagnostics` provides. 19 // care about the extra information `Diagnostics` provides.
20 var diag = clap.Diagnostic{}; 20 var diag = clap.Diagnostic{};
21 var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| { 21 var res = clap.untyped.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| {
22 // Report useful error and exit 22 // Report useful error and exit
23 diag.report(io.getStdErr().writer(), err) catch {}; 23 diag.report(io.getStdErr().writer(), err) catch {};
24 return err; 24 return err;
25 }; 25 };
26 defer args.deinit(); 26 defer res.deinit();
27 27
28 if (args.flag("--help")) 28 if (res.args.help)
29 debug.print("--help\n", .{}); 29 debug.print("--help\n", .{});
30 if (args.option("--number")) |n| 30 if (res.args.number) |n|
31 debug.print("--number = {s}\n", .{n}); 31 debug.print("--number = {s}\n", .{n});
32 for (args.options("--string")) |s| 32 for (res.args.string) |s|
33 debug.print("--string = {s}\n", .{s}); 33 debug.print("--string = {s}\n", .{s});
34 for (args.positionals()) |pos| 34 for (res.positionals) |pos|
35 debug.print("{s}\n", .{pos}); 35 debug.print("{s}\n", .{pos});
36} 36}
diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig
index a7ab7d8..cacda56 100644
--- a/example/streaming-clap.zig
+++ b/example/streaming-clap.zig
@@ -32,7 +32,7 @@ pub fn main() !void {
32 // This is optional. You can also leave the `diagnostic` field unset if you 32 // This is optional. You can also leave the `diagnostic` field unset if you
33 // don't care about the extra information `Diagnostic` provides. 33 // don't care about the extra information `Diagnostic` provides.
34 var diag = clap.Diagnostic{}; 34 var diag = clap.Diagnostic{};
35 var parser = clap.StreamingClap(u8, process.ArgIterator){ 35 var parser = clap.streaming.Clap(u8, process.ArgIterator){
36 .params = &params, 36 .params = &params,
37 .iter = &iter, 37 .iter = &iter,
38 .diagnostic = &diag, 38 .diagnostic = &diag,
diff --git a/example/usage.zig b/example/usage.zig
index 368a6b3..04fedba 100644
--- a/example/usage.zig
+++ b/example/usage.zig
@@ -3,17 +3,17 @@ const std = @import("std");
3 3
4pub fn main() !void { 4pub fn main() !void {
5 const params = comptime [_]clap.Param(clap.Help){ 5 const params = comptime [_]clap.Param(clap.Help){
6 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 6 clap.untyped.parseParam("-h, --help Display this help and exit.") catch unreachable,
7 clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, 7 clap.untyped.parseParam("-v, --version Output version information and exit.") catch unreachable,
8 clap.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable, 8 clap.untyped.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable,
9 }; 9 };
10 10
11 var args = try clap.parse(clap.Help, &params, .{}); 11 var res = try clap.untyped.parse(clap.Help, &params, .{});
12 defer args.deinit(); 12 defer res.deinit();
13 13
14 // clap.usage is a function that can print a simple usage message, given a 14 // clap.usage is a function that can print a simple usage message, given a
15 // slice of Param(Help). There is also a usageEx, which can print a 15 // slice of Param(Help). There is also a usageEx, which can print a
16 // usage message for any Param, but it is more verbose to call. 16 // usage message for any Param, but it is more verbose to call.
17 if (args.flag("--help")) 17 if (res.args.help)
18 return clap.usage(std.io.getStdErr().writer(), &params); 18 return clap.usage(std.io.getStdErr().writer(), &params);
19} 19}