Swap out the logging for proper logging,
Also remove validators.
This commit is contained in:
parent
00a531045b
commit
7abebd7aa1
5 changed files with 34 additions and 94 deletions
|
|
@ -1,5 +1,7 @@
|
|||
const std = @import("std");
|
||||
|
||||
const log = std.log.scoped(.args);
|
||||
|
||||
pub const Arg = union(enum(u2)) {
|
||||
Flag: struct {
|
||||
name: []const u8,
|
||||
|
|
@ -75,7 +77,7 @@ pub const Arg = union(enum(u2)) {
|
|||
switch (flag) {
|
||||
.Flag => |f| {
|
||||
if (f.name.len == 0) {
|
||||
std.debug.print("failed to parse flag: {s}\n", .{arg});
|
||||
log.err("failed to parse flag: {s}\n", .{arg});
|
||||
return error.InvalidFlag;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -3,10 +3,11 @@ const std = @import("std");
|
|||
const Allocator = std.mem.Allocator;
|
||||
|
||||
pub const parsers = @import("./parsers/parsers.zig");
|
||||
pub const validators = @import("./validators/validators.zig");
|
||||
|
||||
const Arg = @import("./arg.zig").Arg;
|
||||
|
||||
const log = std.log.scoped(.args);
|
||||
|
||||
/// Metadata for a field that is parseable by the argument parser.
|
||||
pub const Extra = union(enum(u2)) {
|
||||
Positional,
|
||||
|
|
@ -31,13 +32,6 @@ pub fn Marker(comptime T: type) type {
|
|||
///
|
||||
/// However, it is recommended to use `lys.args.parsers` for common types.
|
||||
parse: ?parsers.ParseSignature(T) = null,
|
||||
/// Optional function that validates the value of the field.
|
||||
/// If this is null, no validation will take place.
|
||||
///
|
||||
/// This function can be any function that takes a `[]const u8` and returns `anyerror!void`.
|
||||
///
|
||||
/// However, it is recommended to use `lys.args.validators` for common types.
|
||||
validate: ?validators.ValidateSignature = null,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -127,8 +121,10 @@ fn nextPositional(flags: []Arg) ?*Arg {
|
|||
/// Does the actual work of initializing T from flags.
|
||||
fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||
comptime {
|
||||
std.debug.assert(@hasField(T, "allocator"));
|
||||
std.debug.assert(@hasDecl(T, "deinit"));
|
||||
if (!@hasField(T, "allocator"))
|
||||
@compileError("T must have an allocator");
|
||||
if (!@hasDecl(T, "deinit"))
|
||||
@compileError("T must have a deinit function");
|
||||
}
|
||||
|
||||
var result = T{
|
||||
|
|
@ -154,7 +150,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
switch (extra) {
|
||||
.Flag => |f| {
|
||||
if (!(f.takesValue or f.toggle)) {
|
||||
std.debug.print("error: 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\n", .{f.name});
|
||||
return error.InvalidFlag;
|
||||
}
|
||||
|
||||
|
|
@ -167,7 +163,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
fie.*.value = true;
|
||||
comptime continue;
|
||||
} else {
|
||||
std.debug.print("error: invalid switch {s}: expected bool, found {s}\n", .{
|
||||
log.err("invalid switch `{s}`: expected T == bool, found: {s}\n", .{
|
||||
f.name,
|
||||
@typeName(@TypeOf(fie.*.value)),
|
||||
});
|
||||
|
|
@ -182,25 +178,36 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
comptime continue;
|
||||
} else {
|
||||
if (fie.*.parse) |parse_fn| {
|
||||
fie.*.value = try parse_fn(value);
|
||||
fie.*.value = parse_fn(value) catch |err| {
|
||||
log.err("could not parse flag `{s}`: `{s}`\n", .{
|
||||
field.name,
|
||||
@errorName(err),
|
||||
});
|
||||
log.err("- tried to parse: {s}\n", .{value});
|
||||
log.err("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
||||
return error.InvalidFlag;
|
||||
};
|
||||
comptime continue;
|
||||
} else {
|
||||
std.debug.print("error: flag `{s}` expected a value, but no parser was provided\n", .{f.name});
|
||||
log.err("flag `{s}` expected a value, but no parser was provided for type `{s}`\n", .{
|
||||
f.name,
|
||||
@typeName(@TypeOf(fie.*.value)),
|
||||
});
|
||||
return error.NoValueForFlag;
|
||||
}
|
||||
|
||||
unreachable;
|
||||
}
|
||||
} else {
|
||||
std.debug.print("error: flag `{s}` expected a value, but none was provided\n", .{f.name});
|
||||
log.err("flag `{s}` expected a value, but none was provided\n", .{f.name});
|
||||
return error.NoValueForFlag;
|
||||
}
|
||||
}
|
||||
|
||||
std.debug.print("error: 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\n", .{f.name});
|
||||
return error.InvalidFlag;
|
||||
} else {
|
||||
std.debug.print("error: invalid flag `{s}`: could not find flag\n", .{f.name});
|
||||
log.err("invalid flag `{s}`: could not find flag\n", .{f.name});
|
||||
return error.InvalidFlag;
|
||||
}
|
||||
},
|
||||
|
|
@ -213,7 +220,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
|
||||
switch (flag.*) {
|
||||
.Flag => |f| {
|
||||
std.debug.print("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})\n", .{
|
||||
f.name,
|
||||
f.value,
|
||||
});
|
||||
|
|
@ -228,7 +235,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
if (@TypeOf(fie.*.value) == std.ArrayList([]const u8)) {
|
||||
fie.*.value = not_consumed;
|
||||
} else {
|
||||
std.debug.print("error: invalid remainder {s}: expected `std.ArrayList([] const u8)`, got `{s}`\n", .{
|
||||
log.err("invalid remainder field `{s}`: expected T == `std.ArrayList([] const u8)`, got T == `{s}`\n", .{
|
||||
field.name,
|
||||
@typeName(@TypeOf(fie.*.value)),
|
||||
});
|
||||
|
|
@ -244,20 +251,20 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
|||
fie.*.value = try result.allocator.dupe(u8, flag.*.Positional.value);
|
||||
} else if (fie.*.parse) |parse_fn| {
|
||||
fie.*.value = parse_fn(flag.*.Positional.value) catch |err| {
|
||||
std.debug.print("error: could not parse positional argument for {s}: {s}\n", .{
|
||||
log.err("could not parse positional argument for `{s}`: `{s}`\n", .{
|
||||
field.name,
|
||||
@errorName(err),
|
||||
});
|
||||
std.debug.print("- tried to parse: {s}\n", .{flag.*.Positional.value});
|
||||
std.debug.print("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
||||
log.err("- tried to parse: {s}\n", .{flag.*.Positional.value});
|
||||
log.err("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
||||
return error.InvalidPositional;
|
||||
};
|
||||
} else {
|
||||
std.debug.print("error: could not parse positional argument for {s}\n", .{field.name});
|
||||
log.err("could not parse positional argument for `{s}`: no parser provided\n", .{field.name});
|
||||
return error.InvalidPositional;
|
||||
}
|
||||
} else {
|
||||
std.debug.print("error: could not find positional argument for {s}\n", .{field.name});
|
||||
log.err("could not find positional argument for `{s}`\n", .{field.name});
|
||||
return error.NoArgumentFound;
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
const std = @import("std");
|
||||
const t = std.testing;
|
||||
|
||||
pub fn notEmpty(value: []const u8) anyerror!bool {
|
||||
if (value.len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
test "not empty" {
|
||||
try t.expect(try notEmpty("") == false);
|
||||
try t.expect(try notEmpty("a"));
|
||||
}
|
||||
|
||||
pub fn notWhiteSpace(value: []const u8) anyerror!bool {
|
||||
if (std.mem.trim(u8, value, " \t\n\r").len == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
test "not white space" {
|
||||
try t.expect(try notWhiteSpace("") == false);
|
||||
try t.expect(try notWhiteSpace("\n\r\t ") == false);
|
||||
try t.expect(try notWhiteSpace("a"));
|
||||
try t.expect(try notWhiteSpace(" a"));
|
||||
try t.expect(try notWhiteSpace("\ta"));
|
||||
try t.expect(try notWhiteSpace("\n\r\t a"));
|
||||
}
|
||||
|
|
@ -1,37 +0,0 @@
|
|||
const std = @import("std");
|
||||
|
||||
/// A function that validates a string value.
|
||||
/// It should return an error if the value is unparseable.
|
||||
/// Or `true` if the value is valid.
|
||||
/// Or `false` if the value is invalid.
|
||||
pub const ValidateSignature = *const fn (value: []const u8) anyerror!bool;
|
||||
|
||||
pub const string = @import("./string.zig");
|
||||
pub const number = @import("./number.zig");
|
||||
|
||||
comptime {
|
||||
const builtin = @import("builtin");
|
||||
|
||||
if (builtin.is_test) {
|
||||
std.mem.doNotOptimizeAway(string);
|
||||
std.mem.doNotOptimizeAway(number);
|
||||
}
|
||||
}
|
||||
|
||||
const this = @This();
|
||||
test "valid validator signatures" {
|
||||
const t = std.testing;
|
||||
|
||||
const decls = @typeInfo(this).Struct.decls;
|
||||
|
||||
inline for (decls) |decl| {
|
||||
const field = @field(this, decl.name);
|
||||
if (comptime std.mem.startsWith(u8, @typeName(field), "*")) {
|
||||
comptime continue; // skip `ValidateSignature`
|
||||
}
|
||||
|
||||
inline for (@typeInfo(field).Struct.decls) |function| {
|
||||
try t.expectEqual(ValidateSignature, @TypeOf(&@field(field, function.name)));
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue