summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar Jimmi Holst Christensen2022-02-25 19:40:00 +0100
committerGravatar Komari Spaghetti2022-03-09 18:12:40 +0100
commit5f7b75d5523d9581eca5a72a362868ff517992e8 (patch)
tree5e874f9c935e0d7c838ea5aadf270ce2929f8e8a
parentBump actions/checkout from 2.4.0 to 3 (diff)
downloadzig-clap-5f7b75d5523d9581eca5a72a362868ff517992e8.tar.gz
zig-clap-5f7b75d5523d9581eca5a72a362868ff517992e8.tar.xz
zig-clap-5f7b75d5523d9581eca5a72a362868ff517992e8.zip
Allow for clap to parse argument values into types
This changes - `.flag`, `.option`, `.options` and `.positionals` are now just fields you access on the result of `parse` and `parseEx`. - `clap.ComptimeClap` has been removed. - `clap.StreamingClap` is now called `clap.streaming.Clap` - `parse` and `parseEx` now takes a `value_parsers` argument that provides the parsers to parse values. - Remove `helpEx`, `helpFull`, `usageEx` and `usageFull`. They now just expect `Id` to have methods for getting the description and value texts.
-rw-r--r--README.md137
-rw-r--r--build.zig2
-rw-r--r--clap.zig546
-rw-r--r--clap/comptime.zig237
-rw-r--r--clap/parsers.zig48
-rw-r--r--clap/streaming.zig34
-rw-r--r--example/README.md.template51
-rw-r--r--example/help.zig8
-rw-r--r--example/simple-error.zig13
-rw-r--r--example/simple-ex.zig39
-rw-r--r--example/simple.zig24
-rw-r--r--example/streaming-clap.zig2
-rw-r--r--example/usage.zig14
13 files changed, 570 insertions, 585 deletions
diff --git a/README.md b/README.md
index 75b4b19..55a43d9 100644
--- a/README.md
+++ b/README.md
@@ -34,75 +34,98 @@ 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.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.parseParam("-n, --number <usize> 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.parseParam("-s, --string <str>... An option parameter which can be specified multiple times.") catch unreachable,
40 clap.parseParam("<POS>...") catch unreachable, 40 clap.parseParam("<str>...") 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.parse(clap.Help, &params, clap.parsers.default, .{
48 .diagnostic = &diag,
49 }) catch |err| {
48 // Report useful error and exit 50 // Report useful error and exit
49 diag.report(io.getStdErr().writer(), err) catch {}; 51 diag.report(io.getStdErr().writer(), err) catch {};
50 return err; 52 return err;
51 }; 53 };
52 defer args.deinit(); 54 defer res.deinit();
53 55
54 if (args.flag("--help")) 56 if (res.args.help)
55 debug.print("--help\n", .{}); 57 debug.print("--help\n", .{});
56 if (args.option("--number")) |n| 58 if (res.args.number) |n|
57 debug.print("--number = {s}\n", .{n}); 59 debug.print("--number = {}\n", .{n});
58 for (args.options("--string")) |s| 60 for (res.args.string) |s|
59 debug.print("--string = {s}\n", .{s}); 61 debug.print("--string = {s}\n", .{s});
60 for (args.positionals()) |pos| 62 for (res.positionals) |pos|
61 debug.print("{s}\n", .{pos}); 63 debug.print("{s}\n", .{pos});
62} 64}
63 65
64``` 66```
65 67
66The data structure returned has lookup speed on par with array access (`arr[i]`) and validates 68The result will contain an `args` field and a `positionals` field. `args` will have one field
67that the strings you pass to `option`, `options` and `flag` are actually parameters that the 69for each none positional parameter of your program. The name of the field will be the longest
68program can take: 70name of the parameter.
71
72The fields in `args` are typed. The type is based on the name of the value the parameter takes.
73Since `--number` takes a `usize` the field `res.args.number` has the type `usize`.
74
75Note that this is only the case because `clap.parsers.default` has a field called `usize` which
76contains a parser that returns `usize`. You can pass in something other than `clap.parsers.default`
77if you want some other mapping.
69 78
70```zig 79```zig
71const clap = @import("clap"); 80const clap = @import("clap");
72const std = @import("std"); 81const std = @import("std");
73 82
83const debug = std.debug;
84const io = std.io;
85const process = std.process;
86
74pub fn main() !void { 87pub fn main() !void {
88 // First we specify what parameters our program can take.
89 // We can use `parseParam` to parse a string to a `Param(Help)`
75 const params = comptime [_]clap.Param(clap.Help){ 90 const params = comptime [_]clap.Param(clap.Help){
76 clap.parseParam("-h, --help Display this help and exit.") catch unreachable, 91 clap.parseParam("-h, --help Display this help and exit.") catch unreachable,
92 clap.parseParam("-n, --number <INT> An option parameter, which takes a value.") catch unreachable,
93 clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable,
94 clap.parseParam("<FILE>...") catch unreachable,
77 }; 95 };
78 96
79 var args = try clap.parse(clap.Help, &params, .{}); 97 // Declare our own parsers which are used to map the argument strings to other
80 defer args.deinit(); 98 // types.
99 const parsers = comptime .{
100 .STR = clap.parsers.string,
101 .FILE = clap.parsers.string,
102 .INT = clap.parsers.int(usize, 10),
103 };
81 104
82 _ = args.flag("--helps"); 105 var diag = clap.Diagnostic{};
83} 106 var res = clap.parse(clap.Help, &params, parsers, .{
107 .diagnostic = &diag,
108 }) catch |err| {
109 diag.report(io.getStdErr().writer(), err) catch {};
110 return err;
111 };
112 defer res.deinit();
84 113
85``` 114 if (res.args.help)
115 debug.print("--help\n", .{});
116 if (res.args.number) |n|
117 debug.print("--number = {}\n", .{n});
118 for (res.args.string) |s|
119 debug.print("--string = {s}\n", .{s});
120 for (res.positionals) |pos|
121 debug.print("{s}\n", .{pos});
122}
86 123
87``` 124```
88zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter.
89 @compileError(name ++ " is not a parameter.");
90 ^
91zig-clap/clap/comptime.zig:77:45: note: called from here
92 const param = comptime findParam(name);
93 ^
94zig-clap/clap.zig:238:31: note: called from here
95 return a.clap.flag(name);
96 ^
97zig-clap/example/simple-error.zig:16:18: note: called from here
98 _ = args.flag("--helps");
99```
100
101There is also a `parseEx` variant that takes an argument iterator.
102 125
103### `StreamingClap` 126### `streaming.Clap`
104 127
105The `StreamingClap` is the base of all the other parsers. It's a streaming parser that uses an 128The `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. 129`args.Iterator` to provide it with arguments lazily.
107 130
108```zig 131```zig
@@ -140,7 +163,7 @@ pub fn main() !void {
140 // This is optional. You can also leave the `diagnostic` field unset if you 163 // This is optional. You can also leave the `diagnostic` field unset if you
141 // don't care about the extra information `Diagnostic` provides. 164 // don't care about the extra information `Diagnostic` provides.
142 var diag = clap.Diagnostic{}; 165 var diag = clap.Diagnostic{};
143 var parser = clap.StreamingClap(u8, process.ArgIterator){ 166 var parser = clap.streaming.Clap(u8, process.ArgIterator){
144 .params = &params, 167 .params = &params,
145 .iter = &iter, 168 .iter = &iter,
146 .diagnostic = &diag, 169 .diagnostic = &diag,
@@ -173,8 +196,9 @@ is generated at runtime.
173 196
174### `help` 197### `help`
175 198
176The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the 199The `help` prints a simple list of all parameters the program can take. It expects the
177program can take. 200`Id` to have a `description` method and an `value` method so that it can provide that
201in the output.
178 202
179```zig 203```zig
180const clap = @import("clap"); 204const clap = @import("clap");
@@ -186,14 +210,14 @@ pub fn main() !void {
186 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 210 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
187 }; 211 };
188 212
189 var args = try clap.parse(clap.Help, &params, .{}); 213 var res = try clap.parse(clap.Help, &params, clap.parsers.default, .{});
190 defer args.deinit(); 214 defer res.deinit();
191 215
192 // clap.help is a function that can print a simple help message, given a 216 // 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 217 // 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. 218 // help message for any Param, but it is more verbose to call.
195 if (args.flag("--help")) 219 if (res.args.help)
196 return clap.help(std.io.getStdErr().writer(), &params); 220 return clap.help(std.io.getStdErr().writer(), clap.Help, &params);
197} 221}
198 222
199``` 223```
@@ -204,19 +228,10 @@ $ zig-out/bin/help --help
204 -v, --version Output version information and exit. 228 -v, --version Output version information and exit.
205``` 229```
206 230
207The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of
208`Param(Help)`.
209
210The `helpEx` is the generic version of `help`. It can print a help message for any
211`Param` give that the caller provides functions for getting the help and value strings.
212
213The `helpFull` is even more generic, allowing the functions that get the help and value strings
214to return errors and take a context as a parameter.
215
216### `usage` 231### `usage`
217 232
218The `usage`, `usageEx` and `usageFull` are functions for printing a small abbreviated version 233The `usage` prints a small abbreviated version of the help message. It expects the `Id`
219of the help message. 234to have a `value` method so it can provide that in the output.
220 235
221```zig 236```zig
222const clap = @import("clap"); 237const clap = @import("clap");
@@ -224,19 +239,19 @@ const std = @import("std");
224 239
225pub fn main() !void { 240pub fn main() !void {
226 const params = comptime [_]clap.Param(clap.Help){ 241 const params = comptime [_]clap.Param(clap.Help){
227 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 242 clap.parseParam("-h, --help Display this help and exit.") catch unreachable,
228 clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, 243 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
229 clap.parseParam(" --value <N> An option parameter, which takes a value.") catch unreachable, 244 clap.parseParam(" --value <str> An option parameter, which takes a value.") catch unreachable,
230 }; 245 };
231 246
232 var args = try clap.parse(clap.Help, &params, .{}); 247 var res = try clap.parse(clap.Help, &params, clap.parsers.default, .{});
233 defer args.deinit(); 248 defer res.deinit();
234 249
235 // clap.usage is a function that can print a simple usage message, given a 250 // 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 251 // 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. 252 // usage message for any Param, but it is more verbose to call.
238 if (args.flag("--help")) 253 if (res.args.help)
239 return clap.usage(std.io.getStdErr().writer(), &params); 254 return clap.usage(std.io.getStdErr().writer(), clap.Help, &params);
240} 255}
241 256
242``` 257```
diff --git a/build.zig b/build.zig
index 9b1c0ce..0d253e2 100644
--- a/build.zig
+++ b/build.zig
@@ -60,7 +60,7 @@ fn readMeStep(b: *Builder) *std.build.Step {
60 const stream = file.writer(); 60 const stream = file.writer();
61 try stream.print(@embedFile("example/README.md.template"), .{ 61 try stream.print(@embedFile("example/README.md.template"), .{
62 @embedFile("example/simple.zig"), 62 @embedFile("example/simple.zig"),
63 @embedFile("example/simple-error.zig"), 63 @embedFile("example/simple-ex.zig"),
64 @embedFile("example/streaming-clap.zig"), 64 @embedFile("example/streaming-clap.zig"),
65 @embedFile("example/help.zig"), 65 @embedFile("example/help.zig"),
66 @embedFile("example/usage.zig"), 66 @embedFile("example/usage.zig"),
diff --git a/clap.zig b/clap.zig
index 39bbef2..1dc90ee 100644
--- a/clap.zig
+++ b/clap.zig
@@ -1,5 +1,6 @@
1const std = @import("std"); 1const std = @import("std");
2 2
3const builtin = std.builtin;
3const debug = std.debug; 4const debug = std.debug;
4const heap = std.heap; 5const heap = std.heap;
5const io = std.io; 6const io = std.io;
@@ -8,21 +9,37 @@ const process = std.process;
8const testing = std.testing; 9const testing = std.testing;
9 10
10pub const args = @import("clap/args.zig"); 11pub const args = @import("clap/args.zig");
12pub const parsers = @import("clap/parsers.zig");
13pub const streaming = @import("clap/streaming.zig");
11 14
12test "clap" { 15test "clap" {
13 testing.refAllDecls(@This()); 16 testing.refAllDecls(@This());
14} 17}
15 18
16pub const ComptimeClap = @import("clap/comptime.zig").ComptimeClap; 19/// The names a `Param` can have.
17pub const StreamingClap = @import("clap/streaming.zig").StreamingClap;
18
19/// The names a ::Param can have.
20pub const Names = struct { 20pub const Names = struct {
21 /// '-' prefix 21 /// '-' prefix
22 short: ?u8 = null, 22 short: ?u8 = null,
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.
@@ -124,18 +141,18 @@ fn parseParamRest(line: []const u8) Param(Help) {
124 return .{ 141 return .{
125 .takes_value = if (takes_many) .many else .one, 142 .takes_value = if (takes_many) .many else .one,
126 .id = .{ 143 .id = .{
127 .msg = mem.trim(u8, line[help_start..], " \t"), 144 .desc = mem.trim(u8, line[help_start..], " \t"),
128 .value = line[1..len], 145 .val = line[1..len],
129 }, 146 },
130 }; 147 };
131 } 148 }
132 149
133 return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } }; 150 return .{ .id = .{ .desc = mem.trim(u8, line, " \t") } };
134} 151}
135 152
136fn expectParam(expect: Param(Help), actual: Param(Help)) !void { 153fn expectParam(expect: Param(Help), actual: Param(Help)) !void {
137 try testing.expectEqualStrings(expect.id.msg, actual.id.msg); 154 try testing.expectEqualStrings(expect.id.desc, actual.id.desc);
138 try testing.expectEqualStrings(expect.id.value, actual.id.value); 155 try testing.expectEqualStrings(expect.id.val, actual.id.val);
139 try testing.expectEqual(expect.names.short, actual.names.short); 156 try testing.expectEqual(expect.names.short, actual.names.short);
140 try testing.expectEqual(expect.takes_value, actual.takes_value); 157 try testing.expectEqual(expect.takes_value, actual.takes_value);
141 if (expect.names.long) |long| { 158 if (expect.names.long) |long| {
@@ -147,58 +164,58 @@ fn expectParam(expect: Param(Help), actual: Param(Help)) !void {
147 164
148test "parseParam" { 165test "parseParam" {
149 try expectParam(Param(Help){ 166 try expectParam(Param(Help){
150 .id = .{ .msg = "Help text", .value = "value" }, 167 .id = .{ .desc = "Help text", .val = "val" },
151 .names = .{ .short = 's', .long = "long" }, 168 .names = .{ .short = 's', .long = "long" },
152 .takes_value = .one, 169 .takes_value = .one,
153 }, try parseParam("-s, --long <value> Help text")); 170 }, try parseParam("-s, --long <val> Help text"));
154 171
155 try expectParam(Param(Help){ 172 try expectParam(Param(Help){
156 .id = .{ .msg = "Help text", .value = "value" }, 173 .id = .{ .desc = "Help text", .val = "val" },
157 .names = .{ .short = 's', .long = "long" }, 174 .names = .{ .short = 's', .long = "long" },
158 .takes_value = .many, 175 .takes_value = .many,
159 }, try parseParam("-s, --long <value>... Help text")); 176 }, try parseParam("-s, --long <val>... Help text"));
160 177
161 try expectParam(Param(Help){ 178 try expectParam(Param(Help){
162 .id = .{ .msg = "Help text", .value = "value" }, 179 .id = .{ .desc = "Help text", .val = "val" },
163 .names = .{ .long = "long" }, 180 .names = .{ .long = "long" },
164 .takes_value = .one, 181 .takes_value = .one,
165 }, try parseParam("--long <value> Help text")); 182 }, try parseParam("--long <val> Help text"));
166 183
167 try expectParam(Param(Help){ 184 try expectParam(Param(Help){
168 .id = .{ .msg = "Help text", .value = "value" }, 185 .id = .{ .desc = "Help text", .val = "val" },
169 .names = .{ .short = 's' }, 186 .names = .{ .short = 's' },
170 .takes_value = .one, 187 .takes_value = .one,
171 }, try parseParam("-s <value> Help text")); 188 }, try parseParam("-s <val> Help text"));
172 189
173 try expectParam(Param(Help){ 190 try expectParam(Param(Help){
174 .id = .{ .msg = "Help text" }, 191 .id = .{ .desc = "Help text" },
175 .names = .{ .short = 's', .long = "long" }, 192 .names = .{ .short = 's', .long = "long" },
176 }, try parseParam("-s, --long Help text")); 193 }, try parseParam("-s, --long Help text"));
177 194
178 try expectParam(Param(Help){ 195 try expectParam(Param(Help){
179 .id = .{ .msg = "Help text" }, 196 .id = .{ .desc = "Help text" },
180 .names = .{ .short = 's' }, 197 .names = .{ .short = 's' },
181 }, try parseParam("-s Help text")); 198 }, try parseParam("-s Help text"));
182 199
183 try expectParam(Param(Help){ 200 try expectParam(Param(Help){
184 .id = .{ .msg = "Help text" }, 201 .id = .{ .desc = "Help text" },
185 .names = .{ .long = "long" }, 202 .names = .{ .long = "long" },
186 }, try parseParam("--long Help text")); 203 }, try parseParam("--long Help text"));
187 204
188 try expectParam(Param(Help){ 205 try expectParam(Param(Help){
189 .id = .{ .msg = "Help text", .value = "A | B" }, 206 .id = .{ .desc = "Help text", .val = "A | B" },
190 .names = .{ .long = "long" }, 207 .names = .{ .long = "long" },
191 .takes_value = .one, 208 .takes_value = .one,
192 }, try parseParam("--long <A | B> Help text")); 209 }, try parseParam("--long <A | B> Help text"));
193 210
194 try expectParam(Param(Help){ 211 try expectParam(Param(Help){
195 .id = .{ .msg = "Help text", .value = "A" }, 212 .id = .{ .desc = "Help text", .val = "A" },
196 .names = .{}, 213 .names = .{},
197 .takes_value = .one, 214 .takes_value = .one,
198 }, try parseParam("<A> Help text")); 215 }, try parseParam("<A> Help text"));
199 216
200 try expectParam(Param(Help){ 217 try expectParam(Param(Help){
201 .id = .{ .msg = "Help text", .value = "A" }, 218 .id = .{ .desc = "Help text", .val = "A" },
202 .names = .{}, 219 .names = .{},
203 .takes_value = .many, 220 .takes_value = .many,
204 }, try parseParam("<A>... Help text")); 221 }, try parseParam("<A>... Help text"));
@@ -206,7 +223,7 @@ test "parseParam" {
206 try testing.expectError(error.TrailingComma, parseParam("--long, Help")); 223 try testing.expectError(error.TrailingComma, parseParam("--long, Help"));
207 try testing.expectError(error.TrailingComma, parseParam("-s, Help")); 224 try testing.expectError(error.TrailingComma, parseParam("-s, Help"));
208 try testing.expectError(error.InvalidShortParam, parseParam("-ss Help")); 225 try testing.expectError(error.InvalidShortParam, parseParam("-ss Help"));
209 try testing.expectError(error.InvalidShortParam, parseParam("-ss <value> Help")); 226 try testing.expectError(error.InvalidShortParam, parseParam("-ss <val> Help"));
210 try testing.expectError(error.InvalidShortParam, parseParam("- Help")); 227 try testing.expectError(error.InvalidShortParam, parseParam("- Help"));
211} 228}
212 229
@@ -230,15 +247,15 @@ pub const Diagnostic = struct {
230 Arg{ .prefix = "", .name = diag.arg }; 247 Arg{ .prefix = "", .name = diag.arg };
231 248
232 switch (err) { 249 switch (err) {
233 error.DoesntTakeValue => try stream.print( 250 streaming.Error.DoesntTakeValue => try stream.print(
234 "The argument '{s}{s}' does not take a value\n", 251 "The argument '{s}{s}' does not take a value\n",
235 .{ a.prefix, a.name }, 252 .{ a.prefix, a.name },
236 ), 253 ),
237 error.MissingValue => try stream.print( 254 streaming.Error.MissingValue => try stream.print(
238 "The argument '{s}{s}' requires a value but none was supplied\n", 255 "The argument '{s}{s}' requires a value but none was supplied\n",
239 .{ a.prefix, a.name }, 256 .{ a.prefix, a.name },
240 ), 257 ),
241 error.InvalidArgument => try stream.print( 258 streaming.Error.InvalidArgument => try stream.print(
242 "Invalid argument '{s}{s}'\n", 259 "Invalid argument '{s}{s}'\n",
243 .{ a.prefix, a.name }, 260 .{ a.prefix, a.name },
244 ), 261 ),
@@ -303,34 +320,6 @@ test "Diagnostic.report" {
303 ); 320 );
304} 321}
305 322
306pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type {
307 return struct {
308 arena: std.heap.ArenaAllocator,
309 clap: ComptimeClap(Id, params),
310 exe_arg: ?[]const u8,
311
312 pub fn deinit(a: *@This()) void {
313 a.arena.deinit();
314 }
315
316 pub fn flag(a: @This(), comptime name: []const u8) bool {
317 return a.clap.flag(name);
318 }
319
320 pub fn option(a: @This(), comptime name: []const u8) ?[]const u8 {
321 return a.clap.option(name);
322 }
323
324 pub fn options(a: @This(), comptime name: []const u8) []const []const u8 {
325 return a.clap.options(name);
326 }
327
328 pub fn positionals(a: @This()) []const []const u8 {
329 return a.clap.positionals();
330 }
331 };
332}
333
334/// Options that can be set to customize the behavior of parsing. 323/// Options that can be set to customize the behavior of parsing.
335pub const ParseOptions = struct { 324pub const ParseOptions = struct {
336 /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`. 325 /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`.
@@ -346,59 +335,348 @@ pub const ParseOptions = struct {
346pub fn parse( 335pub fn parse(
347 comptime Id: type, 336 comptime Id: type,
348 comptime params: []const Param(Id), 337 comptime params: []const Param(Id),
338 comptime value_parsers: anytype,
349 opt: ParseOptions, 339 opt: ParseOptions,
350) !Args(Id, params) { 340) !Result(Id, params, value_parsers) {
351 var arena = heap.ArenaAllocator.init(opt.allocator); 341 var arena = heap.ArenaAllocator.init(opt.allocator);
352 errdefer arena.deinit(); 342 errdefer arena.deinit();
353 343
354 var iter = try process.ArgIterator.initWithAllocator(arena.allocator()); 344 var iter = try process.ArgIterator.initWithAllocator(arena.allocator());
355 const exe_arg = iter.next(); 345 const exe_arg = iter.next();
356 346
357 const clap = try parseEx(Id, params, &iter, .{ 347 const result = try parseEx(Id, params, value_parsers, &iter, .{
358 // Let's reuse the arena from the `OSIterator` since we already have it. 348 // Let's reuse the arena from the `OSIterator` since we already have it.
359 .allocator = arena.allocator(), 349 .allocator = arena.allocator(),
360 .diagnostic = opt.diagnostic, 350 .diagnostic = opt.diagnostic,
361 }); 351 });
362 352
363 return Args(Id, params){ 353 return Result(Id, params, value_parsers){
354 .args = result.args,
355 .positionals = result.positionals,
364 .exe_arg = exe_arg, 356 .exe_arg = exe_arg,
365 .arena = arena, 357 .arena = arena,
366 .clap = clap,
367 }; 358 };
368} 359}
369 360
370/// Parses the command line arguments passed into the program based on an 361pub fn Result(
371/// array of `Param`s. 362 comptime Id: type,
363 comptime params: []const Param(Id),
364 comptime value_parsers: anytype,
365) type {
366 return struct {
367 args: Arguments(Id, params, value_parsers, .slice),
368 positionals: []const FindPositionalType(Id, params, value_parsers),
369 exe_arg: ?[]const u8,
370 arena: std.heap.ArenaAllocator,
371
372 pub fn deinit(result: @This()) void {
373 result.arena.deinit();
374 }
375 };
376}
377
378/// Parses the command line arguments passed into the program based on an array of parameters.
379///
380/// The result will contain an `args` field which contains all the non positional arguments passed
381/// in. There is a field in `args` for each parameter. The name of that field will be the result
382/// of this expression:
383/// ```
384/// param.names.longest().name`
385/// ```
386///
387/// The fields can have types other that `[]const u8` and this is based on what `value_parsers`
388/// you provide. The parser to use for each parameter is determined by the following expression:
389/// ```
390/// @field(value_parsers, param.id.value())
391/// ```
392///
393/// Where `value` is a function that returns the name of the value this parameter takes. A parser
394/// is simple a function with the signature:
395/// ```
396/// fn ([]const u8) Error!T
397/// ```
398///
399/// `T` can be any type and `Error` can be any error. You can pass `clap.parsers.default` if you
400/// just wonna get something up and running.
401///
402/// Caller ownes the result and should free it by calling `result.deinit()`
372pub fn parseEx( 403pub fn parseEx(
373 comptime Id: type, 404 comptime Id: type,
374 comptime params: []const Param(Id), 405 comptime params: []const Param(Id),
406 comptime value_parsers: anytype,
375 iter: anytype, 407 iter: anytype,
376 opt: ParseOptions, 408 opt: ParseOptions,
377) !ComptimeClap(Id, params) { 409) !ResultEx(Id, params, value_parsers) {
378 const Clap = ComptimeClap(Id, params); 410 const allocator = opt.allocator;
379 return try Clap.parse(iter, opt); 411 var positionals = std.ArrayList(
412 FindPositionalType(Id, params, value_parsers),
413 ).init(allocator);
414
415 var arguments = Arguments(Id, params, value_parsers, .list){};
416 errdefer deinitArgs(Id, params, value_parsers, .list, allocator, &arguments);
417
418 var stream = streaming.Clap(Id, @typeInfo(@TypeOf(iter)).Pointer.child){
419 .params = params,
420 .iter = iter,
421 .diagnostic = opt.diagnostic,
422 };
423 while (try stream.next()) |arg| {
424 inline for (params) |*param| {
425 if (param == arg.param) {
426 const parser = comptime switch (param.takes_value) {
427 .none => undefined,
428 .one, .many => @field(value_parsers, param.id.value()),
429 };
430
431 // TODO: Update opt.diagnostics when `parser` fails. This is blocked by compiler
432 // bugs that causes an infinit loop.
433 const longest = comptime param.names.longest();
434 switch (longest.kind) {
435 .short, .long => switch (param.takes_value) {
436 .none => @field(arguments, longest.name) = true,
437 .one => @field(arguments, longest.name) = try parser(arg.value.?),
438 .many => {
439 const value = try parser(arg.value.?);
440 try @field(arguments, longest.name).append(allocator, value);
441 },
442 },
443 .positinal => try positionals.append(try parser(arg.value.?)),
444 }
445 }
446 }
447 }
448
449 var result_args = Arguments(Id, params, value_parsers, .slice){};
450 inline for (@typeInfo(@TypeOf(arguments)).Struct.fields) |field| {
451 if (@typeInfo(field.field_type) == .Struct and
452 @hasDecl(field.field_type, "toOwnedSlice"))
453 {
454 const slice = @field(arguments, field.name).toOwnedSlice(allocator);
455 @field(result_args, field.name) = slice;
456 } else {
457 @field(result_args, field.name) = @field(arguments, field.name);
458 }
459 }
460
461 return ResultEx(Id, params, value_parsers){
462 .args = result_args,
463 .positionals = positionals.toOwnedSlice(),
464 .allocator = allocator,
465 };
466}
467
468pub fn ResultEx(
469 comptime Id: type,
470 comptime params: []const Param(Id),
471 comptime value_parsers: anytype,
472) type {
473 return struct {
474 args: Arguments(Id, params, value_parsers, .slice),
475 positionals: []const FindPositionalType(Id, params, value_parsers),
476 allocator: mem.Allocator,
477
478 pub fn deinit(result: *@This()) void {
479 deinitArgs(Id, params, value_parsers, .slice, result.allocator, &result.args);
480 result.allocator.free(result.positionals);
481 }
482 };
483}
484
485fn FindPositionalType(
486 comptime Id: type,
487 comptime params: []const Param(Id),
488 comptime value_parsers: anytype,
489) type {
490 for (params) |param| {
491 const longest = param.names.longest();
492 if (longest.kind == .positinal)
493 return ParamType(Id, param, value_parsers);
494 }
495
496 return []const u8;
497}
498
499fn ParamType(
500 comptime Id: type,
501 comptime param: Param(Id),
502 comptime value_parsers: anytype,
503) type {
504 const parser = switch (param.takes_value) {
505 .none => parsers.string,
506 .one, .many => @field(value_parsers, param.id.value()),
507 };
508 return parsers.Result(@TypeOf(parser));
509}
510
511fn deinitArgs(
512 comptime Id: type,
513 comptime params: []const Param(Id),
514 comptime value_parsers: anytype,
515 comptime multi_arg_kind: MultiArgKind,
516 allocator: mem.Allocator,
517 arguments: *Arguments(Id, params, value_parsers, multi_arg_kind),
518) void {
519 inline for (params) |param| {
520 const longest = comptime param.names.longest();
521 if (longest.kind == .positinal)
522 continue;
523 if (param.takes_value != .many)
524 continue;
525
526 switch (multi_arg_kind) {
527 .slice => allocator.free(@field(arguments, longest.name)),
528 .list => @field(arguments, longest.name).deinit(allocator),
529 }
530 }
380} 531}
381 532
533const MultiArgKind = enum { slice, list };
534
535fn Arguments(
536 comptime Id: type,
537 comptime params: []const Param(Id),
538 comptime value_parsers: anytype,
539 comptime multi_arg_kind: MultiArgKind,
540) type {
541 var fields: [params.len]builtin.TypeInfo.StructField = undefined;
542
543 var i: usize = 0;
544 for (params) |param| {
545 const longest = param.names.longest();
546 if (longest.kind == .positinal)
547 continue;
548
549 const T = ParamType(Id, param, value_parsers);
550 const FieldType = switch (param.takes_value) {
551 .none => bool,
552 .one => ?T,
553 .many => switch (multi_arg_kind) {
554 .slice => []const T,
555 .list => std.ArrayListUnmanaged(T),
556 },
557 };
558 fields[i] = .{
559 .name = longest.name,
560 .field_type = FieldType,
561 .default_value = switch (param.takes_value) {
562 .none => &false,
563 .one => &@as(?T, null),
564 .many => switch (multi_arg_kind) {
565 .slice => &@as([]const T, &[_]T{}),
566 .list => &std.ArrayListUnmanaged(T){},
567 },
568 },
569 .is_comptime = false,
570 .alignment = @alignOf(FieldType),
571 };
572 i += 1;
573 }
574
575 return @Type(.{ .Struct = .{
576 .layout = .Auto,
577 .fields = fields[0..i],
578 .decls = &.{},
579 .is_tuple = false,
580 } });
581}
582
583test "" {
584 const params = comptime &.{
585 parseParam("-a, --aa") catch unreachable,
586 parseParam("-b, --bb") catch unreachable,
587 parseParam("-c, --cc <str>") catch unreachable,
588 parseParam("-d, --dd <usize>...") catch unreachable,
589 parseParam("<str>") catch unreachable,
590 };
591
592 var iter = args.SliceIterator{
593 .args = &.{ "-a", "-c", "0", "something", "-d", "1", "--dd", "2" },
594 };
595 var res = try parseEx(Help, params, parsers.default, &iter, .{
596 .allocator = testing.allocator,
597 });
598 defer res.deinit();
599
600 try testing.expect(res.args.aa);
601 try testing.expect(!res.args.bb);
602 try testing.expectEqualStrings("0", res.args.cc.?);
603 try testing.expectEqual(@as(usize, 1), res.positionals.len);
604 try testing.expectEqualStrings("something", res.positionals[0]);
605 try testing.expectEqualSlices(usize, &.{ 1, 2 }, res.args.dd);
606}
607
608test "empty" {
609 var iter = args.SliceIterator{ .args = &.{} };
610 var res = try parseEx(u8, &.{}, parsers.default, &iter, .{ .allocator = testing.allocator });
611 defer res.deinit();
612}
613
614fn testErr(
615 comptime params: []const Param(Help),
616 args_strings: []const []const u8,
617 expected: []const u8,
618) !void {
619 var diag = Diagnostic{};
620 var iter = args.SliceIterator{ .args = args_strings };
621 _ = parseEx(Help, params, parsers.default, &iter, .{
622 .allocator = testing.allocator,
623 .diagnostic = &diag,
624 }) catch |err| {
625 var buf: [1024]u8 = undefined;
626 var fbs = io.fixedBufferStream(&buf);
627 diag.report(fbs.writer(), err) catch return error.TestFailed;
628 try testing.expectEqualStrings(expected, fbs.getWritten());
629 return;
630 };
631
632 try testing.expect(false);
633}
634
635test "errors" {
636 const params = comptime [_]Param(Help){
637 parseParam("-a, --aa") catch unreachable,
638 parseParam("-c, --cc <str>") catch unreachable,
639 };
640
641 try testErr(&params, &.{"q"}, "Invalid argument 'q'\n");
642 try testErr(&params, &.{"-q"}, "Invalid argument '-q'\n");
643 try testErr(&params, &.{"--q"}, "Invalid argument '--q'\n");
644 try testErr(&params, &.{"--q=1"}, "Invalid argument '--q'\n");
645 try testErr(&params, &.{"-a=1"}, "The argument '-a' does not take a value\n");
646 try testErr(&params, &.{"--aa=1"}, "The argument '--aa' does not take a value\n");
647 try testErr(&params, &.{"-c"}, "The argument '-c' requires a value but none was supplied\n");
648 try testErr(
649 &params,
650 &.{"--cc"},
651 "The argument '--cc' requires a value but none was supplied\n",
652 );
653}
654
655pub const Help = struct {
656 desc: []const u8 = "",
657 val: []const u8 = "",
658
659 pub fn description(h: Help) []const u8 {
660 return h.desc;
661 }
662
663 pub fn value(h: Help) []const u8 {
664 return h.val;
665 }
666};
667
382/// Will print a help message in the following format: 668/// Will print a help message in the following format:
383/// -s, --long <valueText> helpText 669/// -s, --long <valueText> helpText
384/// -s, helpText 670/// -s, helpText
385/// -s <valueText> helpText 671/// -s <valueText> helpText
386/// --long helpText 672/// --long helpText
387/// --long <valueText> helpText 673/// --long <valueText> helpText
388pub fn helpFull( 674pub fn help(stream: anytype, comptime Id: type, params: []const Param(Id)) !void {
389 stream: anytype,
390 comptime Id: type,
391 params: []const Param(Id),
392 comptime Error: type,
393 context: anytype,
394 helpText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
395 valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
396) !void {
397 const max_spacing = blk: { 675 const max_spacing = blk: {
398 var res: usize = 0; 676 var res: usize = 0;
399 for (params) |param| { 677 for (params) |param| {
400 var cs = io.countingWriter(io.null_writer); 678 var cs = io.countingWriter(io.null_writer);
401 try printParam(cs.writer(), Id, param, Error, context, valueText); 679 try printParam(cs.writer(), Id, param);
402 if (res < cs.bytes_written) 680 if (res < cs.bytes_written)
403 res = @intCast(usize, cs.bytes_written); 681 res = @intCast(usize, cs.bytes_written);
404 } 682 }
@@ -412,13 +690,13 @@ pub fn helpFull(
412 690
413 var cs = io.countingWriter(stream); 691 var cs = io.countingWriter(stream);
414 try stream.writeAll("\t"); 692 try stream.writeAll("\t");
415 try printParam(cs.writer(), Id, param, Error, context, valueText); 693 try printParam(cs.writer(), Id, param);
416 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written)); 694 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written));
417 695
418 const help_text = try helpText(context, param); 696 const description = param.id.description();
419 var help_text_line_it = mem.split(u8, help_text, "\n"); 697 var it = mem.split(u8, description, "\n");
420 var indent_line = false; 698 var indent_line = false;
421 while (help_text_line_it.next()) |line| : (indent_line = true) { 699 while (it.next()) |line| : (indent_line = true) {
422 if (indent_line) { 700 if (indent_line) {
423 try stream.writeAll("\t"); 701 try stream.writeAll("\t");
424 try stream.writeByteNTimes(' ', max_spacing); 702 try stream.writeByteNTimes(' ', max_spacing);
@@ -434,9 +712,6 @@ fn printParam(
434 stream: anytype, 712 stream: anytype,
435 comptime Id: type, 713 comptime Id: type,
436 param: Param(Id), 714 param: Param(Id),
437 comptime Error: type,
438 context: anytype,
439 valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
440) !void { 715) !void {
441 if (param.names.short) |s| { 716 if (param.names.short) |s| {
442 try stream.writeAll(&[_]u8{ '-', s }); 717 try stream.writeAll(&[_]u8{ '-', s });
@@ -458,66 +733,12 @@ fn printParam(
458 return; 733 return;
459 734
460 try stream.writeAll(" <"); 735 try stream.writeAll(" <");
461 try stream.writeAll(try valueText(context, param)); 736 try stream.writeAll(param.id.value());
462 try stream.writeAll(">"); 737 try stream.writeAll(">");
463 if (param.takes_value == .many) 738 if (param.takes_value == .many)
464 try stream.writeAll("..."); 739 try stream.writeAll("...");
465} 740}
466 741
467/// A wrapper around helpFull for simple helpText and valueText functions that
468/// cant return an error or take a context.
469pub fn helpEx(
470 stream: anytype,
471 comptime Id: type,
472 params: []const Param(Id),
473 helpText: fn (Param(Id)) []const u8,
474 valueText: fn (Param(Id)) []const u8,
475) !void {
476 const Context = struct {
477 helpText: fn (Param(Id)) []const u8,
478 valueText: fn (Param(Id)) []const u8,
479
480 pub fn help(c: @This(), p: Param(Id)) error{}![]const u8 {
481 return c.helpText(p);
482 }
483
484 pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 {
485 return c.valueText(p);
486 }
487 };
488
489 return helpFull(
490 stream,
491 Id,
492 params,
493 error{},
494 Context{
495 .helpText = helpText,
496 .valueText = valueText,
497 },
498 Context.help,
499 Context.value,
500 );
501}
502
503pub const Help = struct {
504 msg: []const u8 = "",
505 value: []const u8 = "",
506};
507
508/// A wrapper around helpEx that takes a Param(Help).
509pub fn help(stream: anytype, params: []const Param(Help)) !void {
510 try helpEx(stream, Help, params, getHelpSimple, getValueSimple);
511}
512
513fn getHelpSimple(param: Param(Help)) []const u8 {
514 return param.id.msg;
515}
516
517fn getValueSimple(param: Param(Help)) []const u8 {
518 return param.id.value;
519}
520
521test "clap.help" { 742test "clap.help" {
522 var buf: [1024]u8 = undefined; 743 var buf: [1024]u8 = undefined;
523 var slice_stream = io.fixedBufferStream(&buf); 744 var slice_stream = io.fixedBufferStream(&buf);
@@ -525,6 +746,7 @@ test "clap.help" {
525 @setEvalBranchQuota(10000); 746 @setEvalBranchQuota(10000);
526 try help( 747 try help(
527 slice_stream.writer(), 748 slice_stream.writer(),
749 Help,
528 comptime &.{ 750 comptime &.{
529 parseParam("-a Short flag.") catch unreachable, 751 parseParam("-a Short flag.") catch unreachable,
530 parseParam("-b <V1> Short option.") catch unreachable, 752 parseParam("-b <V1> Short option.") catch unreachable,
@@ -556,18 +778,11 @@ test "clap.help" {
556} 778}
557 779
558/// Will print a usage message in the following format: 780/// Will print a usage message in the following format:
559/// [-abc] [--longa] [-d <valueText>] [--longb <valueText>] <valueText> 781/// [-abc] [--longa] [-d <T>] [--longb <T>] <T>
560/// 782///
561/// First all none value taking parameters, which have a short name are 783/// First all none value taking parameters, which have a short name are printed, then non
562/// printed, then non positional parameters and finally the positinal. 784/// positional parameters and finally the positinal.
563pub fn usageFull( 785pub fn usage(stream: anytype, comptime Id: type, params: []const Param(Id)) !void {
564 stream: anytype,
565 comptime Id: type,
566 params: []const Param(Id),
567 comptime Error: type,
568 context: anytype,
569 valueText: fn (@TypeOf(context), Param(Id)) Error![]const u8,
570) !void {
571 var cos = io.countingWriter(stream); 786 var cos = io.countingWriter(stream);
572 const cs = cos.writer(); 787 const cs = cos.writer();
573 for (params) |param| { 788 for (params) |param| {
@@ -607,7 +822,7 @@ pub fn usageFull(
607 try cs.writeAll(name); 822 try cs.writeAll(name);
608 if (param.takes_value != .none) { 823 if (param.takes_value != .none) {
609 try cs.writeAll(" <"); 824 try cs.writeAll(" <");
610 try cs.writeAll(try valueText(context, param)); 825 try cs.writeAll(param.id.value());
611 try cs.writeAll(">"); 826 try cs.writeAll(">");
612 if (param.takes_value == .many) 827 if (param.takes_value == .many)
613 try cs.writeAll("..."); 828 try cs.writeAll("...");
@@ -621,46 +836,15 @@ pub fn usageFull(
621 try cs.writeAll(" "); 836 try cs.writeAll(" ");
622 837
623 try cs.writeAll("<"); 838 try cs.writeAll("<");
624 try cs.writeAll(try valueText(context, p)); 839 try cs.writeAll(p.id.value());
625 try cs.writeAll(">"); 840 try cs.writeAll(">");
626 } 841 }
627} 842}
628 843
629/// A wrapper around usageFull for a simple valueText functions that
630/// cant return an error or take a context.
631pub fn usageEx(
632 stream: anytype,
633 comptime Id: type,
634 params: []const Param(Id),
635 valueText: fn (Param(Id)) []const u8,
636) !void {
637 const Context = struct {
638 valueText: fn (Param(Id)) []const u8,
639
640 pub fn value(c: @This(), p: Param(Id)) error{}![]const u8 {
641 return c.valueText(p);
642 }
643 };
644
645 return usageFull(
646 stream,
647 Id,
648 params,
649 error{},
650 Context{ .valueText = valueText },
651 Context.value,
652 );
653}
654
655/// A wrapper around usageEx that takes a Param(Help).
656pub fn usage(stream: anytype, params: []const Param(Help)) !void {
657 try usageEx(stream, Help, params, getValueSimple);
658}
659
660fn testUsage(expected: []const u8, params: []const Param(Help)) !void { 844fn testUsage(expected: []const u8, params: []const Param(Help)) !void {
661 var buf: [1024]u8 = undefined; 845 var buf: [1024]u8 = undefined;
662 var fbs = io.fixedBufferStream(&buf); 846 var fbs = io.fixedBufferStream(&buf);
663 try usage(fbs.writer(), params); 847 try usage(fbs.writer(), Help, params);
664 try testing.expectEqualStrings(expected, fbs.getWritten()); 848 try testing.expectEqualStrings(expected, fbs.getWritten());
665} 849}
666 850
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/parsers.zig b/clap/parsers.zig
new file mode 100644
index 0000000..49b95a9
--- /dev/null
+++ b/clap/parsers.zig
@@ -0,0 +1,48 @@
1const std = @import("std");
2
3const fmt = std.fmt;
4
5pub const default = .{
6 .string = string,
7 .str = string,
8 .u8 = int(u8, 0),
9 .u16 = int(u16, 0),
10 .u32 = int(u32, 0),
11 .u64 = int(u64, 0),
12 .usize = int(usize, 0),
13 .i8 = int(i8, 0),
14 .i16 = int(i16, 0),
15 .i32 = int(i32, 0),
16 .i64 = int(i64, 0),
17 .isize = int(isize, 0),
18 .f32 = float(f32),
19 .f64 = float(f64),
20};
21
22pub fn string(in: []const u8) error{}![]const u8 {
23 return in;
24}
25
26pub fn int(comptime T: type, comptime radix: u8) fn ([]const u8) fmt.ParseIntError!T {
27 return struct {
28 fn parse(in: []const u8) fmt.ParseIntError!T {
29 return fmt.parseInt(T, in, radix);
30 }
31 }.parse;
32}
33
34pub fn float(comptime T: type) fn ([]const u8) fmt.ParseFloatError!T {
35 return struct {
36 fn parse(in: []const u8) fmt.ParseFloatError!T {
37 return fmt.parseFloat(T, in);
38 }
39 }.parse;
40}
41
42fn ReturnType(comptime P: type) type {
43 return @typeInfo(P).Fn.return_type.?;
44}
45
46pub fn Result(comptime P: type) type {
47 return @typeInfo(ReturnType(P)).ErrorUnion.payload;
48}
diff --git a/clap/streaming.zig b/clap/streaming.zig
index 8eca51a..2ab9c8d 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();
@@ -20,10 +20,18 @@ pub fn Arg(comptime Id: type) type {
20 }; 20 };
21} 21}
22 22
23pub const Error = error{
24 MissingValue,
25 InvalidArgument,
26 DoesntTakeValue,
27};
28
23/// A command line argument parser which, given an ArgIterator, will parse arguments according 29/// 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 30/// to the params. Clap parses in an iterating manner, so you have to use a loop together with
25/// together with StreamingClap.next to parse all the arguments of your program. 31/// Clap.next to parse all the arguments of your program.
26pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type { 32///
33/// This parser is the building block for all the more complicated parsers.
34pub fn Clap(comptime Id: type, comptime ArgIterator: type) type {
27 return struct { 35 return struct {
28 const State = union(enum) { 36 const State = union(enum) {
29 normal, 37 normal,
@@ -71,7 +79,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
71 continue; 79 continue;
72 if (param.takes_value == .none) { 80 if (param.takes_value == .none) {
73 if (maybe_value != null) 81 if (maybe_value != null)
74 return parser.err(arg, .{ .long = name }, error.DoesntTakeValue); 82 return parser.err(arg, .{ .long = name }, Error.DoesntTakeValue);
75 83
76 return Arg(Id){ .param = param }; 84 return Arg(Id){ .param = param };
77 } 85 }
@@ -81,13 +89,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
81 break :blk v; 89 break :blk v;
82 90
83 break :blk parser.iter.next() orelse 91 break :blk parser.iter.next() orelse
84 return parser.err(arg, .{ .long = name }, error.MissingValue); 92 return parser.err(arg, .{ .long = name }, Error.MissingValue);
85 }; 93 };
86 94
87 return Arg(Id){ .param = param, .value = value }; 95 return Arg(Id){ .param = param, .value = value };
88 } 96 }
89 97
90 return parser.err(arg, .{ .long = name }, error.InvalidArgument); 98 return parser.err(arg, .{ .long = name }, Error.InvalidArgument);
91 }, 99 },
92 .short => return try parser.chaining(.{ 100 .short => return try parser.chaining(.{
93 .arg = arg, 101 .arg = arg,
@@ -105,7 +113,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
105 113
106 return Arg(Id){ .param = param, .value = arg }; 114 return Arg(Id){ .param = param, .value = arg };
107 } else { 115 } else {
108 return parser.err(arg, .{}, error.InvalidArgument); 116 return parser.err(arg, .{}, Error.InvalidArgument);
109 }, 117 },
110 } 118 }
111 } 119 }
@@ -137,13 +145,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
137 const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false; 145 const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false;
138 if (param.takes_value == .none) { 146 if (param.takes_value == .none) {
139 if (next_is_eql) 147 if (next_is_eql)
140 return parser.err(arg, .{ .short = short }, error.DoesntTakeValue); 148 return parser.err(arg, .{ .short = short }, Error.DoesntTakeValue);
141 return Arg(Id){ .param = param }; 149 return Arg(Id){ .param = param };
142 } 150 }
143 151
144 if (arg.len <= next_index) { 152 if (arg.len <= next_index) {
145 const value = parser.iter.next() orelse 153 const value = parser.iter.next() orelse
146 return parser.err(arg, .{ .short = short }, error.MissingValue); 154 return parser.err(arg, .{ .short = short }, Error.MissingValue);
147 155
148 return Arg(Id){ .param = param, .value = value }; 156 return Arg(Id){ .param = param, .value = value };
149 } 157 }
@@ -154,7 +162,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
154 return Arg(Id){ .param = param, .value = arg[next_index..] }; 162 return Arg(Id){ .param = param, .value = arg[next_index..] };
155 } 163 }
156 164
157 return parser.err(arg, .{ .short = arg[index] }, error.InvalidArgument); 165 return parser.err(arg, .{ .short = arg[index] }, Error.InvalidArgument);
158 } 166 }
159 167
160 fn positionalParam(parser: *@This()) ?*const clap.Param(Id) { 168 fn positionalParam(parser: *@This()) ?*const clap.Param(Id) {
@@ -209,7 +217,7 @@ fn testNoErr(
209 results: []const Arg(u8), 217 results: []const Arg(u8),
210) !void { 218) !void {
211 var iter = args.SliceIterator{ .args = args_strings }; 219 var iter = args.SliceIterator{ .args = args_strings };
212 var c = StreamingClap(u8, args.SliceIterator){ 220 var c = Clap(u8, args.SliceIterator){
213 .params = params, 221 .params = params,
214 .iter = &iter, 222 .iter = &iter,
215 }; 223 };
@@ -236,7 +244,7 @@ fn testErr(
236) !void { 244) !void {
237 var diag: clap.Diagnostic = undefined; 245 var diag: clap.Diagnostic = undefined;
238 var iter = args.SliceIterator{ .args = args_strings }; 246 var iter = args.SliceIterator{ .args = args_strings };
239 var c = StreamingClap(u8, args.SliceIterator){ 247 var c = Clap(u8, args.SliceIterator){
240 .params = params, 248 .params = params,
241 .iter = &iter, 249 .iter = &iter,
242 .diagnostic = &diag, 250 .diagnostic = &diag,
diff --git a/example/README.md.template b/example/README.md.template
index 7f5c545..9fbc1cc 100644
--- a/example/README.md.template
+++ b/example/README.md.template
@@ -27,33 +27,24 @@ The simplest way to use this library is to just call the `clap.parse` function.
27{s} 27{s}
28``` 28```
29 29
30The data structure returned has lookup speed on par with array access (`arr[i]`) and validates 30The result will contain an `args` field and a `positionals` field. `args` will have one field
31that the strings you pass to `option`, `options` and `flag` are actually parameters that the 31for each none positional parameter of your program. The name of the field will be the longest
32program can take: 32name of the parameter.
33
34The fields in `args` are typed. The type is based on the name of the value the parameter takes.
35Since `--number` takes a `usize` the field `res.args.number` has the type `usize`.
36
37Note that this is only the case because `clap.parsers.default` has a field called `usize` which
38contains a parser that returns `usize`. You can pass in something other than `clap.parsers.default`
39if you want some other mapping.
33 40
34```zig 41```zig
35{s} 42{s}
36``` 43```
37 44
38``` 45### `streaming.Clap`
39zig-clap/clap/comptime.zig:109:17: error: --helps is not a parameter.
40 @compileError(name ++ " is not a parameter.");
41 ^
42zig-clap/clap/comptime.zig:77:45: note: called from here
43 const param = comptime findParam(name);
44 ^
45zig-clap/clap.zig:238:31: note: called from here
46 return a.clap.flag(name);
47 ^
48zig-clap/example/simple-error.zig:16:18: note: called from here
49 _ = args.flag("--helps");
50```
51
52There is also a `parseEx` variant that takes an argument iterator.
53 46
54### `StreamingClap` 47The `streaming.Clap` is the base of all the other parsers. It's a streaming parser that uses an
55
56The `StreamingClap` 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. 48`args.Iterator` to provide it with arguments lazily.
58 49
59```zig 50```zig
@@ -65,8 +56,9 @@ is generated at runtime.
65 56
66### `help` 57### `help`
67 58
68The `help`, `helpEx` and `helpFull` are functions for printing a simple list of all parameters the 59The `help` prints a simple list of all parameters the program can take. It expects the
69program can take. 60`Id` to have a `description` method and an `value` method so that it can provide that
61in the output.
70 62
71```zig 63```zig
72{s} 64{s}
@@ -78,19 +70,10 @@ $ zig-out/bin/help --help
78 -v, --version Output version information and exit. 70 -v, --version Output version information and exit.
79``` 71```
80 72
81The `help` functions are the simplest to call. It only takes an `OutStream` and a slice of
82`Param(Help)`.
83
84The `helpEx` is the generic version of `help`. It can print a help message for any
85`Param` give that the caller provides functions for getting the help and value strings.
86
87The `helpFull` is even more generic, allowing the functions that get the help and value strings
88to return errors and take a context as a parameter.
89
90### `usage` 73### `usage`
91 74
92The `usage`, `usageEx` and `usageFull` are functions for printing a small abbreviated version 75The `usage` prints a small abbreviated version of the help message. It expects the `Id`
93of the help message. 76to have a `value` method so it can provide that in the output.
94 77
95```zig 78```zig
96{s} 79{s}
diff --git a/example/help.zig b/example/help.zig
index de3b707..f3edb58 100644
--- a/example/help.zig
+++ b/example/help.zig
@@ -7,12 +7,12 @@ pub fn main() !void {
7 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 7 clap.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.parse(clap.Help, &params, clap.parsers.default, .{});
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(), clap.Help, &params);
18} 18}
diff --git a/example/simple-error.zig b/example/simple-error.zig
deleted file mode 100644
index c04a9c6..0000000
--- a/example/simple-error.zig
+++ /dev/null
@@ -1,13 +0,0 @@
1const clap = @import("clap");
2const std = @import("std");
3
4pub fn main() !void {
5 const params = comptime [_]clap.Param(clap.Help){
6 clap.parseParam("-h, --help Display this help and exit.") catch unreachable,
7 };
8
9 var args = try clap.parse(clap.Help, &params, .{});
10 defer args.deinit();
11
12 _ = args.flag("--helps");
13}
diff --git a/example/simple-ex.zig b/example/simple-ex.zig
index d2dc77e..6cb4c3f 100644
--- a/example/simple-ex.zig
+++ b/example/simple-ex.zig
@@ -6,43 +6,38 @@ const io = std.io;
6const process = std.process; 6const process = std.process;
7 7
8pub fn main() !void { 8pub fn main() !void {
9 const allocator = std.heap.page_allocator;
10
11 // First we specify what parameters our program can take. 9 // First we specify what parameters our program can take.
12 // We can use `parseParam` to parse a string to a `Param(Help)` 10 // We can use `parseParam` to parse a string to a `Param(Help)`
13 const params = comptime [_]clap.Param(clap.Help){ 11 const params = comptime [_]clap.Param(clap.Help){
14 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 12 clap.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, 13 clap.parseParam("-n, --number <INT> 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, 14 clap.parseParam("-s, --string <STR>... An option parameter which can be specified multiple times.") catch unreachable,
17 clap.parseParam("<POS>...") catch unreachable, 15 clap.parseParam("<FILE>...") catch unreachable,
18 }; 16 };
19 17
20 var iter = try process.ArgIterator.initWithAllocator(allocator); 18 // Declare our own parsers which are used to map the argument strings to other
21 defer iter.deinit(); 19 // types.
22 20 const parsers = comptime .{
23 // Skip exe argument 21 .STR = clap.parsers.string,
24 _ = iter.next(); 22 .FILE = clap.parsers.string,
23 .INT = clap.parsers.int(usize, 10),
24 };
25 25
26 // Initalize our diagnostics, which can be used for reporting useful errors.
27 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
28 // care about the extra information `Diagnostics` provides.
29 var diag = clap.Diagnostic{}; 26 var diag = clap.Diagnostic{};
30 var args = clap.parseEx(clap.Help, &params, &iter, .{ 27 var res = clap.parse(clap.Help, &params, parsers, .{
31 .allocator = allocator,
32 .diagnostic = &diag, 28 .diagnostic = &diag,
33 }) catch |err| { 29 }) catch |err| {
34 // Report useful error and exit
35 diag.report(io.getStdErr().writer(), err) catch {}; 30 diag.report(io.getStdErr().writer(), err) catch {};
36 return err; 31 return err;
37 }; 32 };
38 defer args.deinit(); 33 defer res.deinit();
39 34
40 if (args.flag("--help")) 35 if (res.args.help)
41 debug.print("--help\n", .{}); 36 debug.print("--help\n", .{});
42 if (args.option("--number")) |n| 37 if (res.args.number) |n|
43 debug.print("--number = {s}\n", .{n}); 38 debug.print("--number = {}\n", .{n});
44 for (args.options("--string")) |s| 39 for (res.args.string) |s|
45 debug.print("--string = {s}\n", .{s}); 40 debug.print("--string = {s}\n", .{s});
46 for (args.positionals()) |pos| 41 for (res.positionals) |pos|
47 debug.print("{s}\n", .{pos}); 42 debug.print("{s}\n", .{pos});
48} 43}
diff --git a/example/simple.zig b/example/simple.zig
index ff6d301..11e975e 100644
--- a/example/simple.zig
+++ b/example/simple.zig
@@ -8,29 +8,31 @@ 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.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.parseParam("-n, --number <usize> 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.parseParam("-s, --string <str>... An option parameter which can be specified multiple times.") catch unreachable,
14 clap.parseParam("<POS>...") catch unreachable, 14 clap.parseParam("<str>...") 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.parse(clap.Help, &params, clap.parsers.default, .{
22 .diagnostic = &diag,
23 }) catch |err| {
22 // Report useful error and exit 24 // Report useful error and exit
23 diag.report(io.getStdErr().writer(), err) catch {}; 25 diag.report(io.getStdErr().writer(), err) catch {};
24 return err; 26 return err;
25 }; 27 };
26 defer args.deinit(); 28 defer res.deinit();
27 29
28 if (args.flag("--help")) 30 if (res.args.help)
29 debug.print("--help\n", .{}); 31 debug.print("--help\n", .{});
30 if (args.option("--number")) |n| 32 if (res.args.number) |n|
31 debug.print("--number = {s}\n", .{n}); 33 debug.print("--number = {}\n", .{n});
32 for (args.options("--string")) |s| 34 for (res.args.string) |s|
33 debug.print("--string = {s}\n", .{s}); 35 debug.print("--string = {s}\n", .{s});
34 for (args.positionals()) |pos| 36 for (res.positionals) |pos|
35 debug.print("{s}\n", .{pos}); 37 debug.print("{s}\n", .{pos});
36} 38}
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..20d4736 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.parseParam("-h, --help Display this help and exit.") catch unreachable,
7 clap.parseParam("-v, --version Output version information and exit. ") catch unreachable, 7 clap.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.parseParam(" --value <str> 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.parse(clap.Help, &params, clap.parsers.default, .{});
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(), clap.Help, &params);
19} 19}