command/parser: sketch out help flag integration
This is a special case flag that cannot be replicated with the normal machinery. It's much easier to special case it. So here we go.
This commit is contained in:
parent
7c9273605d
commit
c3b31b2274
@ -6,7 +6,11 @@ const CommandBuilder = noclip.CommandBuilder;
|
||||
const Choice = enum { first, second };
|
||||
|
||||
const cli = cmd: {
|
||||
var cmd = CommandBuilder(u32).init();
|
||||
var cmd = CommandBuilder(u32).init(
|
||||
\\The definitive noclip demonstration utility
|
||||
\\
|
||||
\\This command demonstrates the functionality of the noclip library. cool!!
|
||||
);
|
||||
cmd.add_option(.{ .OutputType = struct { u8, u8 } }, .{
|
||||
.name = "test",
|
||||
.short_tag = "-t",
|
||||
@ -51,7 +55,11 @@ const cli = cmd: {
|
||||
};
|
||||
|
||||
const subcommand = cmd: {
|
||||
var cmd = CommandBuilder(void).init();
|
||||
var cmd = CommandBuilder(void).init(
|
||||
\\Perform some sort of work
|
||||
\\
|
||||
\\This subcommand is a mystery. It probably does something, but nobody is sure what.
|
||||
);
|
||||
cmd.add_flag(.{}, .{
|
||||
.name = "flag",
|
||||
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
||||
@ -78,10 +86,10 @@ pub fn main() !void {
|
||||
defer arena.deinit();
|
||||
const allocator = arena.allocator();
|
||||
|
||||
var parser = cli.bind(cli_handler, allocator);
|
||||
var parser = cli.create_parser(cli_handler, allocator);
|
||||
var context: u32 = 2;
|
||||
|
||||
var subcon = subcommand.bind(sub_handler, allocator);
|
||||
var subcon = subcommand.create_parser(sub_handler, allocator);
|
||||
try parser.add_subcommand("verb", subcon.interface());
|
||||
|
||||
const iface = parser.interface(&context);
|
||||
|
@ -9,6 +9,7 @@ const ValueCount = parameters.ValueCount;
|
||||
const ParameterGenerics = parameters.ParameterGenerics;
|
||||
const OptionConfig = parameters.OptionConfig;
|
||||
const FlagConfig = parameters.FlagConfig;
|
||||
const ShortLongPair = parameters.ShortLongPair;
|
||||
const FlagBias = parameters.FlagBias;
|
||||
const make_option = parameters.make_option;
|
||||
const make_argument = parameters.make_argument;
|
||||
@ -72,13 +73,56 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
||||
}
|
||||
|
||||
pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
const HelpFlagOption = OptionConfig(.{
|
||||
.UserContext = UserContext,
|
||||
.OutputType = bool,
|
||||
.param_type = .Nominal,
|
||||
.value_count = .flag,
|
||||
.multi = false,
|
||||
});
|
||||
|
||||
return struct {
|
||||
param_spec: ncmeta.MutableTuple = .{},
|
||||
param_spec: ncmeta.TupleBuilder = .{},
|
||||
// this is a strange hack, but it's easily the path of least resistance
|
||||
help_flag: ShortLongPair = .{ .short_tag = "-h", .long_tag = "--help" },
|
||||
description: []const u8,
|
||||
/// if any subcommands are provided, one of them must be specified, or the command has failed.
|
||||
subcommand_required: bool = true,
|
||||
|
||||
pub const UserContextType = UserContext;
|
||||
|
||||
pub fn init() @This() {
|
||||
return .{};
|
||||
pub fn init(comptime description: []const u8) @This() {
|
||||
return .{ .description = description };
|
||||
}
|
||||
|
||||
pub fn create_parser(
|
||||
comptime self: @This(),
|
||||
comptime callback: self.CallbackSignature(),
|
||||
allocator: std.mem.Allocator,
|
||||
) Parser(self, callback) {
|
||||
return Parser(self, callback){
|
||||
.allocator = allocator,
|
||||
.subcommands = std.hash_map.StringHashMap(ParserInterface).init(allocator),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn set_help_flag(
|
||||
comptime self: *@This(),
|
||||
comptime tags: ShortLongPair,
|
||||
) void {
|
||||
self.help_flag = tags;
|
||||
}
|
||||
|
||||
pub fn mock_help_parameter(comptime self: @This()) ?HelpFlagOption {
|
||||
if (self.help_flag.short_tag == null and self.help_flag.long_tag == null) return null;
|
||||
|
||||
return HelpFlagOption{
|
||||
.name = "_internal_help_flag",
|
||||
.short_tag = self.help_flag.short_tag,
|
||||
.long_tag = self.help_flag.long_tag,
|
||||
.description = "Print this help message and exit",
|
||||
.flag_bias = true,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn add_argument(
|
||||
@ -104,15 +148,6 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
self.param_spec.add(make_option(bgen.opt_gen(), config));
|
||||
}
|
||||
|
||||
pub fn set_help_flag(
|
||||
comptime self: *@This(),
|
||||
comptime bgen: BuilderGenerics(UserContext),
|
||||
comptime config: FlagConfig(bgen.flag_gen()),
|
||||
) void {
|
||||
_ = self;
|
||||
_ = config;
|
||||
}
|
||||
|
||||
pub fn add_flag(
|
||||
comptime self: *@This(),
|
||||
comptime bgen: BuilderGenerics(UserContext),
|
||||
@ -372,16 +407,5 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
||||
} });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn bind(
|
||||
comptime self: @This(),
|
||||
comptime callback: self.CallbackSignature(),
|
||||
allocator: std.mem.Allocator,
|
||||
) Parser(self, callback) {
|
||||
return Parser(self, callback){
|
||||
.allocator = allocator,
|
||||
.subcommands = std.hash_map.StringHashMap(ParserInterface).init(allocator),
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
@ -112,7 +112,7 @@ pub fn copy_struct(comptime T: type, source: T, field_overrides: anytype) T {
|
||||
/// Stores type-erased pointers to items in comptime extensible data structures,
|
||||
/// which allows e.g. assembling a tuple through multiple calls rather than all
|
||||
/// at once.
|
||||
pub const MutableTuple = struct {
|
||||
pub const TupleBuilder = struct {
|
||||
pointers: []const *const anyopaque = &[0]*const anyopaque{},
|
||||
types: []const type = &[0]type{},
|
||||
|
||||
|
@ -155,12 +155,12 @@ pub fn OptionConfig(comptime generics: ParameterGenerics) type {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn FlagConfig(comptime generics: ParameterGenerics) type {
|
||||
const ShortLongPair = struct {
|
||||
pub const ShortLongPair = struct {
|
||||
short_tag: ?[]const u8 = null,
|
||||
long_tag: ?[]const u8 = null,
|
||||
};
|
||||
|
||||
pub fn FlagConfig(comptime generics: ParameterGenerics) type {
|
||||
return struct {
|
||||
name: []const u8,
|
||||
|
||||
|
@ -62,9 +62,9 @@ fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
|
||||
// be extremely type-sloppy here, which simplifies the signature.
|
||||
pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
const UserContext = @TypeOf(command).UserContextType;
|
||||
const parameters = command.generate();
|
||||
const Intermediate = command.Intermediate();
|
||||
const Output = command.Output();
|
||||
const parameters = command.generate();
|
||||
|
||||
return struct {
|
||||
intermediate: Intermediate = .{},
|
||||
@ -237,6 +237,10 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
arg: []const u8,
|
||||
argit: *ncmeta.SliceIterator([][:0]u8),
|
||||
) ParseError!void {
|
||||
if (comptime command.help_flag.long_tag) |long|
|
||||
if (std.mem.eql(u8, arg, long))
|
||||
self.print_help();
|
||||
|
||||
inline for (comptime parameters) |param| {
|
||||
const PType = @TypeOf(param);
|
||||
// removing the comptime here causes the compiler to die
|
||||
@ -263,6 +267,10 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
remaining: usize,
|
||||
argit: *ncmeta.SliceIterator([][:0]u8),
|
||||
) ParseError!void {
|
||||
if (comptime command.help_flag.short_tag) |short|
|
||||
if (arg == short[1])
|
||||
self.print_help();
|
||||
|
||||
inline for (comptime parameters) |param| {
|
||||
const PType = @TypeOf(param);
|
||||
// removing the comptime here causes the compiler to die
|
||||
@ -414,5 +422,11 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn print_help(self: @This()) void {
|
||||
_ = self;
|
||||
std.debug.print("help!!!\n", .{});
|
||||
std.process.exit(0);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user