Optional handling for the arg parser
This commit is contained in:
parent
7549bb86d3
commit
6e9d0197de
1 changed files with 75 additions and 19 deletions
|
|
@ -1,5 +1,9 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const builtin = @import("builtin");
|
||||||
|
|
||||||
|
const inDebugMode = builtin.mode == .Debug;
|
||||||
|
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const parsers = @import("./parsers/parsers.zig");
|
pub const parsers = @import("./parsers/parsers.zig");
|
||||||
|
|
@ -145,12 +149,12 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
}
|
}
|
||||||
|
|
||||||
const fie = &@field(result, field.name);
|
const fie = &@field(result, field.name);
|
||||||
const extra = fie.*.extra;
|
const extra: Extra = fie.*.extra;
|
||||||
|
|
||||||
switch (extra) {
|
switch (extra) {
|
||||||
.Flag => |f| {
|
.Flag => |f| {
|
||||||
if (!(f.takesValue or f.toggle)) {
|
if (!(f.takesValue or f.toggle)) {
|
||||||
log.err("invalid flag `{s}`: is not a toggle and doesn't take a value\n", .{f.name});
|
log.err("invalid flag `{s}`: is not a toggle and doesn't take a value", .{f.name});
|
||||||
return error.InvalidFlag;
|
return error.InvalidFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -163,7 +167,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
fie.*.value = true;
|
fie.*.value = true;
|
||||||
comptime continue;
|
comptime continue;
|
||||||
} else {
|
} else {
|
||||||
log.err("invalid switch `{s}`: expected T == bool, found: {s}\n", .{
|
log.err("invalid switch `{s}`: expected T == bool, found: {s}", .{
|
||||||
f.name,
|
f.name,
|
||||||
@typeName(@TypeOf(fie.*.value)),
|
@typeName(@TypeOf(fie.*.value)),
|
||||||
});
|
});
|
||||||
|
|
@ -179,17 +183,17 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
} else {
|
} else {
|
||||||
if (fie.*.parse) |parse_fn| {
|
if (fie.*.parse) |parse_fn| {
|
||||||
fie.*.value = parse_fn(value) catch |err| {
|
fie.*.value = parse_fn(value) catch |err| {
|
||||||
log.err("could not parse flag `{s}`: `{s}`\n", .{
|
log.err("could not parse flag `{s}`: `{s}`", .{
|
||||||
field.name,
|
field.name,
|
||||||
@errorName(err),
|
@errorName(err),
|
||||||
});
|
});
|
||||||
log.err("- tried to parse: {s}\n", .{value});
|
log.err("- tried to parse: {s}", .{value});
|
||||||
log.err("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
log.err("- expected type: {s}", .{@typeName(@TypeOf(fie.*.value))});
|
||||||
return error.InvalidFlag;
|
return error.InvalidFlag;
|
||||||
};
|
};
|
||||||
comptime continue;
|
comptime continue;
|
||||||
} else {
|
} else {
|
||||||
log.err("flag `{s}` expected a value, but no parser was provided for type `{s}`\n", .{
|
log.err("flag `{s}` expected a value, but no parser was provided for type `{s}`", .{
|
||||||
f.name,
|
f.name,
|
||||||
@typeName(@TypeOf(fie.*.value)),
|
@typeName(@TypeOf(fie.*.value)),
|
||||||
});
|
});
|
||||||
|
|
@ -199,16 +203,40 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.err("flag `{s}` expected a value, but none was provided\n", .{f.name});
|
log.err("flag `{s}` expected a value, but none was provided", .{f.name});
|
||||||
return error.NoValueForFlag;
|
return error.NoValueForFlag;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
log.err("invalid flag `{s}`: is not a toggle and doesn't take a value\n", .{f.name});
|
log.err("invalid flag `{s}`: is not a toggle and doesn't take a value", .{f.name});
|
||||||
return error.InvalidFlag;
|
return error.InvalidFlag;
|
||||||
} else {
|
} else {
|
||||||
log.err("invalid flag `{s}`: could not find flag\n", .{f.name});
|
const fieldType = @typeInfo(@TypeOf(fie.*.value));
|
||||||
return error.InvalidFlag;
|
|
||||||
|
switch (fieldType) {
|
||||||
|
.Optional => |_| {
|
||||||
|
log.debug("flag `{s}` is optional, and we couldn't find a value for it", .{f.name});
|
||||||
|
fie.*.value = null;
|
||||||
|
comptime continue;
|
||||||
|
},
|
||||||
|
|
||||||
|
else => {
|
||||||
|
if (inDebugMode) {
|
||||||
|
log.warn("flag `{s}` expected a value, but none was provided", .{f.name});
|
||||||
|
log.warn("expected type: {s}", .{@typeName(@TypeOf(fie.*.value))});
|
||||||
|
log.warn("but the flag wasn't provided...", .{});
|
||||||
|
} else {
|
||||||
|
log.err("flag `{s}` is required (expected type `{s}`)", .{
|
||||||
|
f.name,
|
||||||
|
@typeName(@TypeOf(fie.*.value)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return error.NoValueForFlag;
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
log.err("invalid flag `{s}`: could not be located in args", .{f.name});
|
||||||
|
return error.CouldNotFindFlag;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
.Remainder => {
|
.Remainder => {
|
||||||
|
|
@ -220,7 +248,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
|
|
||||||
switch (flag.*) {
|
switch (flag.*) {
|
||||||
.Flag => |f| {
|
.Flag => |f| {
|
||||||
log.warn("TODO: figure out what to do with remainder flag {s} (value: {?s})\n", .{
|
log.warn("TODO: figure out what to do with remainder flag {s} (value: {?s})", .{
|
||||||
f.name,
|
f.name,
|
||||||
f.value,
|
f.value,
|
||||||
});
|
});
|
||||||
|
|
@ -235,7 +263,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
if (@TypeOf(fie.*.value) == std.ArrayList([]const u8)) {
|
if (@TypeOf(fie.*.value) == std.ArrayList([]const u8)) {
|
||||||
fie.*.value = not_consumed;
|
fie.*.value = not_consumed;
|
||||||
} else {
|
} else {
|
||||||
log.err("invalid remainder field `{s}`: expected T == `std.ArrayList([] const u8)`, got T == `{s}`\n", .{
|
log.err("invalid remainder field `{s}`: expected T == `std.ArrayList([] const u8)`, got T == `{s}`", .{
|
||||||
field.name,
|
field.name,
|
||||||
@typeName(@TypeOf(fie.*.value)),
|
@typeName(@TypeOf(fie.*.value)),
|
||||||
});
|
});
|
||||||
|
|
@ -251,20 +279,20 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
fie.*.value = try result.allocator.dupe(u8, flag.*.Positional.value);
|
fie.*.value = try result.allocator.dupe(u8, flag.*.Positional.value);
|
||||||
} else if (fie.*.parse) |parse_fn| {
|
} else if (fie.*.parse) |parse_fn| {
|
||||||
fie.*.value = parse_fn(flag.*.Positional.value) catch |err| {
|
fie.*.value = parse_fn(flag.*.Positional.value) catch |err| {
|
||||||
log.err("could not parse positional argument for `{s}`: `{s}`\n", .{
|
log.err("could not parse positional argument for `{s}`: `{s}`", .{
|
||||||
field.name,
|
field.name,
|
||||||
@errorName(err),
|
@errorName(err),
|
||||||
});
|
});
|
||||||
log.err("- tried to parse: {s}\n", .{flag.*.Positional.value});
|
log.err("- tried to parse: {s}", .{flag.*.Positional.value});
|
||||||
log.err("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
log.err("- expected type: {s}", .{@typeName(@TypeOf(fie.*.value))});
|
||||||
return error.InvalidPositional;
|
return error.InvalidPositional;
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
log.err("could not parse positional argument for `{s}`: no parser provided\n", .{field.name});
|
log.err("could not parse positional argument for `{s}`: no parser provided", .{field.name});
|
||||||
return error.InvalidPositional;
|
return error.InvalidPositional;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
log.err("could not find positional argument for `{s}`\n", .{field.name});
|
log.err("could not find positional argument for `{s}`", .{field.name});
|
||||||
return error.NoArgumentFound;
|
return error.NoArgumentFound;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -298,7 +326,6 @@ test "parse args" {
|
||||||
.Flag = .{
|
.Flag = .{
|
||||||
.name = "toggle",
|
.name = "toggle",
|
||||||
.toggle = true,
|
.toggle = true,
|
||||||
.takesValue = false,
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -330,6 +357,35 @@ test "parse args" {
|
||||||
try t.expectEqualStrings("positional", result.positional.value);
|
try t.expectEqualStrings("positional", result.positional.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "missing flag" {
|
||||||
|
const args = try t.allocator.alloc([]const u8, 2);
|
||||||
|
defer t.allocator.free(args);
|
||||||
|
|
||||||
|
args[1] = "1234";
|
||||||
|
|
||||||
|
const Demo = struct {
|
||||||
|
allocator: Allocator,
|
||||||
|
|
||||||
|
flag: Marker(bool) = .{
|
||||||
|
.value = undefined,
|
||||||
|
.extra = Extra{
|
||||||
|
.Flag = .{
|
||||||
|
.name = "flag",
|
||||||
|
.toggle = true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.parse = parsers.boolean,
|
||||||
|
},
|
||||||
|
|
||||||
|
fn deinit(self: *@This()) void {
|
||||||
|
self.* = undefined;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = parseArgsImpl(Demo, t.allocator, args);
|
||||||
|
try t.expectError(error.NoValueForFlag, result);
|
||||||
|
}
|
||||||
|
|
||||||
test "parse fn (positional)" {
|
test "parse fn (positional)" {
|
||||||
const args = try t.allocator.alloc([]const u8, 4);
|
const args = try t.allocator.alloc([]const u8, 4);
|
||||||
defer t.allocator.free(args);
|
defer t.allocator.free(args);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue