summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README.md64
-rw-r--r--build.zig4
-rw-r--r--clap.zig241
-rw-r--r--clap/comptime.zig39
-rw-r--r--clap/streaming.zig224
-rw-r--r--example/help.zig7
-rw-r--r--example/simple-error.zig4
-rw-r--r--example/simple-ex.zig19
-rw-r--r--example/simple.zig14
-rw-r--r--example/streaming-clap.zig35
-rw-r--r--example/usage.zig6
-rw-r--r--gyro.zzz14
-rw-r--r--zig.mod2
13 files changed, 300 insertions, 373 deletions
diff --git a/README.md b/README.md
index 23c2672..398a088 100644
--- a/README.md
+++ b/README.md
@@ -25,10 +25,11 @@ into master on every `zig` release.
25The simplest way to use this library is to just call the `clap.parse` function. 25The simplest way to use this library is to just call the `clap.parse` function.
26 26
27```zig 27```zig
28const std = @import("std");
29const clap = @import("clap"); 28const clap = @import("clap");
29const std = @import("std");
30 30
31const debug = std.debug; 31const debug = std.debug;
32const io = std.io;
32 33
33pub fn main() !void { 34pub fn main() !void {
34 // First we specify what parameters our program can take. 35 // First we specify what parameters our program can take.
@@ -41,13 +42,12 @@ pub fn main() !void {
41 }; 42 };
42 43
43 // Initalize our diagnostics, which can be used for reporting useful errors. 44 // Initalize our diagnostics, which can be used for reporting useful errors.
44 // This is optional. You can also just pass `null` to `parser.next` if you 45 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
45 // don't care about the extra information `Diagnostics` provides. 46 // care about the extra information `Diagnostics` provides.
46 var diag: clap.Diagnostic = undefined; 47 var diag = clap.Diagnostic{};
47 48 var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| {
48 var args = clap.parse(clap.Help, &params, std.heap.page_allocator, &diag) catch |err| {
49 // Report useful error and exit 49 // Report useful error and exit
50 diag.report(std.io.getStdErr().writer(), err) catch {}; 50 diag.report(io.getStdErr().writer(), err) catch {};
51 return err; 51 return err;
52 }; 52 };
53 defer args.deinit(); 53 defer args.deinit();
@@ -69,15 +69,15 @@ that the strings you pass to `option`, `options` and `flag` are actually paramet
69program can take: 69program can take:
70 70
71```zig 71```zig
72const std = @import("std");
73const clap = @import("clap"); 72const clap = @import("clap");
73const std = @import("std");
74 74
75pub fn main() !void { 75pub fn main() !void {
76 const params = comptime [_]clap.Param(clap.Help){ 76 const params = comptime [_]clap.Param(clap.Help){
77 clap.parseParam("-h, --help Display this help and exit.") catch unreachable, 77 clap.parseParam("-h, --help Display this help and exit.") catch unreachable,
78 }; 78 };
79 79
80 var args = try clap.parse(clap.Help, &params, std.heap.direct_allocator, null); 80 var args = try clap.parse(clap.Help, &params, .{});
81 defer args.deinit(); 81 defer args.deinit();
82 82
83 _ = args.flag("--helps"); 83 _ = args.flag("--helps");
@@ -107,29 +107,27 @@ The `StreamingClap` is the base of all the other parsers. It's a streaming parse
107`args.Iterator` to provide it with arguments lazily. 107`args.Iterator` to provide it with arguments lazily.
108 108
109```zig 109```zig
110const std = @import("std");
111const clap = @import("clap"); 110const clap = @import("clap");
111const std = @import("std");
112 112
113const debug = std.debug; 113const debug = std.debug;
114const io = std.io;
114 115
115pub fn main() !void { 116pub fn main() !void {
116 const allocator = std.heap.page_allocator; 117 const allocator = std.heap.page_allocator;
117 118
118 // First we specify what parameters our program can take. 119 // First we specify what parameters our program can take.
119 const params = [_]clap.Param(u8){ 120 const params = [_]clap.Param(u8){
120 clap.Param(u8){ 121 .{
121 .id = 'h', 122 .id = 'h',
122 .names = clap.Names{ .short = 'h', .long = "help" }, 123 .names = .{ .short = 'h', .long = "help" },
123 }, 124 },
124 clap.Param(u8){ 125 .{
125 .id = 'n', 126 .id = 'n',
126 .names = clap.Names{ .short = 'n', .long = "number" }, 127 .names = .{ .short = 'n', .long = "number" },
127 .takes_value = .One, 128 .takes_value = .one,
128 },
129 clap.Param(u8){
130 .id = 'f',
131 .takes_value = .One,
132 }, 129 },
130 .{ .id = 'f', .takes_value = .one },
133 }; 131 };
134 132
135 // We then initialize an argument iterator. We will use the OsIterator as it nicely 133 // We then initialize an argument iterator. We will use the OsIterator as it nicely
@@ -137,21 +135,20 @@ pub fn main() !void {
137 var iter = try clap.args.OsIterator.init(allocator); 135 var iter = try clap.args.OsIterator.init(allocator);
138 defer iter.deinit(); 136 defer iter.deinit();
139 137
140 // Initialize our streaming parser. 138 // Initalize our diagnostics, which can be used for reporting useful errors.
139 // This is optional. You can also leave the `diagnostic` field unset if you
140 // don't care about the extra information `Diagnostic` provides.
141 var diag = clap.Diagnostic{};
141 var parser = clap.StreamingClap(u8, clap.args.OsIterator){ 142 var parser = clap.StreamingClap(u8, clap.args.OsIterator){
142 .params = &params, 143 .params = &params,
143 .iter = &iter, 144 .iter = &iter,
145 .diagnostic = &diag,
144 }; 146 };
145 147
146 // Initalize our diagnostics, which can be used for reporting useful errors.
147 // This is optional. You can also just pass `null` to `parser.next` if you
148 // don't care about the extra information `Diagnostics` provides.
149 var diag: clap.Diagnostic = undefined;
150
151 // Because we use a streaming parser, we have to consume each argument parsed individually. 148 // Because we use a streaming parser, we have to consume each argument parsed individually.
152 while (parser.next(&diag) catch |err| { 149 while (parser.next() catch |err| {
153 // Report useful error and exit 150 // Report useful error and exit
154 diag.report(std.io.getStdErr().writer(), err) catch {}; 151 diag.report(io.getStdErr().writer(), err) catch {};
155 return err; 152 return err;
156 }) |arg| { 153 }) |arg| {
157 // arg.param will point to the parameter which matched the argument. 154 // arg.param will point to the parameter which matched the argument.
@@ -179,18 +176,15 @@ The `help`, `helpEx` and `helpFull` are functions for printing a simple list of
179program can take. 176program can take.
180 177
181```zig 178```zig
182const std = @import("std");
183const clap = @import("clap"); 179const clap = @import("clap");
180const std = @import("std");
184 181
185pub fn main() !void { 182pub fn main() !void {
186 const stderr_file = std.io.getStdErr();
187 var stderr_out_stream = stderr_file.writer();
188
189 // clap.help is a function that can print a simple help message, given a 183 // clap.help is a function that can print a simple help message, given a
190 // slice of Param(Help). There is also a helpEx, which can print a 184 // slice of Param(Help). There is also a helpEx, which can print a
191 // help message for any Param, but it is more verbose to call. 185 // help message for any Param, but it is more verbose to call.
192 try clap.help( 186 try clap.help(
193 stderr_out_stream, 187 std.io.getStdErr().writer(),
194 comptime &[_]clap.Param(clap.Help){ 188 comptime &[_]clap.Param(clap.Help){
195 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 189 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
196 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 190 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
@@ -220,17 +214,15 @@ The `usage`, `usageEx` and `usageFull` are functions for printing a small abbrev
220of the help message. 214of the help message.
221 215
222```zig 216```zig
223const std = @import("std");
224const clap = @import("clap"); 217const clap = @import("clap");
218const std = @import("std");
225 219
226pub fn main() !void { 220pub fn main() !void {
227 const stderr = std.io.getStdErr().writer();
228
229 // clap.usage is a function that can print a simple usage message, given a 221 // clap.usage is a function that can print a simple usage message, given a
230 // slice of Param(Help). There is also a usageEx, which can print a 222 // slice of Param(Help). There is also a usageEx, which can print a
231 // usage message for any Param, but it is more verbose to call. 223 // usage message for any Param, but it is more verbose to call.
232 try clap.usage( 224 try clap.usage(
233 stderr, 225 std.io.getStdErr().writer(),
234 comptime &[_]clap.Param(clap.Help){ 226 comptime &[_]clap.Param(clap.Help){
235 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 227 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
236 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 228 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
diff --git a/build.zig b/build.zig
index 623140d..bef10be 100644
--- a/build.zig
+++ b/build.zig
@@ -1,8 +1,8 @@
1const builtin = @import("builtin"); 1const builtin = @import("builtin");
2const std = @import("std"); 2const std = @import("std");
3 3
4const Mode = builtin.Mode;
5const Builder = std.build.Builder; 4const Builder = std.build.Builder;
5const Mode = builtin.Mode;
6 6
7pub fn build(b: *Builder) void { 7pub fn build(b: *Builder) void {
8 const mode = b.standardReleaseOptions(); 8 const mode = b.standardReleaseOptions();
@@ -58,7 +58,7 @@ fn readMeStep(b: *Builder) *std.build.Step {
58 fn make(step: *std.build.Step) anyerror!void { 58 fn make(step: *std.build.Step) anyerror!void {
59 @setEvalBranchQuota(10000); 59 @setEvalBranchQuota(10000);
60 const file = try std.fs.cwd().createFile("README.md", .{}); 60 const file = try std.fs.cwd().createFile("README.md", .{});
61 const stream = &file.writer(); 61 const stream = file.writer();
62 try stream.print(@embedFile("example/README.md.template"), .{ 62 try stream.print(@embedFile("example/README.md.template"), .{
63 @embedFile("example/simple.zig"), 63 @embedFile("example/simple.zig"),
64 @embedFile("example/simple-error.zig"), 64 @embedFile("example/simple-error.zig"),
diff --git a/clap.zig b/clap.zig
index be2153f..8d721fa 100644
--- a/clap.zig
+++ b/clap.zig
@@ -1,6 +1,7 @@
1const std = @import("std"); 1const std = @import("std");
2 2
3const debug = std.debug; 3const debug = std.debug;
4const heap = std.heap;
4const io = std.io; 5const io = std.io;
5const mem = std.mem; 6const mem = std.mem;
6const testing = std.testing; 7const testing = std.testing;
@@ -25,9 +26,9 @@ pub const Names = struct {
25 26
26/// Whether a param takes no value (a flag), one value, or can be specified multiple times. 27/// Whether a param takes no value (a flag), one value, or can be specified multiple times.
27pub const Values = enum { 28pub const Values = enum {
28 None, 29 none,
29 One, 30 one,
30 Many, 31 many,
31}; 32};
32 33
33/// Represents a parameter for the command line. 34/// Represents a parameter for the command line.
@@ -55,7 +56,7 @@ pub fn Param(comptime Id: type) type {
55 return struct { 56 return struct {
56 id: Id = Id{}, 57 id: Id = Id{},
57 names: Names = Names{}, 58 names: Names = Names{},
58 takes_value: Values = .None, 59 takes_value: Values = .none,
59 }; 60 };
60} 61}
61 62
@@ -109,8 +110,8 @@ fn parseParamRest(line: []const u8) Param(Help) {
109 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk; 110 const len = mem.indexOfScalar(u8, line, '>') orelse break :blk;
110 const takes_many = mem.startsWith(u8, line[len + 1 ..], "..."); 111 const takes_many = mem.startsWith(u8, line[len + 1 ..], "...");
111 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many); 112 const help_start = len + 1 + @as(usize, 3) * @boolToInt(takes_many);
112 return Param(Help){ 113 return .{
113 .takes_value = if (takes_many) .Many else .One, 114 .takes_value = if (takes_many) .many else .one,
114 .id = .{ 115 .id = .{
115 .msg = mem.trim(u8, line[help_start..], " \t"), 116 .msg = mem.trim(u8, line[help_start..], " \t"),
116 .value = line[1..len], 117 .value = line[1..len],
@@ -118,7 +119,7 @@ fn parseParamRest(line: []const u8) Param(Help) {
118 }; 119 };
119 } 120 }
120 121
121 return Param(Help){ .id = .{ .msg = mem.trim(u8, line, " \t") } }; 122 return .{ .id = .{ .msg = mem.trim(u8, line, " \t") } };
122} 123}
123 124
124fn expectParam(expect: Param(Help), actual: Param(Help)) void { 125fn expectParam(expect: Param(Help), actual: Param(Help)) void {
@@ -135,114 +136,60 @@ fn expectParam(expect: Param(Help), actual: Param(Help)) void {
135 136
136test "parseParam" { 137test "parseParam" {
137 expectParam(Param(Help){ 138 expectParam(Param(Help){
138 .id = Help{ 139 .id = .{ .msg = "Help text", .value = "value" },
139 .msg = "Help text", 140 .names = .{ .short = 's', .long = "long" },
140 .value = "value", 141 .takes_value = .one,
141 },
142 .names = Names{
143 .short = 's',
144 .long = "long",
145 },
146 .takes_value = .One,
147 }, try parseParam("-s, --long <value> Help text")); 142 }, try parseParam("-s, --long <value> Help text"));
143
148 expectParam(Param(Help){ 144 expectParam(Param(Help){
149 .id = Help{ 145 .id = .{ .msg = "Help text", .value = "value" },
150 .msg = "Help text", 146 .names = .{ .short = 's', .long = "long" },
151 .value = "value", 147 .takes_value = .many,
152 },
153 .names = Names{
154 .short = 's',
155 .long = "long",
156 },
157 .takes_value = .Many,
158 }, try parseParam("-s, --long <value>... Help text")); 148 }, try parseParam("-s, --long <value>... Help text"));
149
159 expectParam(Param(Help){ 150 expectParam(Param(Help){
160 .id = Help{ 151 .id = .{ .msg = "Help text", .value = "value" },
161 .msg = "Help text", 152 .names = .{ .long = "long" },
162 .value = "value", 153 .takes_value = .one,
163 },
164 .names = Names{
165 .short = null,
166 .long = "long",
167 },
168 .takes_value = .One,
169 }, try parseParam("--long <value> Help text")); 154 }, try parseParam("--long <value> Help text"));
155
170 expectParam(Param(Help){ 156 expectParam(Param(Help){
171 .id = Help{ 157 .id = .{ .msg = "Help text", .value = "value" },
172 .msg = "Help text", 158 .names = .{ .short = 's' },
173 .value = "value", 159 .takes_value = .one,
174 },
175 .names = Names{
176 .short = 's',
177 .long = null,
178 },
179 .takes_value = .One,
180 }, try parseParam("-s <value> Help text")); 160 }, try parseParam("-s <value> Help text"));
161
181 expectParam(Param(Help){ 162 expectParam(Param(Help){
182 .id = Help{ 163 .id = .{ .msg = "Help text" },
183 .msg = "Help text", 164 .names = .{ .short = 's', .long = "long" },
184 .value = "",
185 },
186 .names = Names{
187 .short = 's',
188 .long = "long",
189 },
190 .takes_value = .None,
191 }, try parseParam("-s, --long Help text")); 165 }, try parseParam("-s, --long Help text"));
166
192 expectParam(Param(Help){ 167 expectParam(Param(Help){
193 .id = Help{ 168 .id = .{ .msg = "Help text" },
194 .msg = "Help text", 169 .names = .{ .short = 's' },
195 .value = "",
196 },
197 .names = Names{
198 .short = 's',
199 .long = null,
200 },
201 .takes_value = .None,
202 }, try parseParam("-s Help text")); 170 }, try parseParam("-s Help text"));
171
203 expectParam(Param(Help){ 172 expectParam(Param(Help){
204 .id = Help{ 173 .id = .{ .msg = "Help text" },
205 .msg = "Help text", 174 .names = .{ .long = "long" },
206 .value = "",
207 },
208 .names = Names{
209 .short = null,
210 .long = "long",
211 },
212 .takes_value = .None,
213 }, try parseParam("--long Help text")); 175 }, try parseParam("--long Help text"));
176
214 expectParam(Param(Help){ 177 expectParam(Param(Help){
215 .id = Help{ 178 .id = .{ .msg = "Help text", .value = "A | B" },
216 .msg = "Help text", 179 .names = .{ .long = "long" },
217 .value = "A | B", 180 .takes_value = .one,
218 },
219 .names = Names{
220 .short = null,
221 .long = "long",
222 },
223 .takes_value = .One,
224 }, try parseParam("--long <A | B> Help text")); 181 }, try parseParam("--long <A | B> Help text"));
182
225 expectParam(Param(Help){ 183 expectParam(Param(Help){
226 .id = Help{ 184 .id = .{ .msg = "Help text", .value = "A" },
227 .msg = "Help text", 185 .names = .{},
228 .value = "A", 186 .takes_value = .one,
229 },
230 .names = Names{
231 .short = null,
232 .long = null,
233 },
234 .takes_value = .One,
235 }, try parseParam("<A> Help text")); 187 }, try parseParam("<A> Help text"));
188
236 expectParam(Param(Help){ 189 expectParam(Param(Help){
237 .id = Help{ 190 .id = .{ .msg = "Help text", .value = "A" },
238 .msg = "Help text", 191 .names = .{},
239 .value = "A", 192 .takes_value = .many,
240 },
241 .names = Names{
242 .short = null,
243 .long = null,
244 },
245 .takes_value = .Many,
246 }, try parseParam("<A>... Help text")); 193 }, try parseParam("<A>... Help text"));
247 194
248 testing.expectError(error.TrailingComma, parseParam("--long, Help")); 195 testing.expectError(error.TrailingComma, parseParam("--long, Help"));
@@ -307,7 +254,6 @@ pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type {
307 exe_arg: ?[]const u8, 254 exe_arg: ?[]const u8,
308 255
309 pub fn deinit(a: *@This()) void { 256 pub fn deinit(a: *@This()) void {
310 a.clap.deinit();
311 a.arena.deinit(); 257 a.arena.deinit();
312 } 258 }
313 259
@@ -329,20 +275,37 @@ pub fn Args(comptime Id: type, comptime params: []const Param(Id)) type {
329 }; 275 };
330} 276}
331 277
278/// Options that can be set to customize the behavior of parsing.
279pub const ParseOptions = struct {
280 /// The allocator used for all memory allocations. Defaults to the `heap.page_allocator`.
281 /// Note: You should probably override this allocator if you are calling `parseEx`. Unlike
282 /// `parse`, `parseEx` does not wrap the allocator so the heap allocator can be
283 /// quite expensive. (TODO: Can we pick a better default? For `parse`, this allocator
284 /// is fine, as it wraps it in an arena)
285 allocator: *mem.Allocator = heap.page_allocator,
286 diagnostic: ?*Diagnostic = null,
287};
288
332/// Same as `parseEx` but uses the `args.OsIterator` by default. 289/// Same as `parseEx` but uses the `args.OsIterator` by default.
333pub fn parse( 290pub fn parse(
334 comptime Id: type, 291 comptime Id: type,
335 comptime params: []const Param(Id), 292 comptime params: []const Param(Id),
336 allocator: *mem.Allocator, 293 opt: ParseOptions,
337 diag: ?*Diagnostic,
338) !Args(Id, params) { 294) !Args(Id, params) {
339 var iter = try args.OsIterator.init(allocator); 295 var iter = try args.OsIterator.init(opt.allocator);
340 const clap = try parseEx(Id, params, allocator, &iter, diag); 296 var res = Args(Id, params){
341 return Args(Id, params){
342 .arena = iter.arena, 297 .arena = iter.arena,
343 .clap = clap,
344 .exe_arg = iter.exe_arg, 298 .exe_arg = iter.exe_arg,
299 .clap = undefined,
345 }; 300 };
301
302 // Let's reuse the arena from the `OSIterator` since we already have
303 // it.
304 res.clap = try parseEx(Id, params, &iter, .{
305 .allocator = &res.arena.allocator,
306 .diagnostic = opt.diagnostic,
307 });
308 return res;
346} 309}
347 310
348/// Parses the command line arguments passed into the program based on an 311/// Parses the command line arguments passed into the program based on an
@@ -350,12 +313,11 @@ pub fn parse(
350pub fn parseEx( 313pub fn parseEx(
351 comptime Id: type, 314 comptime Id: type,
352 comptime params: []const Param(Id), 315 comptime params: []const Param(Id),
353 allocator: *mem.Allocator,
354 iter: anytype, 316 iter: anytype,
355 diag: ?*Diagnostic, 317 opt: ParseOptions,
356) !ComptimeClap(Id, params) { 318) !ComptimeClap(Id, params) {
357 const Clap = ComptimeClap(Id, params); 319 const Clap = ComptimeClap(Id, params);
358 return try Clap.parse(allocator, iter, diag); 320 return try Clap.parse(iter, opt);
359} 321}
360 322
361/// Will print a help message in the following format: 323/// Will print a help message in the following format:
@@ -376,10 +338,10 @@ pub fn helpFull(
376 const max_spacing = blk: { 338 const max_spacing = blk: {
377 var res: usize = 0; 339 var res: usize = 0;
378 for (params) |param| { 340 for (params) |param| {
379 var counting_stream = io.countingWriter(io.null_writer); 341 var cs = io.countingOutStream(io.null_writer);
380 try printParam(counting_stream.writer(), Id, param, Error, context, valueText); 342 try printParam(cs.writer(), Id, param, Error, context, valueText);
381 if (res < counting_stream.bytes_written) 343 if (res < cs.bytes_written)
382 res = @intCast(usize, counting_stream.bytes_written); 344 res = @intCast(usize, cs.bytes_written);
383 } 345 }
384 346
385 break :blk res; 347 break :blk res;
@@ -389,11 +351,11 @@ pub fn helpFull(
389 if (param.names.short == null and param.names.long == null) 351 if (param.names.short == null and param.names.long == null)
390 continue; 352 continue;
391 353
392 var counting_stream = io.countingWriter(stream); 354 var cs = io.countingWriter(stream);
393 try stream.print("\t", .{}); 355 try stream.print("\t", .{});
394 try printParam(counting_stream.writer(), Id, param, Error, context, valueText); 356 try printParam(cs.writer(), Id, param, Error, context, valueText);
395 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, counting_stream.bytes_written)); 357 try stream.writeByteNTimes(' ', max_spacing - @intCast(usize, cs.bytes_written));
396 try stream.print("\t{s}\n", .{try helpText(context, param)}); 358 try stream.print("\t{}\n", .{try helpText(context, param)});
397 } 359 }
398} 360}
399 361
@@ -421,9 +383,9 @@ fn printParam(
421 } 383 }
422 384
423 switch (param.takes_value) { 385 switch (param.takes_value) {
424 .None => {}, 386 .none => {},
425 .One => try stream.print(" <{s}>", .{valueText(context, param)}), 387 .one => try stream.print(" <{s}>", .{valueText(context, param)}),
426 .Many => try stream.print(" <{s}>...", .{valueText(context, param)}), 388 .many => try stream.print(" <{s}>...", .{valueText(context, param)}),
427 } 389 }
428} 390}
429 391
@@ -489,19 +451,14 @@ test "clap.help" {
489 try help( 451 try help(
490 slice_stream.writer(), 452 slice_stream.writer(),
491 comptime &[_]Param(Help){ 453 comptime &[_]Param(Help){
492 parseParam("-a Short flag. ") catch unreachable, 454 parseParam("-a Short flag.") catch unreachable,
493 parseParam("-b <V1> Short option.") catch unreachable, 455 parseParam("-b <V1> Short option.") catch unreachable,
494 parseParam("--aa Long flag. ") catch unreachable, 456 parseParam("--aa Long flag.") catch unreachable,
495 parseParam("--bb <V2> Long option. ") catch unreachable, 457 parseParam("--bb <V2> Long option.") catch unreachable,
496 parseParam("-c, --cc Both flag. ") catch unreachable, 458 parseParam("-c, --cc Both flag.") catch unreachable,
497 parseParam("-d, --dd <V3> Both option. ") catch unreachable, 459 parseParam("-d, --dd <V3> Both option.") catch unreachable,
498 parseParam("-d, --dd <V3>... Both repeated option. ") catch unreachable, 460 parseParam("-d, --dd <V3>... Both repeated option.") catch unreachable,
499 Param(Help){ 461 parseParam("<P> Positional. This should not appear in the help message.") catch unreachable,
500 .id = Help{
501 .msg = "Positional. This should not appear in the help message.",
502 },
503 .takes_value = .One,
504 },
505 }, 462 },
506 ); 463 );
507 464
@@ -534,7 +491,7 @@ pub fn usageFull(
534 const cs = cos.writer(); 491 const cs = cos.writer();
535 for (params) |param| { 492 for (params) |param| {
536 const name = param.names.short orelse continue; 493 const name = param.names.short orelse continue;
537 if (param.takes_value != .None) 494 if (param.takes_value != .none)
538 continue; 495 continue;
539 496
540 if (cos.bytes_written == 0) 497 if (cos.bytes_written == 0)
@@ -546,7 +503,7 @@ pub fn usageFull(
546 503
547 var positional: ?Param(Id) = null; 504 var positional: ?Param(Id) = null;
548 for (params) |param| { 505 for (params) |param| {
549 if (param.takes_value == .None and param.names.short != null) 506 if (param.takes_value == .none and param.names.short != null)
550 continue; 507 continue;
551 508
552 const prefix = if (param.names.short) |_| "-" else "--"; 509 const prefix = if (param.names.short) |_| "-" else "--";
@@ -562,9 +519,9 @@ pub fn usageFull(
562 519
563 try cs.print("[{s}{s}", .{ prefix, name }); 520 try cs.print("[{s}{s}", .{ prefix, name });
564 switch (param.takes_value) { 521 switch (param.takes_value) {
565 .None => {}, 522 .none => {},
566 .One => try cs.print(" <{s}>", .{try valueText(context, param)}), 523 .one => try cs.print(" <{s}>", .{try valueText(context, param)}),
567 .Many => try cs.print(" <{s}>...", .{try valueText(context, param)}), 524 .many => try cs.print(" <{s}>...", .{try valueText(context, param)}),
568 } 525 }
569 526
570 try cs.writeByte(']'); 527 try cs.writeByte(']');
@@ -634,12 +591,7 @@ test "usage" {
634 parseParam("--b <v>") catch unreachable, 591 parseParam("--b <v>") catch unreachable,
635 }); 592 });
636 try testUsage("<file>", comptime &[_]Param(Help){ 593 try testUsage("<file>", comptime &[_]Param(Help){
637 Param(Help){ 594 parseParam("<file>") catch unreachable,
638 .id = Help{
639 .value = "file",
640 },
641 .takes_value = .One,
642 },
643 }); 595 });
644 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", comptime &[_]Param(Help){ 596 try testUsage("[-ab] [-c <value>] [-d <v>] [--e] [--f] [--g <value>] [--h <v>] [-i <v>...] <file>", comptime &[_]Param(Help){
645 parseParam("-a") catch unreachable, 597 parseParam("-a") catch unreachable,
@@ -651,11 +603,6 @@ test "usage" {
651 parseParam("--g <value>") catch unreachable, 603 parseParam("--g <value>") catch unreachable,
652 parseParam("--h <v>") catch unreachable, 604 parseParam("--h <v>") catch unreachable,
653 parseParam("-i <v>...") catch unreachable, 605 parseParam("-i <v>...") catch unreachable,
654 Param(Help){ 606 parseParam("<file>") catch unreachable,
655 .id = Help{
656 .value = "file",
657 },
658 .takes_value = .One,
659 },
660 }); 607 });
661} 608}
diff --git a/clap/comptime.zig b/clap/comptime.zig
index 8ab61cb..122ff16 100644
--- a/clap/comptime.zig
+++ b/clap/comptime.zig
@@ -1,10 +1,10 @@
1const clap = @import("../clap.zig"); 1const clap = @import("../clap.zig");
2const std = @import("std"); 2const std = @import("std");
3 3
4const testing = std.testing; 4const debug = std.debug;
5const heap = std.heap; 5const heap = std.heap;
6const mem = std.mem; 6const mem = std.mem;
7const debug = std.debug; 7const testing = std.testing;
8 8
9/// Deprecated: Use `parseEx` instead 9/// Deprecated: Use `parseEx` instead
10pub fn ComptimeClap( 10pub fn ComptimeClap(
@@ -19,9 +19,9 @@ pub fn ComptimeClap(
19 var index: usize = 0; 19 var index: usize = 0;
20 if (param.names.long != null or param.names.short != null) { 20 if (param.names.long != null or param.names.short != null) {
21 const ptr = switch (param.takes_value) { 21 const ptr = switch (param.takes_value) {
22 .None => &flags, 22 .none => &flags,
23 .One => &single_options, 23 .one => &single_options,
24 .Many => &multi_options, 24 .many => &multi_options,
25 }; 25 };
26 index = ptr.*; 26 index = ptr.*;
27 ptr.* += 1; 27 ptr.* += 1;
@@ -42,7 +42,8 @@ pub fn ComptimeClap(
42 pos: []const []const u8, 42 pos: []const []const u8,
43 allocator: *mem.Allocator, 43 allocator: *mem.Allocator,
44 44
45 pub fn parse(allocator: *mem.Allocator, iter: anytype, diag: ?*clap.Diagnostic) !@This() { 45 pub fn parse(iter: anytype, opt: clap.ParseOptions) !@This() {
46 const allocator = opt.allocator;
46 var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options; 47 var multis = [_]std.ArrayList([]const u8){undefined} ** multi_options;
47 for (multis) |*multi| { 48 for (multis) |*multi| {
48 multi.* = std.ArrayList([]const u8).init(allocator); 49 multi.* = std.ArrayList([]const u8).init(allocator);
@@ -62,15 +63,15 @@ pub fn ComptimeClap(
62 .params = converted_params, 63 .params = converted_params,
63 .iter = iter, 64 .iter = iter,
64 }; 65 };
65 while (try stream.next(diag)) |arg| { 66 while (try stream.next()) |arg| {
66 const param = arg.param; 67 const param = arg.param;
67 if (param.names.long == null and param.names.short == null) { 68 if (param.names.long == null and param.names.short == null) {
68 try pos.append(arg.value.?); 69 try pos.append(arg.value.?);
69 } else if (param.takes_value == .One) { 70 } else if (param.takes_value == .one) {
70 debug.assert(res.single_options.len != 0); 71 debug.assert(res.single_options.len != 0);
71 if (res.single_options.len != 0) 72 if (res.single_options.len != 0)
72 res.single_options[param.id] = arg.value.?; 73 res.single_options[param.id] = arg.value.?;
73 } else if (param.takes_value == .Many) { 74 } else if (param.takes_value == .many) {
74 debug.assert(multis.len != 0); 75 debug.assert(multis.len != 0);
75 if (multis.len != 0) 76 if (multis.len != 0)
76 try multis[param.id].append(arg.value.?); 77 try multis[param.id].append(arg.value.?);
@@ -81,24 +82,22 @@ pub fn ComptimeClap(
81 } 82 }
82 } 83 }
83 84
84 for (multis) |*multi, i| { 85 for (multis) |*multi, i|
85 res.multi_options[i] = multi.toOwnedSlice(); 86 res.multi_options[i] = multi.toOwnedSlice();
86 }
87 res.pos = pos.toOwnedSlice(); 87 res.pos = pos.toOwnedSlice();
88 88
89 return res; 89 return res;
90 } 90 }
91 91
92 pub fn deinit(parser: *@This()) void { 92 pub fn deinit(parser: @This()) void {
93 for (parser.multi_options) |o| 93 for (parser.multi_options) |o|
94 parser.allocator.free(o); 94 parser.allocator.free(o);
95 parser.allocator.free(parser.pos); 95 parser.allocator.free(parser.pos);
96 parser.* = undefined;
97 } 96 }
98 97
99 pub fn flag(parser: @This(), comptime name: []const u8) bool { 98 pub fn flag(parser: @This(), comptime name: []const u8) bool {
100 const param = comptime findParam(name); 99 const param = comptime findParam(name);
101 if (param.takes_value != .None) 100 if (param.takes_value != .none)
102 @compileError(name ++ " is an option and not a flag."); 101 @compileError(name ++ " is an option and not a flag.");
103 102
104 return parser.flags[param.id]; 103 return parser.flags[param.id];
@@ -106,18 +105,18 @@ pub fn ComptimeClap(
106 105
107 pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 { 106 pub fn option(parser: @This(), comptime name: []const u8) ?[]const u8 {
108 const param = comptime findParam(name); 107 const param = comptime findParam(name);
109 if (param.takes_value == .None) 108 if (param.takes_value == .none)
110 @compileError(name ++ " is a flag and not an option."); 109 @compileError(name ++ " is a flag and not an option.");
111 if (param.takes_value == .Many) 110 if (param.takes_value == .many)
112 @compileError(name ++ " takes many options, not one."); 111 @compileError(name ++ " takes many options, not one.");
113 return parser.single_options[param.id]; 112 return parser.single_options[param.id];
114 } 113 }
115 114
116 pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 { 115 pub fn options(parser: @This(), comptime name: []const u8) []const []const u8 {
117 const param = comptime findParam(name); 116 const param = comptime findParam(name);
118 if (param.takes_value == .None) 117 if (param.takes_value == .none)
119 @compileError(name ++ " is a flag and not an option."); 118 @compileError(name ++ " is a flag and not an option.");
120 if (param.takes_value == .One) 119 if (param.takes_value == .one)
121 @compileError(name ++ " takes one option, not multiple."); 120 @compileError(name ++ " takes one option, not multiple.");
122 121
123 return parser.multi_options[param.id]; 122 return parser.multi_options[param.id];
@@ -155,14 +154,12 @@ test "" {
155 clap.parseParam("<P>") catch unreachable, 154 clap.parseParam("<P>") catch unreachable,
156 }); 155 });
157 156
158 var buf: [1024]u8 = undefined;
159 var fb_allocator = heap.FixedBufferAllocator.init(buf[0..]);
160 var iter = clap.args.SliceIterator{ 157 var iter = clap.args.SliceIterator{
161 .args = &[_][]const u8{ 158 .args = &[_][]const u8{
162 "-a", "-c", "0", "something", "-d", "a", "--dd", "b", 159 "-a", "-c", "0", "something", "-d", "a", "--dd", "b",
163 }, 160 },
164 }; 161 };
165 var args = try Clap.parse(&fb_allocator.allocator, &iter, null); 162 var args = try Clap.parse(&iter, .{ .allocator = testing.allocator });
166 defer args.deinit(); 163 defer args.deinit();
167 164
168 testing.expect(args.flag("-a")); 165 testing.expect(args.flag("-a"));
diff --git a/clap/streaming.zig b/clap/streaming.zig
index 0fe5aae..a2a0ca8 100644
--- a/clap/streaming.zig
+++ b/clap/streaming.zig
@@ -40,12 +40,13 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
40 iter: *ArgIterator, 40 iter: *ArgIterator,
41 state: State = .normal, 41 state: State = .normal,
42 positional: ?*const clap.Param(Id) = null, 42 positional: ?*const clap.Param(Id) = null,
43 diagnostic: ?*clap.Diagnostic = null,
43 44
44 /// Get the next Arg that matches a Param. 45 /// Get the next Arg that matches a Param.
45 pub fn next(parser: *@This(), diag: ?*clap.Diagnostic) !?Arg(Id) { 46 pub fn next(parser: *@This()) !?Arg(Id) {
46 switch (parser.state) { 47 switch (parser.state) {
47 .normal => return try parser.normal(diag), 48 .normal => return try parser.normal(),
48 .chaining => |state| return try parser.chainging(state, diag), 49 .chaining => |state| return try parser.chainging(state),
49 .rest_are_positional => { 50 .rest_are_positional => {
50 const param = parser.positionalParam() orelse unreachable; 51 const param = parser.positionalParam() orelse unreachable;
51 const value = (try parser.iter.next()) orelse return null; 52 const value = (try parser.iter.next()) orelse return null;
@@ -54,7 +55,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
54 } 55 }
55 } 56 }
56 57
57 fn normal(parser: *@This(), diag: ?*clap.Diagnostic) !?Arg(Id) { 58 fn normal(parser: *@This()) !?Arg(Id) {
58 const arg_info = (try parser.parseNextArg()) orelse return null; 59 const arg_info = (try parser.parseNextArg()) orelse return null;
59 const arg = arg_info.arg; 60 const arg = arg_info.arg;
60 switch (arg_info.kind) { 61 switch (arg_info.kind) {
@@ -68,9 +69,9 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
68 69
69 if (!mem.eql(u8, name, match)) 70 if (!mem.eql(u8, name, match))
70 continue; 71 continue;
71 if (param.takes_value == .None) { 72 if (param.takes_value == .none) {
72 if (maybe_value != null) 73 if (maybe_value != null)
73 return err(diag, arg, .{ .long = name }, error.DoesntTakeValue); 74 return parser.err(arg, .{ .long = name }, error.DoesntTakeValue);
74 75
75 return Arg(Id){ .param = param }; 76 return Arg(Id){ .param = param };
76 } 77 }
@@ -80,18 +81,18 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
80 break :blk v; 81 break :blk v;
81 82
82 break :blk (try parser.iter.next()) orelse 83 break :blk (try parser.iter.next()) orelse
83 return err(diag, arg, .{ .long = name }, error.MissingValue); 84 return parser.err(arg, .{ .long = name }, error.MissingValue);
84 }; 85 };
85 86
86 return Arg(Id){ .param = param, .value = value }; 87 return Arg(Id){ .param = param, .value = value };
87 } 88 }
88 89
89 return err(diag, arg, .{ .long = name }, error.InvalidArgument); 90 return parser.err(arg, .{ .long = name }, error.InvalidArgument);
90 }, 91 },
91 .short => return try parser.chainging(.{ 92 .short => return try parser.chainging(.{
92 .arg = arg, 93 .arg = arg,
93 .index = 0, 94 .index = 0,
94 }, diag), 95 }),
95 .positional => if (parser.positionalParam()) |param| { 96 .positional => if (parser.positionalParam()) |param| {
96 // If we find a positional with the value `--` then we 97 // If we find a positional with the value `--` then we
97 // interpret the rest of the arguments as positional 98 // interpret the rest of the arguments as positional
@@ -104,12 +105,12 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
104 105
105 return Arg(Id){ .param = param, .value = arg }; 106 return Arg(Id){ .param = param, .value = arg };
106 } else { 107 } else {
107 return err(diag, arg, .{}, error.InvalidArgument); 108 return parser.err(arg, .{}, error.InvalidArgument);
108 }, 109 },
109 } 110 }
110 } 111 }
111 112
112 fn chainging(parser: *@This(), state: State.Chaining, diag: ?*clap.Diagnostic) !?Arg(Id) { 113 fn chainging(parser: *@This(), state: State.Chaining) !?Arg(Id) {
113 const arg = state.arg; 114 const arg = state.arg;
114 const index = state.index; 115 const index = state.index;
115 const next_index = index + 1; 116 const next_index = index + 1;
@@ -121,7 +122,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
121 122
122 // Before we return, we have to set the new state of the clap 123 // Before we return, we have to set the new state of the clap
123 defer { 124 defer {
124 if (arg.len <= next_index or param.takes_value != .None) { 125 if (arg.len <= next_index or param.takes_value != .none) {
125 parser.state = .normal; 126 parser.state = .normal;
126 } else { 127 } else {
127 parser.state = .{ 128 parser.state = .{
@@ -134,15 +135,15 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
134 } 135 }
135 136
136 const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false; 137 const next_is_eql = if (next_index < arg.len) arg[next_index] == '=' else false;
137 if (param.takes_value == .None) { 138 if (param.takes_value == .none) {
138 if (next_is_eql) 139 if (next_is_eql)
139 return err(diag, arg, .{ .short = short }, error.DoesntTakeValue); 140 return parser.err(arg, .{ .short = short }, error.DoesntTakeValue);
140 return Arg(Id){ .param = param }; 141 return Arg(Id){ .param = param };
141 } 142 }
142 143
143 if (arg.len <= next_index) { 144 if (arg.len <= next_index) {
144 const value = (try parser.iter.next()) orelse 145 const value = (try parser.iter.next()) orelse
145 return err(diag, arg, .{ .short = short }, error.MissingValue); 146 return parser.err(arg, .{ .short = short }, error.MissingValue);
146 147
147 return Arg(Id){ .param = param, .value = value }; 148 return Arg(Id){ .param = param, .value = value };
148 } 149 }
@@ -153,7 +154,7 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
153 return Arg(Id){ .param = param, .value = arg[next_index..] }; 154 return Arg(Id){ .param = param, .value = arg[next_index..] };
154 } 155 }
155 156
156 return err(diag, arg, .{ .short = arg[index] }, error.InvalidArgument); 157 return parser.err(arg, .{ .short = arg[index] }, error.InvalidArgument);
157 } 158 }
158 159
159 fn positionalParam(parser: *@This()) ?*const clap.Param(Id) { 160 fn positionalParam(parser: *@This()) ?*const clap.Param(Id) {
@@ -194,8 +195,8 @@ pub fn StreamingClap(comptime Id: type, comptime ArgIterator: type) type {
194 return ArgInfo{ .arg = full_arg, .kind = .positional }; 195 return ArgInfo{ .arg = full_arg, .kind = .positional };
195 } 196 }
196 197
197 fn err(diag: ?*clap.Diagnostic, arg: []const u8, names: clap.Names, _err: anytype) @TypeOf(_err) { 198 fn err(parser: @This(), arg: []const u8, names: clap.Names, _err: anytype) @TypeOf(_err) {
198 if (diag) |d| 199 if (parser.diagnostic) |d|
199 d.* = .{ .arg = arg, .name = names }; 200 d.* = .{ .arg = arg, .name = names };
200 return _err; 201 return _err;
201 } 202 }
@@ -210,7 +211,7 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r
210 }; 211 };
211 212
212 for (results) |res| { 213 for (results) |res| {
213 const arg = (c.next(null) catch unreachable) orelse unreachable; 214 const arg = (c.next() catch unreachable) orelse unreachable;
214 testing.expectEqual(res.param, arg.param); 215 testing.expectEqual(res.param, arg.param);
215 const expected_value = res.value orelse { 216 const expected_value = res.value orelse {
216 testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value); 217 testing.expectEqual(@as(@TypeOf(arg.value), null), arg.value);
@@ -220,22 +221,23 @@ fn testNoErr(params: []const clap.Param(u8), args_strings: []const []const u8, r
220 testing.expectEqualSlices(u8, expected_value, actual_value); 221 testing.expectEqualSlices(u8, expected_value, actual_value);
221 } 222 }
222 223
223 if (c.next(null) catch unreachable) |_| 224 if (c.next() catch unreachable) |_|
224 unreachable; 225 unreachable;
225} 226}
226 227
227fn testErr(params: []const clap.Param(u8), args_strings: []const []const u8, expected: []const u8) void { 228fn testErr(params: []const clap.Param(u8), args_strings: []const []const u8, expected: []const u8) void {
228 var diag: clap.Diagnostic = undefined; 229 var diag = clap.Diagnostic{};
229 var iter = args.SliceIterator{ .args = args_strings }; 230 var iter = args.SliceIterator{ .args = args_strings };
230 var c = StreamingClap(u8, args.SliceIterator){ 231 var c = StreamingClap(u8, args.SliceIterator){
231 .params = params, 232 .params = params,
232 .iter = &iter, 233 .iter = &iter,
234 .diagnostic = &diag,
233 }; 235 };
234 while (c.next(&diag) catch |err| { 236 while (c.next() catch |err| {
235 var buf: [1024]u8 = undefined; 237 var buf: [1024]u8 = undefined;
236 var slice_stream = io.fixedBufferStream(&buf); 238 var fbs = io.fixedBufferStream(&buf);
237 diag.report(slice_stream.writer(), err) catch unreachable; 239 diag.report(fbs.writer(), err) catch unreachable;
238 testing.expectEqualStrings(expected, slice_stream.getWritten()); 240 testing.expectEqualStrings(expected, fbs.getWritten());
239 return; 241 return;
240 }) |_| {} 242 }) |_| {}
241 243
@@ -244,23 +246,17 @@ fn testErr(params: []const clap.Param(u8), args_strings: []const []const u8, exp
244 246
245test "short params" { 247test "short params" {
246 const params = [_]clap.Param(u8){ 248 const params = [_]clap.Param(u8){
247 clap.Param(u8){ 249 .{ .id = 0, .names = .{ .short = 'a' } },
248 .id = 0, 250 .{ .id = 1, .names = .{ .short = 'b' } },
249 .names = clap.Names{ .short = 'a' }, 251 .{
250 },
251 clap.Param(u8){
252 .id = 1,
253 .names = clap.Names{ .short = 'b' },
254 },
255 clap.Param(u8){
256 .id = 2, 252 .id = 2,
257 .names = clap.Names{ .short = 'c' }, 253 .names = .{ .short = 'c' },
258 .takes_value = .One, 254 .takes_value = .one,
259 }, 255 },
260 clap.Param(u8){ 256 .{
261 .id = 3, 257 .id = 3,
262 .names = clap.Names{ .short = 'd' }, 258 .names = .{ .short = 'd' },
263 .takes_value = .Many, 259 .takes_value = .many,
264 }, 260 },
265 }; 261 };
266 262
@@ -277,42 +273,36 @@ test "short params" {
277 "0", "-ac=0", "-d=0", 273 "0", "-ac=0", "-d=0",
278 }, 274 },
279 &[_]Arg(u8){ 275 &[_]Arg(u8){
280 Arg(u8){ .param = a }, 276 .{ .param = a },
281 Arg(u8){ .param = b }, 277 .{ .param = b },
282 Arg(u8){ .param = a }, 278 .{ .param = a },
283 Arg(u8){ .param = b }, 279 .{ .param = b },
284 Arg(u8){ .param = b }, 280 .{ .param = b },
285 Arg(u8){ .param = a }, 281 .{ .param = a },
286 Arg(u8){ .param = c, .value = "0" }, 282 .{ .param = c, .value = "0" },
287 Arg(u8){ .param = c, .value = "0" }, 283 .{ .param = c, .value = "0" },
288 Arg(u8){ .param = a }, 284 .{ .param = a },
289 Arg(u8){ .param = c, .value = "0" }, 285 .{ .param = c, .value = "0" },
290 Arg(u8){ .param = a }, 286 .{ .param = a },
291 Arg(u8){ .param = c, .value = "0" }, 287 .{ .param = c, .value = "0" },
292 Arg(u8){ .param = d, .value = "0" }, 288 .{ .param = d, .value = "0" },
293 }, 289 },
294 ); 290 );
295} 291}
296 292
297test "long params" { 293test "long params" {
298 const params = [_]clap.Param(u8){ 294 const params = [_]clap.Param(u8){
299 clap.Param(u8){ 295 .{ .id = 0, .names = .{ .long = "aa" } },
300 .id = 0, 296 .{ .id = 1, .names = .{ .long = "bb" } },
301 .names = clap.Names{ .long = "aa" }, 297 .{
302 },
303 clap.Param(u8){
304 .id = 1,
305 .names = clap.Names{ .long = "bb" },
306 },
307 clap.Param(u8){
308 .id = 2, 298 .id = 2,
309 .names = clap.Names{ .long = "cc" }, 299 .names = .{ .long = "cc" },
310 .takes_value = .One, 300 .takes_value = .one,
311 }, 301 },
312 clap.Param(u8){ 302 .{
313 .id = 3, 303 .id = 3,
314 .names = clap.Names{ .long = "dd" }, 304 .names = .{ .long = "dd" },
315 .takes_value = .Many, 305 .takes_value = .many,
316 }, 306 },
317 }; 307 };
318 308
@@ -329,59 +319,47 @@ test "long params" {
329 "--cc=0", "--dd=0", 319 "--cc=0", "--dd=0",
330 }, 320 },
331 &[_]Arg(u8){ 321 &[_]Arg(u8){
332 Arg(u8){ .param = aa }, 322 .{ .param = aa },
333 Arg(u8){ .param = bb }, 323 .{ .param = bb },
334 Arg(u8){ .param = cc, .value = "0" }, 324 .{ .param = cc, .value = "0" },
335 Arg(u8){ .param = cc, .value = "0" }, 325 .{ .param = cc, .value = "0" },
336 Arg(u8){ .param = dd, .value = "0" }, 326 .{ .param = dd, .value = "0" },
337 }, 327 },
338 ); 328 );
339} 329}
340 330
341test "positional params" { 331test "positional params" {
342 const params = [_]clap.Param(u8){clap.Param(u8){ 332 const params = [_]clap.Param(u8){.{
343 .id = 0, 333 .id = 0,
344 .takes_value = .One, 334 .takes_value = .one,
345 }}; 335 }};
346 336
347 testNoErr( 337 testNoErr(
348 &params, 338 &params,
349 &[_][]const u8{ "aa", "bb" }, 339 &[_][]const u8{ "aa", "bb" },
350 &[_]Arg(u8){ 340 &[_]Arg(u8){
351 Arg(u8){ .param = &params[0], .value = "aa" }, 341 .{ .param = &params[0], .value = "aa" },
352 Arg(u8){ .param = &params[0], .value = "bb" }, 342 .{ .param = &params[0], .value = "bb" },
353 }, 343 },
354 ); 344 );
355} 345}
356 346
357test "all params" { 347test "all params" {
358 const params = [_]clap.Param(u8){ 348 const params = [_]clap.Param(u8){
359 clap.Param(u8){ 349 .{
360 .id = 0, 350 .id = 0,
361 .names = clap.Names{ 351 .names = .{ .short = 'a', .long = "aa" },
362 .short = 'a',
363 .long = "aa",
364 },
365 }, 352 },
366 clap.Param(u8){ 353 .{
367 .id = 1, 354 .id = 1,
368 .names = clap.Names{ 355 .names = .{ .short = 'b', .long = "bb" },
369 .short = 'b',
370 .long = "bb",
371 },
372 }, 356 },
373 clap.Param(u8){ 357 .{
374 .id = 2, 358 .id = 2,
375 .names = clap.Names{ 359 .names = .{ .short = 'c', .long = "cc" },
376 .short = 'c', 360 .takes_value = .one,
377 .long = "cc",
378 },
379 .takes_value = .One,
380 },
381 clap.Param(u8){
382 .id = 3,
383 .takes_value = .One,
384 }, 361 },
362 .{ .id = 3, .takes_value = .one },
385 }; 363 };
386 364
387 const aa = &params[0]; 365 const aa = &params[0];
@@ -399,46 +377,40 @@ test "all params" {
399 "-", "--", "--cc=0", "-a", 377 "-", "--", "--cc=0", "-a",
400 }, 378 },
401 &[_]Arg(u8){ 379 &[_]Arg(u8){
402 Arg(u8){ .param = aa }, 380 .{ .param = aa },
403 Arg(u8){ .param = bb }, 381 .{ .param = bb },
404 Arg(u8){ .param = aa }, 382 .{ .param = aa },
405 Arg(u8){ .param = bb }, 383 .{ .param = bb },
406 Arg(u8){ .param = bb }, 384 .{ .param = bb },
407 Arg(u8){ .param = aa }, 385 .{ .param = aa },
408 Arg(u8){ .param = cc, .value = "0" }, 386 .{ .param = cc, .value = "0" },
409 Arg(u8){ .param = cc, .value = "0" }, 387 .{ .param = cc, .value = "0" },
410 Arg(u8){ .param = aa }, 388 .{ .param = aa },
411 Arg(u8){ .param = cc, .value = "0" }, 389 .{ .param = cc, .value = "0" },
412 Arg(u8){ .param = aa }, 390 .{ .param = aa },
413 Arg(u8){ .param = cc, .value = "0" }, 391 .{ .param = cc, .value = "0" },
414 Arg(u8){ .param = aa }, 392 .{ .param = aa },
415 Arg(u8){ .param = bb }, 393 .{ .param = bb },
416 Arg(u8){ .param = cc, .value = "0" }, 394 .{ .param = cc, .value = "0" },
417 Arg(u8){ .param = cc, .value = "0" }, 395 .{ .param = cc, .value = "0" },
418 Arg(u8){ .param = positional, .value = "something" }, 396 .{ .param = positional, .value = "something" },
419 Arg(u8){ .param = positional, .value = "-" }, 397 .{ .param = positional, .value = "-" },
420 Arg(u8){ .param = positional, .value = "--cc=0" }, 398 .{ .param = positional, .value = "--cc=0" },
421 Arg(u8){ .param = positional, .value = "-a" }, 399 .{ .param = positional, .value = "-a" },
422 }, 400 },
423 ); 401 );
424} 402}
425 403
426test "errors" { 404test "errors" {
427 const params = [_]clap.Param(u8){ 405 const params = [_]clap.Param(u8){
428 clap.Param(u8){ 406 .{
429 .id = 0, 407 .id = 0,
430 .names = clap.Names{ 408 .names = .{ .short = 'a', .long = "aa" },
431 .short = 'a',
432 .long = "aa",
433 },
434 }, 409 },
435 clap.Param(u8){ 410 .{
436 .id = 1, 411 .id = 1,
437 .names = clap.Names{ 412 .names = .{ .short = 'c', .long = "cc" },
438 .short = 'c', 413 .takes_value = .one,
439 .long = "cc",
440 },
441 .takes_value = .One,
442 }, 414 },
443 }; 415 };
444 testErr(&params, &[_][]const u8{"q"}, "Invalid argument 'q'\n"); 416 testErr(&params, &[_][]const u8{"q"}, "Invalid argument 'q'\n");
diff --git a/example/help.zig b/example/help.zig
index c210ff9..3cf9e42 100644
--- a/example/help.zig
+++ b/example/help.zig
@@ -1,15 +1,12 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const std = @import("std");
3 3
4pub fn main() !void { 4pub fn main() !void {
5 const stderr_file = std.io.getStdErr();
6 var stderr_out_stream = stderr_file.writer();
7
8 // clap.help is a function that can print a simple help message, given a 5 // clap.help is a function that can print a simple help message, given a
9 // slice of Param(Help). There is also a helpEx, which can print a 6 // slice of Param(Help). There is also a helpEx, which can print a
10 // help message for any Param, but it is more verbose to call. 7 // help message for any Param, but it is more verbose to call.
11 try clap.help( 8 try clap.help(
12 stderr_out_stream, 9 std.io.getStdErr().writer(),
13 comptime &[_]clap.Param(clap.Help){ 10 comptime &[_]clap.Param(clap.Help){
14 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 11 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
15 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 12 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
diff --git a/example/simple-error.zig b/example/simple-error.zig
index 3c62f0e..c04a9c6 100644
--- a/example/simple-error.zig
+++ b/example/simple-error.zig
@@ -1,12 +1,12 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const 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 }; 7 };
8 8
9 var args = try clap.parse(clap.Help, &params, std.heap.direct_allocator, null); 9 var args = try clap.parse(clap.Help, &params, .{});
10 defer args.deinit(); 10 defer args.deinit();
11 11
12 _ = args.flag("--helps"); 12 _ = args.flag("--helps");
diff --git a/example/simple-ex.zig b/example/simple-ex.zig
index b890860..88598aa 100644
--- a/example/simple-ex.zig
+++ b/example/simple-ex.zig
@@ -1,7 +1,8 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const std = @import("std");
3 3
4const debug = std.debug; 4const debug = std.debug;
5const io = std.io;
5 6
6pub fn main() !void { 7pub fn main() !void {
7 const allocator = std.heap.page_allocator; 8 const allocator = std.heap.page_allocator;
@@ -21,13 +22,19 @@ pub fn main() !void {
21 defer iter.deinit(); 22 defer iter.deinit();
22 23
23 // Initalize our diagnostics, which can be used for reporting useful errors. 24 // Initalize our diagnostics, which can be used for reporting useful errors.
24 // This is optional. You can also just pass `null` to `parser.next` if you 25 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
25 // don't care about the extra information `Diagnostics` provides. 26 // care about the extra information `Diagnostics` provides.
26 var diag: clap.Diagnostic = undefined; 27 var diag = clap.Diagnostic{};
27 28 var args = clap.parseEx(clap.Help, &params, &iter, .{
28 var args = clap.parseEx(clap.Help, &params, allocator, &iter, &diag) catch |err| { 29 .allocator = allocator,
30 .diagnostic = &diag,
31 }) catch |err| {
29 // Report useful error and exit 32 // Report useful error and exit
33<<<<<<< HEAD
30 diag.report(std.io.getStdErr().writer(), err) catch {}; 34 diag.report(std.io.getStdErr().writer(), err) catch {};
35=======
36 diag.report(io.getStdErr().writer(), err) catch {};
37>>>>>>> master
31 return err; 38 return err;
32 }; 39 };
33 defer args.deinit(); 40 defer args.deinit();
diff --git a/example/simple.zig b/example/simple.zig
index 559bba6..69473fa 100644
--- a/example/simple.zig
+++ b/example/simple.zig
@@ -1,7 +1,8 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const std = @import("std");
3 3
4const debug = std.debug; 4const debug = std.debug;
5const io = std.io;
5 6
6pub fn main() !void { 7pub fn main() !void {
7 // First we specify what parameters our program can take. 8 // First we specify what parameters our program can take.
@@ -14,13 +15,12 @@ pub fn main() !void {
14 }; 15 };
15 16
16 // Initalize our diagnostics, which can be used for reporting useful errors. 17 // Initalize our diagnostics, which can be used for reporting useful errors.
17 // This is optional. You can also just pass `null` to `parser.next` if you 18 // This is optional. You can also pass `.{}` to `clap.parse` if you don't
18 // don't care about the extra information `Diagnostics` provides. 19 // care about the extra information `Diagnostics` provides.
19 var diag: clap.Diagnostic = undefined; 20 var diag = clap.Diagnostic{};
20 21 var args = clap.parse(clap.Help, &params, .{ .diagnostic = &diag }) catch |err| {
21 var args = clap.parse(clap.Help, &params, std.heap.page_allocator, &diag) catch |err| {
22 // Report useful error and exit 22 // Report useful error and exit
23 diag.report(std.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 args.deinit();
diff --git a/example/streaming-clap.zig b/example/streaming-clap.zig
index 5468fd5..32d44c4 100644
--- a/example/streaming-clap.zig
+++ b/example/streaming-clap.zig
@@ -1,26 +1,24 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const std = @import("std");
3 3
4const debug = std.debug; 4const debug = std.debug;
5const io = std.io;
5 6
6pub fn main() !void { 7pub fn main() !void {
7 const allocator = std.heap.page_allocator; 8 const allocator = std.heap.page_allocator;
8 9
9 // First we specify what parameters our program can take. 10 // First we specify what parameters our program can take.
10 const params = [_]clap.Param(u8){ 11 const params = [_]clap.Param(u8){
11 clap.Param(u8){ 12 .{
12 .id = 'h', 13 .id = 'h',
13 .names = clap.Names{ .short = 'h', .long = "help" }, 14 .names = .{ .short = 'h', .long = "help" },
14 }, 15 },
15 clap.Param(u8){ 16 .{
16 .id = 'n', 17 .id = 'n',
17 .names = clap.Names{ .short = 'n', .long = "number" }, 18 .names = .{ .short = 'n', .long = "number" },
18 .takes_value = .One, 19 .takes_value = .one,
19 },
20 clap.Param(u8){
21 .id = 'f',
22 .takes_value = .One,
23 }, 20 },
21 .{ .id = 'f', .takes_value = .one },
24 }; 22 };
25 23
26 // We then initialize an argument iterator. We will use the OsIterator as it nicely 24 // We then initialize an argument iterator. We will use the OsIterator as it nicely
@@ -28,21 +26,24 @@ pub fn main() !void {
28 var iter = try clap.args.OsIterator.init(allocator); 26 var iter = try clap.args.OsIterator.init(allocator);
29 defer iter.deinit(); 27 defer iter.deinit();
30 28
31 // Initialize our streaming parser. 29 // Initalize our diagnostics, which can be used for reporting useful errors.
30 // This is optional. You can also leave the `diagnostic` field unset if you
31 // don't care about the extra information `Diagnostic` provides.
32 var diag = clap.Diagnostic{};
32 var parser = clap.StreamingClap(u8, clap.args.OsIterator){ 33 var parser = clap.StreamingClap(u8, clap.args.OsIterator){
33 .params = &params, 34 .params = &params,
34 .iter = &iter, 35 .iter = &iter,
36 .diagnostic = &diag,
35 }; 37 };
36 38
37 // Initalize our diagnostics, which can be used for reporting useful errors.
38 // This is optional. You can also just pass `null` to `parser.next` if you
39 // don't care about the extra information `Diagnostics` provides.
40 var diag: clap.Diagnostic = undefined;
41
42 // Because we use a streaming parser, we have to consume each argument parsed individually. 39 // Because we use a streaming parser, we have to consume each argument parsed individually.
43 while (parser.next(&diag) catch |err| { 40 while (parser.next() catch |err| {
44 // Report useful error and exit 41 // Report useful error and exit
42<<<<<<< HEAD
45 diag.report(std.io.getStdErr().writer(), err) catch {}; 43 diag.report(std.io.getStdErr().writer(), err) catch {};
44=======
45 diag.report(io.getStdErr().writer(), err) catch {};
46>>>>>>> master
46 return err; 47 return err;
47 }) |arg| { 48 }) |arg| {
48 // arg.param will point to the parameter which matched the argument. 49 // arg.param will point to the parameter which matched the argument.
diff --git a/example/usage.zig b/example/usage.zig
index dc88c48..e044f1d 100644
--- a/example/usage.zig
+++ b/example/usage.zig
@@ -1,14 +1,12 @@
1const std = @import("std");
2const clap = @import("clap"); 1const clap = @import("clap");
2const std = @import("std");
3 3
4pub fn main() !void { 4pub fn main() !void {
5 const stderr = std.io.getStdErr().writer();
6
7 // clap.usage is a function that can print a simple usage message, given a 5 // clap.usage is a function that can print a simple usage message, given a
8 // slice of Param(Help). There is also a usageEx, which can print a 6 // slice of Param(Help). There is also a usageEx, which can print a
9 // usage message for any Param, but it is more verbose to call. 7 // usage message for any Param, but it is more verbose to call.
10 try clap.usage( 8 try clap.usage(
11 stderr, 9 std.io.getStdErr().writer(),
12 comptime &[_]clap.Param(clap.Help){ 10 comptime &[_]clap.Param(clap.Help){
13 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable, 11 clap.parseParam("-h, --help Display this help and exit. ") catch unreachable,
14 clap.parseParam("-v, --version Output version information and exit.") catch unreachable, 12 clap.parseParam("-v, --version Output version information and exit.") catch unreachable,
diff --git a/gyro.zzz b/gyro.zzz
new file mode 100644
index 0000000..3853db0
--- /dev/null
+++ b/gyro.zzz
@@ -0,0 +1,14 @@
1pkgs:
2 clap:
3 version: 0.3.0
4 license: Unlicense
5 description: Simple command line argument parsing library
6 source_url: "https://github.com/Hejsil/zig-clap"
7 root: clap.zig
8 files:
9 README.md
10 LICENSE
11 build.zig
12 clap/*.zig
13 example/*.zig
14
diff --git a/zig.mod b/zig.mod
index c9bd69f..00c1a69 100644
--- a/zig.mod
+++ b/zig.mod
@@ -1,3 +1,5 @@
1id: aoe2l16htluewam6bfwvv0khsbbno8g8jd7suonifg74u7kd
1name: clap 2name: clap
2main: clap.zig 3main: clap.zig
4license: Unlicense
3dependencies: 5dependencies: