summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--clap.zig68
1 files changed, 61 insertions, 7 deletions
diff --git a/clap.zig b/clap.zig
index 2c78e95..189f757 100644
--- a/clap.zig
+++ b/clap.zig
@@ -8,9 +8,8 @@ const io = std.io;
8 8
9const assert = debug.assert; 9const assert = debug.assert;
10 10
11// TODO: Missing a few convinient features 11// TODO:
12// * Short arguments that doesn't take values should probably be able to be 12// * Inform the caller which argument caused the error.
13// chain like many linux programs: "rm -rf"
14pub fn Option(comptime Result: type, comptime ParseError: type) type { 13pub fn Option(comptime Result: type, comptime ParseError: type) type {
15 return struct { 14 return struct {
16 const Self = this; 15 const Self = this;
@@ -150,10 +149,10 @@ pub fn Parser(comptime Result: type, comptime ParseError: type, comptime default
150 const after_eql = arg_info.after_eql; 149 const after_eql = arg_info.after_eql;
151 150
152 success: { 151 success: {
153 var required_index = usize(0);
154 152
155 switch (kind) { 153 switch (kind) {
156 Arg.Kind.None => { 154 Arg.Kind.None => {
155 var required_index = usize(0);
157 inline for (options) |option| { 156 inline for (options) |option| {
158 defer if (option.kind == OptionT.Kind.Required) required_index += 1; 157 defer if (option.kind == OptionT.Kind.Required) required_index += 1;
159 if (option.short != null) continue; 158 if (option.short != null) continue;
@@ -165,10 +164,31 @@ pub fn Parser(comptime Result: type, comptime ParseError: type, comptime default
165 } 164 }
166 }, 165 },
167 Arg.Kind.Short => { 166 Arg.Kind.Short => {
167 if (arg.len == 0) return error.FoundShortOptionWithNoName;
168 short_arg_loop: for (arg[0..arg.len - 1]) |short_arg| {
169 var required_index = usize(0);
170 inline for (options) |option| {
171 defer if (option.kind == OptionT.Kind.Required) required_index += 1;
172 const short = option.short ?? continue;
173 if (short_arg == short) {
174 if (option.takes_value) return error.OptionMissingValue;
175
176 try option.parse(&result, []u8{});
177 required = newRequired(option, required, required_index);
178 continue :short_arg_loop;
179 }
180 }
181
182 return error.InvalidArgument;
183 }
184
185 const last_arg = arg[arg.len - 1];
186 var required_index = usize(0);
168 inline for (options) |option| { 187 inline for (options) |option| {
169 defer if (option.kind == OptionT.Kind.Required) required_index += 1; 188 defer if (option.kind == OptionT.Kind.Required) required_index += 1;
170 const short = option.short ?? continue; 189 const short = option.short ?? continue;
171 if (arg.len == 1 and arg[0] == short) { 190
191 if (last_arg == short) {
172 if (option.takes_value) { 192 if (option.takes_value) {
173 const value = after_eql ?? it.next() ?? return error.OptionMissingValue; 193 const value = after_eql ?? it.next() ?? return error.OptionMissingValue;
174 try option.parse(&result, value); 194 try option.parse(&result, value);
@@ -182,6 +202,7 @@ pub fn Parser(comptime Result: type, comptime ParseError: type, comptime default
182 } 202 }
183 }, 203 },
184 Arg.Kind.Long => { 204 Arg.Kind.Long => {
205 var required_index = usize(0);
185 inline for (options) |option| { 206 inline for (options) |option| {
186 defer if (option.kind == OptionT.Kind.Required) required_index += 1; 207 defer if (option.kind == OptionT.Kind.Required) required_index += 1;
187 const long = option.long ?? continue; 208 const long = option.long ?? continue;
@@ -292,7 +313,15 @@ test "clap.parse.Example" {
292 fn bFromStr(color: &Self, str: []const u8) !void { 313 fn bFromStr(color: &Self, str: []const u8) !void {
293 color.b = try fmt.parseInt(u8, str, 10); 314 color.b = try fmt.parseInt(u8, str, 10);
294 } 315 }
316
317 // TODO: There is a segfault when we try to use the error set: @typeOf(fmt.parseInt).ReturnType.ErrorSet
318 fn setMax(color: &Self, str: []const u8) !void {
319 color.r = try fmt.parseInt(u8, "255", 10);
320 color.g = try fmt.parseInt(u8, "255", 10);
321 color.b = try fmt.parseInt(u8, "255", 10);
322 }
295 }; 323 };
324 const Error = @typeOf(Color.setMax).ReturnType.ErrorSet;
296 325
297 const Case = struct { args: []const []const u8, res: Color, err: ?error }; 326 const Case = struct { args: []const []const u8, res: Color, err: ?error };
298 const cases = []Case { 327 const cases = []Case {
@@ -322,6 +351,16 @@ test "clap.parse.Example" {
322 .err = null, 351 .err = null,
323 }, 352 },
324 Case { 353 Case {
354 .args = [][]const u8 { "-mr", "100" },
355 .res = Color { .r = 100, .g = 255, .b = 255 },
356 .err = null,
357 },
358 Case {
359 .args = [][]const u8 { "-mr=100" },
360 .res = Color { .r = 100, .g = 255, .b = 255 },
361 .err = null,
362 },
363 Case {
325 .args = [][]const u8 { "-g", "200", "-b", "255" }, 364 .args = [][]const u8 { "-g", "200", "-b", "255" },
326 .res = Color { .r = 0, .g = 0, .b = 0 }, 365 .res = Color { .r = 0, .g = 0, .b = 0 },
327 .err = error.RequiredArgumentWasntHandled, 366 .err = error.RequiredArgumentWasntHandled,
@@ -336,10 +375,20 @@ test "clap.parse.Example" {
336 .res = Color { .r = 0, .g = 0, .b = 0 }, 375 .res = Color { .r = 0, .g = 0, .b = 0 },
337 .err = error.OptionMissingValue, 376 .err = error.OptionMissingValue,
338 }, 377 },
378 Case {
379 .args = [][]const u8 { "-" },
380 .res = Color { .r = 0, .g = 0, .b = 0 },
381 .err = error.FoundShortOptionWithNoName,
382 },
383 Case {
384 .args = [][]const u8 { "-rg", "100" },
385 .res = Color { .r = 0, .g = 0, .b = 0 },
386 .err = error.OptionMissingValue,
387 },
339 }; 388 };
340 389
341 const COption = Option(Color, @typeOf(Color.rFromStr).ReturnType.ErrorSet); 390 const COption = Option(Color, Error);
342 const Clap = Parser(Color, @typeOf(Color.rFromStr).ReturnType.ErrorSet, 391 const Clap = Parser(Color, Error,
343 Color { .r = 0, .g = 0, .b = 0 }, 392 Color { .r = 0, .g = 0, .b = 0 },
344 comptime []COption { 393 comptime []COption {
345 COption.init(Color.rFromStr) 394 COption.init(Color.rFromStr)
@@ -358,11 +407,16 @@ test "clap.parse.Example" {
358 .setShort('b') 407 .setShort('b')
359 .setLong("blue") 408 .setLong("blue")
360 .takesValue(true), 409 .takesValue(true),
410 COption.init(Color.setMax)
411 .setHelp("Set all values to max")
412 .setShort('m')
413 .setLong("max"),
361 } 414 }
362 ); 415 );
363 416
364 for (cases) |case, i| { 417 for (cases) |case, i| {
365 if (Clap.parse(case.args)) |res| { 418 if (Clap.parse(case.args)) |res| {
419 assert(case.err == null);
366 assert(res.r == case.res.r); 420 assert(res.r == case.res.r);
367 assert(res.g == case.res.g); 421 assert(res.g == case.res.g);
368 assert(res.b == case.res.b); 422 assert(res.b == case.res.b);