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 std = @import("std");
|
||||||
|
|
||||||
|
const log = std.log.scoped(.args);
|
||||||
|
|
||||||
pub const Arg = union(enum(u2)) {
|
pub const Arg = union(enum(u2)) {
|
||||||
Flag: struct {
|
Flag: struct {
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
|
|
@ -75,7 +77,7 @@ pub const Arg = union(enum(u2)) {
|
||||||
switch (flag) {
|
switch (flag) {
|
||||||
.Flag => |f| {
|
.Flag => |f| {
|
||||||
if (f.name.len == 0) {
|
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;
|
return error.InvalidFlag;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
||||||
|
|
@ -3,10 +3,11 @@ const std = @import("std");
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
pub const parsers = @import("./parsers/parsers.zig");
|
pub const parsers = @import("./parsers/parsers.zig");
|
||||||
pub const validators = @import("./validators/validators.zig");
|
|
||||||
|
|
||||||
const Arg = @import("./arg.zig").Arg;
|
const Arg = @import("./arg.zig").Arg;
|
||||||
|
|
||||||
|
const log = std.log.scoped(.args);
|
||||||
|
|
||||||
/// Metadata for a field that is parseable by the argument parser.
|
/// Metadata for a field that is parseable by the argument parser.
|
||||||
pub const Extra = union(enum(u2)) {
|
pub const Extra = union(enum(u2)) {
|
||||||
Positional,
|
Positional,
|
||||||
|
|
@ -31,13 +32,6 @@ pub fn Marker(comptime T: type) type {
|
||||||
///
|
///
|
||||||
/// However, it is recommended to use `lys.args.parsers` for common types.
|
/// However, it is recommended to use `lys.args.parsers` for common types.
|
||||||
parse: ?parsers.ParseSignature(T) = null,
|
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.
|
/// Does the actual work of initializing T from flags.
|
||||||
fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
comptime {
|
comptime {
|
||||||
std.debug.assert(@hasField(T, "allocator"));
|
if (!@hasField(T, "allocator"))
|
||||||
std.debug.assert(@hasDecl(T, "deinit"));
|
@compileError("T must have an allocator");
|
||||||
|
if (!@hasDecl(T, "deinit"))
|
||||||
|
@compileError("T must have a deinit function");
|
||||||
}
|
}
|
||||||
|
|
||||||
var result = T{
|
var result = T{
|
||||||
|
|
@ -154,7 +150,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
switch (extra) {
|
switch (extra) {
|
||||||
.Flag => |f| {
|
.Flag => |f| {
|
||||||
if (!(f.takesValue or f.toggle)) {
|
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;
|
return error.InvalidFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -167,7 +163,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
fie.*.value = true;
|
fie.*.value = true;
|
||||||
comptime continue;
|
comptime continue;
|
||||||
} else {
|
} 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,
|
f.name,
|
||||||
@typeName(@TypeOf(fie.*.value)),
|
@typeName(@TypeOf(fie.*.value)),
|
||||||
});
|
});
|
||||||
|
|
@ -182,25 +178,36 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
comptime continue;
|
comptime continue;
|
||||||
} else {
|
} else {
|
||||||
if (fie.*.parse) |parse_fn| {
|
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;
|
comptime continue;
|
||||||
} else {
|
} 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;
|
return error.NoValueForFlag;
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable;
|
unreachable;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
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;
|
return error.InvalidFlag;
|
||||||
} else {
|
} 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;
|
return error.InvalidFlag;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -213,7 +220,7 @@ fn initFromParsed(comptime T: type, allocator: Allocator, flags: []Arg) !T {
|
||||||
|
|
||||||
switch (flag.*) {
|
switch (flag.*) {
|
||||||
.Flag => |f| {
|
.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.name,
|
||||||
f.value,
|
f.value,
|
||||||
});
|
});
|
||||||
|
|
@ -228,7 +235,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 {
|
||||||
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,
|
field.name,
|
||||||
@typeName(@TypeOf(fie.*.value)),
|
@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);
|
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| {
|
||||||
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,
|
field.name,
|
||||||
@errorName(err),
|
@errorName(err),
|
||||||
});
|
});
|
||||||
std.debug.print("- tried to parse: {s}\n", .{flag.*.Positional.value});
|
log.err("- tried to parse: {s}\n", .{flag.*.Positional.value});
|
||||||
std.debug.print("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
log.err("- expected type: {s}\n", .{@typeName(@TypeOf(fie.*.value))});
|
||||||
return error.InvalidPositional;
|
return error.InvalidPositional;
|
||||||
};
|
};
|
||||||
} else {
|
} 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;
|
return error.InvalidPositional;
|
||||||
}
|
}
|
||||||
} else {
|
} 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;
|
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