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 Choice = enum { first, second };
|
||||||
|
|
||||||
const cli = cmd: {
|
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 } }, .{
|
cmd.add_option(.{ .OutputType = struct { u8, u8 } }, .{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.short_tag = "-t",
|
.short_tag = "-t",
|
||||||
@ -51,7 +55,11 @@ const cli = cmd: {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const subcommand = 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(.{}, .{
|
cmd.add_flag(.{}, .{
|
||||||
.name = "flag",
|
.name = "flag",
|
||||||
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
||||||
@ -78,10 +86,10 @@ pub fn main() !void {
|
|||||||
defer arena.deinit();
|
defer arena.deinit();
|
||||||
const allocator = arena.allocator();
|
const allocator = arena.allocator();
|
||||||
|
|
||||||
var parser = cli.bind(cli_handler, allocator);
|
var parser = cli.create_parser(cli_handler, allocator);
|
||||||
var context: u32 = 2;
|
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());
|
try parser.add_subcommand("verb", subcon.interface());
|
||||||
|
|
||||||
const iface = parser.interface(&context);
|
const iface = parser.interface(&context);
|
||||||
|
@ -9,6 +9,7 @@ const ValueCount = parameters.ValueCount;
|
|||||||
const ParameterGenerics = parameters.ParameterGenerics;
|
const ParameterGenerics = parameters.ParameterGenerics;
|
||||||
const OptionConfig = parameters.OptionConfig;
|
const OptionConfig = parameters.OptionConfig;
|
||||||
const FlagConfig = parameters.FlagConfig;
|
const FlagConfig = parameters.FlagConfig;
|
||||||
|
const ShortLongPair = parameters.ShortLongPair;
|
||||||
const FlagBias = parameters.FlagBias;
|
const FlagBias = parameters.FlagBias;
|
||||||
const make_option = parameters.make_option;
|
const make_option = parameters.make_option;
|
||||||
const make_argument = parameters.make_argument;
|
const make_argument = parameters.make_argument;
|
||||||
@ -72,13 +73,56 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn CommandBuilder(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 {
|
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 const UserContextType = UserContext;
|
||||||
|
|
||||||
pub fn init() @This() {
|
pub fn init(comptime description: []const u8) @This() {
|
||||||
return .{};
|
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(
|
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));
|
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(
|
pub fn add_flag(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime bgen: BuilderGenerics(UserContext),
|
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,
|
/// Stores type-erased pointers to items in comptime extensible data structures,
|
||||||
/// which allows e.g. assembling a tuple through multiple calls rather than all
|
/// which allows e.g. assembling a tuple through multiple calls rather than all
|
||||||
/// at once.
|
/// at once.
|
||||||
pub const MutableTuple = struct {
|
pub const TupleBuilder = struct {
|
||||||
pointers: []const *const anyopaque = &[0]*const anyopaque{},
|
pointers: []const *const anyopaque = &[0]*const anyopaque{},
|
||||||
types: []const type = &[0]type{},
|
types: []const type = &[0]type{},
|
||||||
|
|
||||||
|
@ -155,12 +155,12 @@ pub fn OptionConfig(comptime generics: ParameterGenerics) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn FlagConfig(comptime generics: ParameterGenerics) type {
|
pub const ShortLongPair = struct {
|
||||||
const ShortLongPair = struct {
|
short_tag: ?[]const u8 = null,
|
||||||
short_tag: ?[]const u8 = null,
|
long_tag: ?[]const u8 = null,
|
||||||
long_tag: ?[]const u8 = null,
|
};
|
||||||
};
|
|
||||||
|
|
||||||
|
pub fn FlagConfig(comptime generics: ParameterGenerics) type {
|
||||||
return struct {
|
return struct {
|
||||||
name: []const u8,
|
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.
|
// be extremely type-sloppy here, which simplifies the signature.
|
||||||
pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
||||||
const UserContext = @TypeOf(command).UserContextType;
|
const UserContext = @TypeOf(command).UserContextType;
|
||||||
|
const parameters = command.generate();
|
||||||
const Intermediate = command.Intermediate();
|
const Intermediate = command.Intermediate();
|
||||||
const Output = command.Output();
|
const Output = command.Output();
|
||||||
const parameters = command.generate();
|
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
intermediate: Intermediate = .{},
|
intermediate: Intermediate = .{},
|
||||||
@ -237,6 +237,10 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
arg: []const u8,
|
arg: []const u8,
|
||||||
argit: *ncmeta.SliceIterator([][:0]u8),
|
argit: *ncmeta.SliceIterator([][:0]u8),
|
||||||
) ParseError!void {
|
) 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| {
|
inline for (comptime parameters) |param| {
|
||||||
const PType = @TypeOf(param);
|
const PType = @TypeOf(param);
|
||||||
// removing the comptime here causes the compiler to die
|
// 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,
|
remaining: usize,
|
||||||
argit: *ncmeta.SliceIterator([][:0]u8),
|
argit: *ncmeta.SliceIterator([][:0]u8),
|
||||||
) ParseError!void {
|
) ParseError!void {
|
||||||
|
if (comptime command.help_flag.short_tag) |short|
|
||||||
|
if (arg == short[1])
|
||||||
|
self.print_help();
|
||||||
|
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
const PType = @TypeOf(param);
|
const PType = @TypeOf(param);
|
||||||
// removing the comptime here causes the compiler to die
|
// 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