parser: support unexposed values
Having thought about this more, it seems likely that complex converters could benefit from being able to parse their arguments on the fly without having to structure them into a rigid type. This is sort of a get out of jail free card for custom converters as they can dump into the user context type or whatever they want directly.
This commit is contained in:
parent
b01c10409d
commit
6ffc1c1a4c
@ -25,7 +25,6 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
||||
multi: bool = false,
|
||||
|
||||
pub fn arg_gen(comptime self: @This()) ParameterGenerics {
|
||||
if (self.OutputType == void) @compileError("argument must have OutputType specified");
|
||||
if (self.value_count == .flag) @compileError("argument may not be a flag");
|
||||
if (self.value_count == .count) @compileError("argument may not be a count");
|
||||
|
||||
@ -39,8 +38,8 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
||||
}
|
||||
|
||||
pub fn opt_gen(comptime self: @This()) ParameterGenerics {
|
||||
if (self.OutputType == void) @compileError("option must have OutputType specified");
|
||||
if (self.value_count == .flag) @compileError("option may not be a flag");
|
||||
if (self.value_count == .count) @compileError("option may not be a count");
|
||||
|
||||
return ParameterGenerics{
|
||||
.UserContext = UserContext,
|
||||
@ -246,6 +245,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
var env_var_fields: []const StructField = &[_]StructField{};
|
||||
|
||||
paramloop: for (spec, 0..) |param, idx| {
|
||||
const PType = @TypeOf(param);
|
||||
// these three blocks are to check for redundantly defined tags and
|
||||
// environment variables. This only works within a command. It
|
||||
// doesn't support compile time checks for conflict into
|
||||
@ -278,12 +278,13 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
.alignment = 0,
|
||||
}};
|
||||
|
||||
if (!PType.has_output) continue :paramloop;
|
||||
|
||||
while (flag_skip > 0) {
|
||||
flag_skip -= 1;
|
||||
continue :paramloop;
|
||||
}
|
||||
|
||||
const PType = @TypeOf(param);
|
||||
if (PType.is_flag) {
|
||||
var peek = idx + 1;
|
||||
var bias_seen: [ncmeta.enum_length(FlagBias)]bool = [_]bool{false} ** ncmeta.enum_length(FlagBias);
|
||||
|
@ -29,16 +29,20 @@ pub const FlagBias = enum {
|
||||
|
||||
pub const ParameterGenerics = struct {
|
||||
UserContext: type = void,
|
||||
/// If void, do not expose this parameter in the aggregate converted parameter
|
||||
/// object. The converter for this parameter shall not return a value. This may be
|
||||
/// useful for implementing complex conversion that produces output through its
|
||||
/// side effects or by modifying the user context.
|
||||
OutputType: type = void,
|
||||
param_type: ParameterType,
|
||||
value_count: ValueCount,
|
||||
/// allow this named parameter to be passed multiple times.
|
||||
/// values will be appended when it is encountered. If false, only the
|
||||
/// final encountered instance will be used.
|
||||
multi: bool,
|
||||
// since we now use multi in place of greedy values for simplicity, we may want to
|
||||
// convert this an enum or add an additional flag to distinguish between the
|
||||
// many-to-many and the many-to-one cases.
|
||||
multi: bool,
|
||||
|
||||
pub fn fixed_value_count(comptime OutputType: type, comptime value_count: ValueCount) ValueCount {
|
||||
return comptime if (value_count == .fixed)
|
||||
@ -54,14 +58,14 @@ pub const ParameterGenerics = struct {
|
||||
value_count;
|
||||
}
|
||||
|
||||
pub fn clone_without_multi(comptime self: @This()) @This() {
|
||||
return .{ .UserContext = self.UserContext, .OutputType = self.OutputType, .param_type = self.param_type, .value_count = self.value_count, .multi = false };
|
||||
}
|
||||
|
||||
pub fn has_context(comptime self: @This()) bool {
|
||||
return comptime self.UserContext != void;
|
||||
}
|
||||
|
||||
pub fn has_output(comptime self: @This()) bool {
|
||||
return self.OutputType != void;
|
||||
}
|
||||
|
||||
pub fn is_flag(comptime self: @This()) bool {
|
||||
return comptime switch (self.value_count) {
|
||||
.flag, .count => true,
|
||||
@ -71,7 +75,7 @@ pub const ParameterGenerics = struct {
|
||||
|
||||
pub fn ConvertedType(comptime self: @This()) type {
|
||||
// is this the correct way to collapse this?
|
||||
return comptime if (self.multi and self.value_count != .count)
|
||||
return comptime if (self.multi and self.value_count != .count and self.OutputType != void)
|
||||
std.ArrayList(self.ReturnValue())
|
||||
else
|
||||
self.ReturnValue();
|
||||
@ -184,6 +188,7 @@ fn OptionType(comptime generics: ParameterGenerics) type {
|
||||
pub const is_flag: bool = generics.is_flag();
|
||||
pub const value_count: ValueCount = generics.value_count;
|
||||
pub const multi: bool = generics.multi;
|
||||
pub const has_output: bool = generics.has_output();
|
||||
|
||||
name: []const u8,
|
||||
short_tag: ?[]const u8,
|
||||
|
@ -425,11 +425,17 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
if (@field(self.intermediate, param.name)) |intermediate| {
|
||||
var buffer = std.ArrayList(u8).init(self.allocator);
|
||||
const writer = buffer.writer();
|
||||
|
||||
if (comptime @TypeOf(param).has_output) {
|
||||
@field(self.output, param.name) = try param.converter(context, intermediate, writer);
|
||||
} else {
|
||||
try param.converter(context, intermediate, writer);
|
||||
}
|
||||
} else {
|
||||
if (comptime param.required) {
|
||||
return ParseError.RequiredParameterMissing;
|
||||
} else if (comptime param.default) |def| {
|
||||
} else if (comptime @TypeOf(param).has_output) {
|
||||
if (comptime param.default) |def| {
|
||||
// this has to be explicitly set because even though we set it as
|
||||
// the field default, it gets clobbered because self.output is
|
||||
// initialized as undefined.
|
||||
@ -440,6 +446,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help(self: *@This(), name: []const u8) noreturn {
|
||||
defer std.process.exit(0);
|
||||
|
Loading…
x
Reference in New Issue
Block a user