Compare commits
10 Commits
ad17458e45
...
80c4853171
Author | SHA1 | Date | |
---|---|---|---|
80c4853171 | |||
883218cdca | |||
d091de5686 | |||
29175d07ce | |||
86342bcd1f | |||
adf05ca489 | |||
71653858ab | |||
d1803284b4 | |||
5f0d7b34d7 | |||
efbc6e7b66 |
11
build.zig
11
build.zig
@ -5,7 +5,7 @@ pub fn build(b: *std.Build) void {
|
|||||||
const optimize: std.builtin.Mode = b.standardOptimizeOption(.{});
|
const optimize: std.builtin.Mode = b.standardOptimizeOption(.{});
|
||||||
|
|
||||||
const noclip = b.addModule("noclip", .{
|
const noclip = b.addModule("noclip", .{
|
||||||
.source_file = .{ .path = "source/noclip.zig" }
|
.source_file = .{ .path = "source/noclip.zig" },
|
||||||
});
|
});
|
||||||
|
|
||||||
demo(b, noclip, target, optimize);
|
demo(b, noclip, target, optimize);
|
||||||
@ -21,7 +21,12 @@ pub fn build(b: *std.Build) void {
|
|||||||
test_step.dependOn(&tests.step);
|
test_step.dependOn(&tests.step);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn demo(b: *std.Build, noclip: *std.Build.Module, target: std.zig.CrossTarget, optimize: std.builtin.Mode) void {
|
fn demo(
|
||||||
|
b: *std.Build,
|
||||||
|
noclip: *std.Build.Module,
|
||||||
|
target: std.zig.CrossTarget,
|
||||||
|
optimize: std.builtin.Mode,
|
||||||
|
) void {
|
||||||
const demo_step = b.step("demo", "Build and install CLI demo program");
|
const demo_step = b.step("demo", "Build and install CLI demo program");
|
||||||
|
|
||||||
const exe = b.addExecutable(.{
|
const exe = b.addExecutable(.{
|
||||||
@ -31,7 +36,7 @@ fn demo(b: *std.Build, noclip: *std.Build.Module, target: std.zig.CrossTarget, o
|
|||||||
.optimize = optimize,
|
.optimize = optimize,
|
||||||
});
|
});
|
||||||
exe.addModule("noclip", noclip);
|
exe.addModule("noclip", noclip);
|
||||||
const install_demo = b.addInstallArtifact(exe);
|
const install_demo = b.addInstallArtifact(exe, .{});
|
||||||
|
|
||||||
demo_step.dependOn(&install_demo.step);
|
demo_step.dependOn(&install_demo.step);
|
||||||
}
|
}
|
||||||
|
@ -13,7 +13,7 @@ const cli = cmd: {
|
|||||||
\\This command demonstrates the functionality of the noclip library. cool!
|
\\This command demonstrates the functionality of the noclip library. cool!
|
||||||
,
|
,
|
||||||
};
|
};
|
||||||
cmd.add_option(.{ .OutputType = struct { u8, u8 } }, .{
|
cmd.addOption(.{ .OutputType = struct { u8, u8 } }, .{
|
||||||
.name = "test",
|
.name = "test",
|
||||||
.short_tag = "-t",
|
.short_tag = "-t",
|
||||||
.long_tag = "--test",
|
.long_tag = "--test",
|
||||||
@ -21,7 +21,7 @@ const cli = cmd: {
|
|||||||
.description = "multi-value test option",
|
.description = "multi-value test option",
|
||||||
.nice_type_name = "int> <int",
|
.nice_type_name = "int> <int",
|
||||||
});
|
});
|
||||||
cmd.add_option(.{ .OutputType = Choice }, .{
|
cmd.addOption(.{ .OutputType = Choice }, .{
|
||||||
.name = "choice",
|
.name = "choice",
|
||||||
.short_tag = "-c",
|
.short_tag = "-c",
|
||||||
.long_tag = "--choice",
|
.long_tag = "--choice",
|
||||||
@ -30,7 +30,7 @@ const cli = cmd: {
|
|||||||
.description = "enum choice option",
|
.description = "enum choice option",
|
||||||
.nice_type_name = "choice",
|
.nice_type_name = "choice",
|
||||||
});
|
});
|
||||||
cmd.add_option(.{ .OutputType = u32 }, .{
|
cmd.addOption(.{ .OutputType = u32 }, .{
|
||||||
.name = "default",
|
.name = "default",
|
||||||
.short_tag = "-d",
|
.short_tag = "-d",
|
||||||
.long_tag = "--default",
|
.long_tag = "--default",
|
||||||
@ -39,33 +39,29 @@ const cli = cmd: {
|
|||||||
.description = "default value integer option",
|
.description = "default value integer option",
|
||||||
.nice_type_name = "uint",
|
.nice_type_name = "uint",
|
||||||
});
|
});
|
||||||
cmd.add_option(.{ .OutputType = u8, .multi = true }, .{
|
cmd.addOption(.{ .OutputType = u8, .multi = true }, .{
|
||||||
.name = "multi",
|
.name = "multi",
|
||||||
.short_tag = "-m",
|
.short_tag = "-m",
|
||||||
.long_tag = "--multi",
|
.long_tag = "--multi",
|
||||||
.description = "multiple specification test option",
|
.description = "multiple specification test option",
|
||||||
});
|
});
|
||||||
cmd.add_flag(.{}, .{
|
cmd.addFlag(.{}, .{
|
||||||
.name = "flag",
|
.name = "flag",
|
||||||
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
||||||
.falsy = .{ .short_tag = "-F", .long_tag = "--no-flag" },
|
.falsy = .{ .short_tag = "-F", .long_tag = "--no-flag" },
|
||||||
.env_var = "NOCLIP_FLAG",
|
.env_var = "NOCLIP_FLAG",
|
||||||
.description = "boolean flag",
|
.description = "boolean flag",
|
||||||
});
|
});
|
||||||
cmd.add_flag(.{ .multi = true }, .{
|
cmd.addFlag(.{ .multi = true }, .{
|
||||||
.name = "multiflag",
|
.name = "multiflag",
|
||||||
.truthy = .{ .short_tag = "-M" },
|
.truthy = .{ .short_tag = "-M" },
|
||||||
.description = "multiple specification test flag ",
|
.description = "multiple specification test flag ",
|
||||||
});
|
});
|
||||||
cmd.add_option(.{ .OutputType = u8 }, .{
|
cmd.addOption(.{ .OutputType = u8 }, .{
|
||||||
.name = "env",
|
.name = "env",
|
||||||
.env_var = "NOCLIP_ENVIRON",
|
.env_var = "NOCLIP_ENVIRON",
|
||||||
.description = "environment variable only option",
|
.description = "environment variable only option",
|
||||||
});
|
});
|
||||||
cmd.add_argument(.{ .OutputType = []const u8 }, .{
|
|
||||||
.name = "arg",
|
|
||||||
.description = "This is an argument that doesn't really do anything, but it's very important.",
|
|
||||||
});
|
|
||||||
|
|
||||||
break :cmd cmd;
|
break :cmd cmd;
|
||||||
};
|
};
|
||||||
@ -73,27 +69,31 @@ const cli = cmd: {
|
|||||||
const subcommand = cmd: {
|
const subcommand = cmd: {
|
||||||
var cmd = CommandBuilder([]const u8){
|
var cmd = CommandBuilder([]const u8){
|
||||||
.description =
|
.description =
|
||||||
\\Perform some sort of work
|
\\Demonstrate subcommand functionality
|
||||||
\\
|
\\
|
||||||
\\This subcommand is a mystery. It probably does something, but nobody is sure what.
|
\\This command demonstrates how subcommands work.
|
||||||
,
|
,
|
||||||
};
|
};
|
||||||
cmd.simple_flag(.{
|
cmd.simpleFlag(.{
|
||||||
.name = "flag",
|
.name = "flag",
|
||||||
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
.truthy = .{ .short_tag = "-f", .long_tag = "--flag" },
|
||||||
.falsy = .{ .long_tag = "--no-flag" },
|
.falsy = .{ .long_tag = "--no-flag" },
|
||||||
.env_var = "NOCLIP_SUBFLAG",
|
.env_var = "NOCLIP_SUBFLAG",
|
||||||
});
|
});
|
||||||
cmd.add_argument(.{ .OutputType = []const u8 }, .{ .name = "argument" });
|
cmd.addArgument(.{ .OutputType = []const u8 }, .{ .name = "argument" });
|
||||||
|
cmd.addArgument(.{ .OutputType = []const u8 }, .{
|
||||||
|
.name = "arg",
|
||||||
|
.description = "This is an argument that doesn't really do anything, but it's very important.",
|
||||||
|
});
|
||||||
break :cmd cmd;
|
break :cmd cmd;
|
||||||
};
|
};
|
||||||
|
|
||||||
fn sub_handler(context: []const u8, result: subcommand.Output()) !void {
|
fn subHandler(context: []const u8, result: subcommand.Output()) !void {
|
||||||
std.debug.print("subcommand: {s}\n", .{result.argument});
|
std.debug.print("subcommand: {s}\n", .{result.argument});
|
||||||
std.debug.print("context: {s}\n", .{context});
|
std.debug.print("context: {s}\n", .{context});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cli_handler(context: *u32, result: cli.Output()) !void {
|
fn cliHandler(context: *u32, result: cli.Output()) !void {
|
||||||
std.debug.print("context: {d}\n", .{context.*});
|
std.debug.print("context: {d}\n", .{context.*});
|
||||||
std.debug.print("callback is working {any}\n", .{result.choice});
|
std.debug.print("callback is working {any}\n", .{result.choice});
|
||||||
std.debug.print("callback is working {d}\n", .{result.default});
|
std.debug.print("callback is working {d}\n", .{result.default});
|
||||||
@ -101,16 +101,18 @@ fn cli_handler(context: *u32, result: cli.Output()) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn main() !u8 {
|
pub fn main() !u8 {
|
||||||
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer arena.deinit();
|
defer _ = gpa.deinit();
|
||||||
const allocator = arena.allocator();
|
const allocator = gpa.allocator();
|
||||||
|
|
||||||
|
var parser = try cli.createParser(cliHandler, allocator);
|
||||||
|
defer parser.deinitTree();
|
||||||
|
|
||||||
var parser = cli.create_parser(cli_handler, allocator);
|
|
||||||
var context: u32 = 2;
|
var context: u32 = 2;
|
||||||
const sc: []const u8 = "whassup";
|
const sc: []const u8 = "whassup";
|
||||||
|
|
||||||
var subcon = subcommand.create_parser(sub_handler, allocator);
|
var subcon = try subcommand.createParser(subHandler, allocator);
|
||||||
try parser.add_subcommand("verb", subcon.interface(&sc));
|
try parser.addSubcommand("verb", subcon.interface(&sc));
|
||||||
|
|
||||||
const iface = parser.interface(&context);
|
const iface = parser.interface(&context);
|
||||||
iface.execute() catch return 1;
|
iface.execute() catch return 1;
|
||||||
|
@ -22,8 +22,7 @@ ____
|
|||||||
|
|
||||||
== Hello
|
== Hello
|
||||||
|
|
||||||
Requires a pre-release version of Zig `0.11.0`. `0.11.0-dev.1844+75ff34db9` is the
|
Requires Zig `0.11.x`.
|
||||||
oldest version specifically known to me to work.
|
|
||||||
|
|
||||||
=== Features
|
=== Features
|
||||||
|
|
||||||
@ -40,7 +39,7 @@ and bugs likely exist.
|
|||||||
* arbitrarily nestable subcommands for building sophisticated tools
|
* arbitrarily nestable subcommands for building sophisticated tools
|
||||||
* parser that supports many conventional CLI behaviors
|
* parser that supports many conventional CLI behaviors
|
||||||
** congealed short flags (e.g. `-abc` is the same as `-a -b -c`)
|
** congealed short flags (e.g. `-abc` is the same as `-a -b -c`)
|
||||||
** `--` to force early of argument parsing
|
** `--` to force early end of flag parsing
|
||||||
** both `--long value` and `--long=value` styles are supported
|
** both `--long value` and `--long=value` styles are supported
|
||||||
* option values are converted into rich native types using conversion callback functions
|
* option values are converted into rich native types using conversion callback functions
|
||||||
** integers
|
** integers
|
||||||
|
@ -12,8 +12,8 @@ const OptionConfig = parameters.OptionConfig;
|
|||||||
const FlagConfig = parameters.FlagConfig;
|
const FlagConfig = parameters.FlagConfig;
|
||||||
const ShortLongPair = parameters.ShortLongPair;
|
const ShortLongPair = parameters.ShortLongPair;
|
||||||
const FlagBias = parameters.FlagBias;
|
const FlagBias = parameters.FlagBias;
|
||||||
const make_option = parameters.make_option;
|
const makeOption = parameters.makeOption;
|
||||||
const make_argument = parameters.make_argument;
|
const makeArgument = parameters.makeArgument;
|
||||||
|
|
||||||
const Parser = parser.Parser;
|
const Parser = parser.Parser;
|
||||||
const ParserInterface = parser.ParserInterface;
|
const ParserInterface = parser.ParserInterface;
|
||||||
@ -24,7 +24,7 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
|||||||
value_count: ValueCount = .{ .fixed = 1 },
|
value_count: ValueCount = .{ .fixed = 1 },
|
||||||
multi: bool = false,
|
multi: bool = false,
|
||||||
|
|
||||||
pub fn arg_gen(comptime self: @This()) ParameterGenerics {
|
pub fn argGen(comptime self: @This()) ParameterGenerics {
|
||||||
if (self.value_count == .flag) @compileError("argument may not be a flag");
|
if (self.value_count == .flag) @compileError("argument may not be a flag");
|
||||||
if (self.value_count == .count) @compileError("argument may not be a count");
|
if (self.value_count == .count) @compileError("argument may not be a count");
|
||||||
|
|
||||||
@ -32,12 +32,12 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
|||||||
.UserContext = UserContext,
|
.UserContext = UserContext,
|
||||||
.OutputType = self.OutputType,
|
.OutputType = self.OutputType,
|
||||||
.param_type = .Ordinal,
|
.param_type = .Ordinal,
|
||||||
.value_count = ParameterGenerics.fixed_value_count(self.OutputType, self.value_count),
|
.value_count = ParameterGenerics.fixedValueCount(self.OutputType, self.value_count),
|
||||||
.multi = self.multi,
|
.multi = self.multi,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn opt_gen(comptime self: @This()) ParameterGenerics {
|
pub fn optGen(comptime self: @This()) ParameterGenerics {
|
||||||
if (self.value_count == .flag) @compileError("option may not be a flag");
|
if (self.value_count == .flag) @compileError("option may not be a flag");
|
||||||
if (self.value_count == .count) @compileError("option may not be a count");
|
if (self.value_count == .count) @compileError("option may not be a count");
|
||||||
|
|
||||||
@ -45,12 +45,12 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
|||||||
.UserContext = UserContext,
|
.UserContext = UserContext,
|
||||||
.OutputType = self.OutputType,
|
.OutputType = self.OutputType,
|
||||||
.param_type = .Nominal,
|
.param_type = .Nominal,
|
||||||
.value_count = ParameterGenerics.fixed_value_count(self.OutputType, self.value_count),
|
.value_count = ParameterGenerics.fixedValueCount(self.OutputType, self.value_count),
|
||||||
.multi = self.multi,
|
.multi = self.multi,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn count_gen(comptime _: @This()) ParameterGenerics {
|
pub fn countGen(comptime _: @This()) ParameterGenerics {
|
||||||
return ParameterGenerics{
|
return ParameterGenerics{
|
||||||
.UserContext = UserContext,
|
.UserContext = UserContext,
|
||||||
.OutputType = usize,
|
.OutputType = usize,
|
||||||
@ -60,7 +60,7 @@ fn BuilderGenerics(comptime UserContext: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn flag_gen(comptime self: @This()) ParameterGenerics {
|
pub fn flagGen(comptime self: @This()) ParameterGenerics {
|
||||||
return ParameterGenerics{
|
return ParameterGenerics{
|
||||||
.UserContext = UserContext,
|
.UserContext = UserContext,
|
||||||
.OutputType = bool,
|
.OutputType = bool,
|
||||||
@ -83,19 +83,50 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
|
|
||||||
pub const UserContextType = UserContext;
|
pub const UserContextType = UserContext;
|
||||||
|
|
||||||
pub fn create_parser(
|
pub fn createParser(
|
||||||
comptime self: @This(),
|
comptime self: @This(),
|
||||||
comptime callback: self.CallbackSignature(),
|
comptime callback: self.CallbackSignature(),
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
) Parser(self, callback) {
|
) !Parser(self, callback) {
|
||||||
|
// note: this is freed in Parser.deinit
|
||||||
|
var arena = try allocator.create(std.heap.ArenaAllocator);
|
||||||
|
arena.* = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
const arena_alloc = arena.allocator();
|
||||||
|
|
||||||
return Parser(self, callback){
|
return Parser(self, callback){
|
||||||
.allocator = allocator,
|
.arena = arena,
|
||||||
.subcommands = std.hash_map.StringHashMap(ParserInterface).init(allocator),
|
.allocator = arena_alloc,
|
||||||
.help_builder = help.HelpBuilder(self).init(allocator),
|
.subcommands = std.hash_map.StringHashMap(ParserInterface).init(arena_alloc),
|
||||||
|
.help_builder = help.HelpBuilder(self).init(arena_alloc),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_help_flag(
|
pub fn createInterface(
|
||||||
|
comptime self: @This(),
|
||||||
|
comptime callback: self.CallbackSignature(),
|
||||||
|
allocator: std.mem.Allocator,
|
||||||
|
context: UserContextType,
|
||||||
|
) !ParserInterface {
|
||||||
|
var arena = try allocator.create(std.heap.ArenaAllocator);
|
||||||
|
arena.* = std.heap.ArenaAllocator.init(allocator);
|
||||||
|
const arena_alloc = arena.allocator();
|
||||||
|
|
||||||
|
var this_parser = try arena_alloc.create(Parser(self, callback));
|
||||||
|
this_parser.* = .{
|
||||||
|
.arena = arena,
|
||||||
|
.allocator = arena_alloc,
|
||||||
|
.subcommands = std.hash_map.StringHashMap(ParserInterface).init(arena_alloc),
|
||||||
|
.help_builder = help.HelpBuilder(self).init(arena_alloc),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (UserContextType == void) {
|
||||||
|
return this_parser.interface();
|
||||||
|
} else {
|
||||||
|
return this_parser.interface(context);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setHelpFlag(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime tags: ShortLongPair,
|
comptime tags: ShortLongPair,
|
||||||
) void {
|
) void {
|
||||||
@ -104,49 +135,49 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
|
|
||||||
const string_generics = BuilderGenerics(UserContext){ .OutputType = []const u8 };
|
const string_generics = BuilderGenerics(UserContext){ .OutputType = []const u8 };
|
||||||
|
|
||||||
pub fn string_option(
|
pub fn stringOption(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime cfg: OptionConfig(string_generics.opt_gen()),
|
comptime cfg: OptionConfig(string_generics.optGen()),
|
||||||
) void {
|
) void {
|
||||||
const config = if (cfg.nice_type_name == null)
|
const config = if (cfg.nice_type_name == null)
|
||||||
ncmeta.copy_struct(@TypeOf(cfg), cfg, .{ .nice_type_name = "string" })
|
ncmeta.copyStruct(@TypeOf(cfg), cfg, .{ .nice_type_name = "string" })
|
||||||
else
|
else
|
||||||
cfg;
|
cfg;
|
||||||
|
|
||||||
self.add_option(string_generics, config);
|
self.addOption(string_generics, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn string_argument(
|
pub fn stringArgument(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime cfg: OptionConfig(string_generics.arg_gen()),
|
comptime cfg: OptionConfig(string_generics.argGen()),
|
||||||
) void {
|
) void {
|
||||||
const config = if (cfg.nice_type_name == null)
|
const config = if (cfg.nice_type_name == null)
|
||||||
ncmeta.copy_struct(@TypeOf(cfg), cfg, .{ .nice_type_name = "string" })
|
ncmeta.copyStruct(@TypeOf(cfg), cfg, .{ .nice_type_name = "string" })
|
||||||
else
|
else
|
||||||
cfg;
|
cfg;
|
||||||
|
|
||||||
self.add_argument(string_generics, config);
|
self.addArgument(string_generics, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn simple_flag(
|
pub fn simpleFlag(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime cfg: FlagConfig(string_generics.flag_gen()),
|
comptime cfg: FlagConfig(string_generics.flagGen()),
|
||||||
) void {
|
) void {
|
||||||
self.add_flag(string_generics, cfg);
|
self.addFlag(string_generics, cfg);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_argument(
|
pub fn addArgument(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime bgen: BuilderGenerics(UserContext),
|
comptime bgen: BuilderGenerics(UserContext),
|
||||||
comptime config: OptionConfig(bgen.arg_gen()),
|
comptime config: OptionConfig(bgen.argGen()),
|
||||||
) void {
|
) void {
|
||||||
self.param_spec.add(make_argument(bgen.arg_gen(), config));
|
self.param_spec.add(makeArgument(bgen.argGen(), config));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_option(
|
pub fn addOption(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime bgen: BuilderGenerics(UserContext),
|
comptime bgen: BuilderGenerics(UserContext),
|
||||||
comptime config: OptionConfig(bgen.opt_gen()),
|
comptime config: OptionConfig(bgen.optGen()),
|
||||||
) void {
|
) void {
|
||||||
if (comptime bgen.value_count == .fixed and bgen.value_count.fixed == 0) {
|
if (comptime bgen.value_count == .fixed and bgen.value_count.fixed == 0) {
|
||||||
@compileError(
|
@compileError(
|
||||||
@ -155,13 +186,13 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
self.param_spec.add(make_option(bgen.opt_gen(), config));
|
self.param_spec.add(makeOption(bgen.optGen(), config));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_flag(
|
pub fn addFlag(
|
||||||
comptime self: *@This(),
|
comptime self: *@This(),
|
||||||
comptime bgen: BuilderGenerics(UserContext),
|
comptime bgen: BuilderGenerics(UserContext),
|
||||||
comptime config: FlagConfig(bgen.flag_gen()),
|
comptime config: FlagConfig(bgen.flagGen()),
|
||||||
) void {
|
) void {
|
||||||
comptime {
|
comptime {
|
||||||
if (config.truthy == null and config.falsy == null and config.env_var == null) {
|
if (config.truthy == null and config.falsy == null and config.env_var == null) {
|
||||||
@ -172,7 +203,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const generics = bgen.flag_gen();
|
const generics = bgen.flagGen();
|
||||||
var args = OptionConfig(generics){
|
var args = OptionConfig(generics){
|
||||||
.name = config.name,
|
.name = config.name,
|
||||||
//
|
//
|
||||||
@ -205,7 +236,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
args.long_tag = truthy_pair.long_tag;
|
args.long_tag = truthy_pair.long_tag;
|
||||||
args.flag_bias = .truthy;
|
args.flag_bias = .truthy;
|
||||||
|
|
||||||
self.param_spec.add(make_option(generics, args));
|
self.param_spec.add(makeOption(generics, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.falsy) |falsy_pair| {
|
if (config.falsy) |falsy_pair| {
|
||||||
@ -221,7 +252,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
args.long_tag = falsy_pair.long_tag;
|
args.long_tag = falsy_pair.long_tag;
|
||||||
args.flag_bias = .falsy;
|
args.flag_bias = .falsy;
|
||||||
|
|
||||||
self.param_spec.add(make_option(generics, args));
|
self.param_spec.add(makeOption(generics, args));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (config.env_var) |env_var| {
|
if (config.env_var) |env_var| {
|
||||||
@ -231,7 +262,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
args.env_var = env_var;
|
args.env_var = env_var;
|
||||||
args.flag_bias = .unbiased;
|
args.flag_bias = .unbiased;
|
||||||
|
|
||||||
self.param_spec.add(make_option(generics, args));
|
self.param_spec.add(makeOption(generics, args));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -296,16 +327,16 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
|
|
||||||
if (PType.is_flag) {
|
if (PType.is_flag) {
|
||||||
var peek = idx + 1;
|
var peek = idx + 1;
|
||||||
var bias_seen: [ncmeta.enum_length(FlagBias)]bool = [_]bool{false} ** ncmeta.enum_length(FlagBias);
|
var bias_seen: [ncmeta.enumLength(FlagBias)]bool = [_]bool{false} ** ncmeta.enumLength(FlagBias);
|
||||||
bias_seen[@enumToInt(param.flag_bias)] = true;
|
bias_seen[@intFromEnum(param.flag_bias)] = true;
|
||||||
while (peek < spec.len) : (peek += 1) {
|
while (peek < spec.len) : (peek += 1) {
|
||||||
const peek_param = spec[peek];
|
const peek_param = spec[peek];
|
||||||
|
|
||||||
if (@TypeOf(peek_param).is_flag and std.mem.eql(u8, param.name, peek_param.name)) {
|
if (@TypeOf(peek_param).is_flag and std.mem.eql(u8, param.name, peek_param.name)) {
|
||||||
if (bias_seen[@enumToInt(peek_param.flag_bias)] == true) {
|
if (bias_seen[@intFromEnum(peek_param.flag_bias)] == true) {
|
||||||
@compileError("redundant flag!!!! " ++ param.name);
|
@compileError("redundant flag!!!! " ++ param.name);
|
||||||
} else {
|
} else {
|
||||||
bias_seen[@enumToInt(peek_param.flag_bias)] = true;
|
bias_seen[@intFromEnum(peek_param.flag_bias)] = true;
|
||||||
}
|
}
|
||||||
flag_skip += 1;
|
flag_skip += 1;
|
||||||
} else {
|
} else {
|
||||||
@ -327,7 +358,7 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
fields = fields ++ &[_]StructField{.{
|
fields = fields ++ &[_]StructField{.{
|
||||||
.name = param.name,
|
.name = param.name,
|
||||||
.type = FieldType,
|
.type = FieldType,
|
||||||
.default_value = default,
|
.default_value = @ptrCast(default),
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(FieldType),
|
.alignment = @alignOf(FieldType),
|
||||||
}};
|
}};
|
||||||
@ -371,16 +402,16 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
const PType = @TypeOf(param);
|
const PType = @TypeOf(param);
|
||||||
if (PType.is_flag) {
|
if (PType.is_flag) {
|
||||||
var peek = idx + 1;
|
var peek = idx + 1;
|
||||||
var bias_seen: [ncmeta.enum_length(FlagBias)]bool = [_]bool{false} ** ncmeta.enum_length(FlagBias);
|
var bias_seen: [ncmeta.enumLength(FlagBias)]bool = [_]bool{false} ** ncmeta.enumLength(FlagBias);
|
||||||
bias_seen[@enumToInt(param.flag_bias)] = true;
|
bias_seen[@intFromEnum(param.flag_bias)] = true;
|
||||||
while (peek < spec.len) : (peek += 1) {
|
while (peek < spec.len) : (peek += 1) {
|
||||||
const peek_param = spec[peek];
|
const peek_param = spec[peek];
|
||||||
|
|
||||||
if (@TypeOf(peek_param).is_flag and std.mem.eql(u8, param.name, peek_param.name)) {
|
if (@TypeOf(peek_param).is_flag and std.mem.eql(u8, param.name, peek_param.name)) {
|
||||||
if (bias_seen[@enumToInt(peek_param.flag_bias)] == true) {
|
if (bias_seen[@intFromEnum(peek_param.flag_bias)] == true) {
|
||||||
@compileError("redundant flag!!!! " ++ param.name);
|
@compileError("redundant flag!!!! " ++ param.name);
|
||||||
} else {
|
} else {
|
||||||
bias_seen[@enumToInt(peek_param.flag_bias)] = true;
|
bias_seen[@intFromEnum(peek_param.flag_bias)] = true;
|
||||||
}
|
}
|
||||||
flag_skip += 1;
|
flag_skip += 1;
|
||||||
} else {
|
} else {
|
||||||
@ -397,13 +428,10 @@ pub fn CommandBuilder(comptime UserContext: type) type {
|
|||||||
fields = &(@as([fields.len]StructField, fields[0..fields.len].*) ++ [1]StructField{.{
|
fields = &(@as([fields.len]StructField, fields[0..fields.len].*) ++ [1]StructField{.{
|
||||||
.name = param.name,
|
.name = param.name,
|
||||||
.type = FieldType,
|
.type = FieldType,
|
||||||
.default_value = @ptrCast(
|
.default_value = @ptrCast(&@as(
|
||||||
?*const anyopaque,
|
FieldType,
|
||||||
&@as(
|
if (PType.value_count == .count) 0 else null,
|
||||||
FieldType,
|
)),
|
||||||
if (PType.value_count == .count) 0 else null,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
.is_comptime = false,
|
.is_comptime = false,
|
||||||
.alignment = @alignOf(?[]const u8),
|
.alignment = @alignOf(?[]const u8),
|
||||||
}});
|
}});
|
||||||
|
@ -17,30 +17,30 @@ pub fn ConverterSignature(comptime gen: ParameterGenerics) type {
|
|||||||
) ConversionError!gen.ConvertedType();
|
) ConversionError!gen.ConvertedType();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn default_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
pub fn DefaultConverter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
||||||
return if (comptime gen.multi)
|
return if (comptime gen.multi)
|
||||||
multi_converter(gen)
|
MultiConverter(gen)
|
||||||
else switch (@typeInfo(gen.OutputType)) {
|
else switch (@typeInfo(gen.OutputType)) {
|
||||||
.Bool => flag_converter(gen),
|
.Bool => FlagConverter(gen),
|
||||||
.Int => int_converter(gen),
|
.Int => IntConverter(gen),
|
||||||
.Pointer => |info| if (info.size == .Slice and info.child == u8)
|
.Pointer => |info| if (info.size == .Slice and info.child == u8)
|
||||||
string_converter(gen)
|
StringConverter(gen)
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
.Enum => |info| if (info.is_exhaustive) choice_converter(gen) else null,
|
.Enum => |info| if (info.is_exhaustive) ChoiceConverter(gen) else null,
|
||||||
// TODO: how to handle structs with field defaults? maybe this should only work
|
// TODO: how to handle structs with field defaults? maybe this should only work
|
||||||
// for tuples, which I don't think can have defaults.
|
// for tuples, which I don't think can have defaults.
|
||||||
.Struct => |info| if (gen.value_count == .fixed and gen.value_count.fixed == info.fields.len)
|
.Struct => |info| if (gen.value_count == .fixed and gen.value_count.fixed == info.fields.len)
|
||||||
struct_converter(gen)
|
StructConverter(gen)
|
||||||
else
|
else
|
||||||
null,
|
null,
|
||||||
else => null,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn multi_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
fn MultiConverter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
||||||
const converter = default_converter(
|
const converter = DefaultConverter(
|
||||||
ncmeta.copy_struct(ParameterGenerics, gen, .{ .multi = false }),
|
ncmeta.copyStruct(ParameterGenerics, gen, .{ .multi = false }),
|
||||||
) orelse
|
) orelse
|
||||||
@compileError("no default converter");
|
@compileError("no default converter");
|
||||||
const Intermediate = gen.IntermediateType();
|
const Intermediate = gen.IntermediateType();
|
||||||
@ -59,7 +59,7 @@ fn multi_converter(comptime gen: ParameterGenerics) ?ConverterSignature(gen) {
|
|||||||
}.handler;
|
}.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn flag_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
fn FlagConverter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn handler(_: gen.UserContext, input: []const u8, _: ErrorWriter) ConversionError!bool {
|
pub fn handler(_: gen.UserContext, input: []const u8, _: ErrorWriter) ConversionError!bool {
|
||||||
// treat an empty string as falsy
|
// treat an empty string as falsy
|
||||||
@ -79,7 +79,7 @@ fn flag_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|||||||
}.handler;
|
}.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn string_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
fn StringConverter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
||||||
return struct {
|
return struct {
|
||||||
pub fn handler(_: gen.UserContext, input: []const u8, _: ErrorWriter) ConversionError![]const u8 {
|
pub fn handler(_: gen.UserContext, input: []const u8, _: ErrorWriter) ConversionError![]const u8 {
|
||||||
return input;
|
return input;
|
||||||
@ -87,7 +87,7 @@ fn string_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|||||||
}.handler;
|
}.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn int_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
fn IntConverter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
||||||
const IntType = gen.OutputType;
|
const IntType = gen.OutputType;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
@ -100,7 +100,7 @@ fn int_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|||||||
}.handler;
|
}.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn struct_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
fn StructConverter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
||||||
const StructType = gen.OutputType;
|
const StructType = gen.OutputType;
|
||||||
const type_info = @typeInfo(StructType).Struct;
|
const type_info = @typeInfo(StructType).Struct;
|
||||||
const Intermediate = gen.IntermediateType();
|
const Intermediate = gen.IntermediateType();
|
||||||
@ -117,15 +117,15 @@ fn struct_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|||||||
|
|
||||||
var result: StructType = undefined;
|
var result: StructType = undefined;
|
||||||
inline for (comptime type_info.fields, 0..) |field, idx| {
|
inline for (comptime type_info.fields, 0..) |field, idx| {
|
||||||
const converter = comptime default_converter(
|
const Converter = comptime DefaultConverter(
|
||||||
ncmeta.copy_struct(ParameterGenerics, gen, .{
|
ncmeta.copyStruct(ParameterGenerics, gen, .{
|
||||||
.OutputType = field.type,
|
.OutputType = field.type,
|
||||||
.value_count = .{ .fixed = 1 },
|
.value_count = .{ .fixed = 1 },
|
||||||
}),
|
}),
|
||||||
) orelse
|
) orelse
|
||||||
@compileError("cannot get converter for field" ++ field.name);
|
@compileError("cannot get converter for field" ++ field.name);
|
||||||
|
|
||||||
@field(result, field.name) = try converter(context, input.items[idx], failure);
|
@field(result, field.name) = try Converter(context, input.items[idx], failure);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@ -133,7 +133,7 @@ fn struct_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
|||||||
}.handler;
|
}.handler;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn choice_converter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
fn ChoiceConverter(comptime gen: ParameterGenerics) ConverterSignature(gen) {
|
||||||
const EnumType = gen.OutputType;
|
const EnumType = gen.OutputType;
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
|
@ -20,7 +20,7 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
wrap_width: usize = 100,
|
wrap_width: usize = 100,
|
||||||
writer: Writer,
|
writer: Writer,
|
||||||
|
|
||||||
pub fn print_pair(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
pub fn printPair(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
||||||
try self.writer.writeByteNTimes(' ', leading_indent);
|
try self.writer.writeByteNTimes(' ', leading_indent);
|
||||||
const left = std.mem.trim(u8, pair.left, " \n");
|
const left = std.mem.trim(u8, pair.left, " \n");
|
||||||
try self.writer.writeAll(left);
|
try self.writer.writeAll(left);
|
||||||
@ -30,26 +30,26 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
if (offset > tabstop) return NoclipError.UnexpectedFailure;
|
if (offset > tabstop) return NoclipError.UnexpectedFailure;
|
||||||
|
|
||||||
try self.writer.writeByteNTimes(' ', tabstop - offset);
|
try self.writer.writeByteNTimes(' ', tabstop - offset);
|
||||||
try self.print_rewrap(std.mem.trim(u8, pair.right, " \n"), tabstop);
|
try self.printRewrap(std.mem.trim(u8, pair.right, " \n"), tabstop);
|
||||||
try self.writer.writeByte('\n');
|
try self.writer.writeByte('\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_pair_brief(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
pub fn printPairBrief(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
||||||
const brief = ncmeta.partition(u8, pair.right, &[_][]const u8{"\n\n"})[0];
|
const brief = ncmeta.partition(u8, pair.right, &[_][]const u8{"\n\n"})[0];
|
||||||
const simulacrum: AlignablePair = .{
|
const simulacrum: AlignablePair = .{
|
||||||
.left = pair.left,
|
.left = pair.left,
|
||||||
.right = brief,
|
.right = brief,
|
||||||
};
|
};
|
||||||
|
|
||||||
try self.print_pair(simulacrum, leading_indent, tabstop);
|
try self.printPair(simulacrum, leading_indent, tabstop);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn print_wrapped(self: *@This(), text: []const u8, leading_indent: usize) !void {
|
pub fn printWrapped(self: *@This(), text: []const u8, leading_indent: usize) !void {
|
||||||
try self.writer.writeByteNTimes(' ', leading_indent);
|
try self.writer.writeByteNTimes(' ', leading_indent);
|
||||||
try self.print_rewrap(std.mem.trim(u8, text, "\n"), leading_indent);
|
try self.printRewrap(std.mem.trim(u8, text, "\n"), leading_indent);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_rewrap(self: *@This(), text: []const u8, indent: usize) !void {
|
fn printRewrap(self: *@This(), text: []const u8, indent: usize) !void {
|
||||||
// TODO: lol return a real error
|
// TODO: lol return a real error
|
||||||
if (indent >= self.wrap_width) return NoclipError.UnexpectedFailure;
|
if (indent >= self.wrap_width) return NoclipError.UnexpectedFailure;
|
||||||
|
|
||||||
@ -62,8 +62,8 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
if (line.len == 0) {
|
if (line.len == 0) {
|
||||||
// we have a trailing line that needs to be cleaned up
|
// we have a trailing line that needs to be cleaned up
|
||||||
if (location > indent)
|
if (location > indent)
|
||||||
_ = try self.clear_line(indent);
|
_ = try self.clearLine(indent);
|
||||||
location = try self.clear_line(indent);
|
location = try self.clearLine(indent);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +91,7 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (location != indent)
|
if (location != indent)
|
||||||
location = try self.clear_line(indent);
|
location = try self.clearLine(indent);
|
||||||
|
|
||||||
need_forced_break = true;
|
need_forced_break = true;
|
||||||
continue :choppa;
|
continue :choppa;
|
||||||
@ -100,13 +100,13 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
if (location > indent)
|
if (location > indent)
|
||||||
try self.writer.writeByte(' ');
|
try self.writer.writeByte(' ');
|
||||||
try self.writer.writeAll(choppee[0..split]);
|
try self.writer.writeAll(choppee[0..split]);
|
||||||
location = try self.clear_line(indent);
|
location = try self.clearLine(indent);
|
||||||
choppee = choppee[split + 1 ..];
|
choppee = choppee[split + 1 ..];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clear_line(self: *@This(), indent: usize) !usize {
|
fn clearLine(self: *@This(), indent: usize) !usize {
|
||||||
try self.writer.writeByte('\n');
|
try self.writer.writeByte('\n');
|
||||||
try self.writer.writeByteNTimes(' ', indent);
|
try self.writer.writeByteNTimes(' ', indent);
|
||||||
return indent;
|
return indent;
|
||||||
@ -115,7 +115,7 @@ pub fn StructuredPrinter(comptime Writer: type) type {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn HelpBuilder(comptime command: anytype) type {
|
pub fn HelpBuilder(comptime command: anytype) type {
|
||||||
const help_info = opt_info(command.generate());
|
const help_info = optInfo(command.generate());
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
writebuffer: std.ArrayList(u8),
|
writebuffer: std.ArrayList(u8),
|
||||||
@ -126,7 +126,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_message(
|
pub fn buildMessage(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
subcommands: parser.CommandMap,
|
subcommands: parser.CommandMap,
|
||||||
@ -136,26 +136,26 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
"Usage: {s}{s}{s}{s}\n\n",
|
"Usage: {s}{s}{s}{s}\n\n",
|
||||||
.{
|
.{
|
||||||
name,
|
name,
|
||||||
self.option_brief(),
|
self.optionBrief(),
|
||||||
try self.args_brief(),
|
try self.argsBrief(),
|
||||||
self.subcommands_brief(subcommands),
|
self.subcommandsBrief(subcommands),
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
var printer = StructuredPrinter(@TypeOf(writer)){ .writer = writer };
|
var printer = StructuredPrinter(@TypeOf(writer)){ .writer = writer };
|
||||||
try printer.print_wrapped(command.description, 2);
|
try printer.printWrapped(command.description, 2);
|
||||||
try writer.writeAll("\n\n");
|
try writer.writeAll("\n\n");
|
||||||
|
|
||||||
const arguments = try self.describe_arguments();
|
const arguments = try self.describeArguments();
|
||||||
const options = try self.describe_options();
|
const options = try self.describeOptions();
|
||||||
const env_vars = try self.describe_env();
|
const env_vars = try self.describeEnv();
|
||||||
const subcs = try self.describe_subcommands(subcommands);
|
const subcs = try self.describeSubcommands(subcommands);
|
||||||
const max_just = @max(arguments.just, @max(options.just, @max(env_vars.just, subcs.just)));
|
const max_just = @max(arguments.just, @max(options.just, @max(env_vars.just, subcs.just)));
|
||||||
|
|
||||||
if (arguments.pairs.len > 0) {
|
if (arguments.pairs.len > 0) {
|
||||||
try writer.writeAll("Arguments:\n");
|
try writer.writeAll("Arguments:\n");
|
||||||
for (arguments.pairs) |pair|
|
for (arguments.pairs) |pair|
|
||||||
try printer.print_pair(pair, 2, max_just + 4);
|
try printer.printPair(pair, 2, max_just + 4);
|
||||||
|
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
}
|
}
|
||||||
@ -163,7 +163,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
if (options.pairs.len > 0) {
|
if (options.pairs.len > 0) {
|
||||||
try writer.writeAll("Options:\n");
|
try writer.writeAll("Options:\n");
|
||||||
for (options.pairs) |pair|
|
for (options.pairs) |pair|
|
||||||
try printer.print_pair(pair, 2, max_just + 4);
|
try printer.printPair(pair, 2, max_just + 4);
|
||||||
|
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
}
|
}
|
||||||
@ -171,7 +171,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
if (env_vars.pairs.len > 0) {
|
if (env_vars.pairs.len > 0) {
|
||||||
try writer.writeAll("Environment variables:\n");
|
try writer.writeAll("Environment variables:\n");
|
||||||
for (env_vars.pairs) |pair|
|
for (env_vars.pairs) |pair|
|
||||||
try printer.print_pair(pair, 2, max_just + 4);
|
try printer.printPair(pair, 2, max_just + 4);
|
||||||
|
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
}
|
}
|
||||||
@ -179,7 +179,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
if (subcs.pairs.len > 0) {
|
if (subcs.pairs.len > 0) {
|
||||||
try writer.writeAll("Subcommands:\n");
|
try writer.writeAll("Subcommands:\n");
|
||||||
for (subcs.pairs) |pair|
|
for (subcs.pairs) |pair|
|
||||||
try printer.print_pair_brief(pair, 2, max_just + 4);
|
try printer.printPairBrief(pair, 2, max_just + 4);
|
||||||
|
|
||||||
try writer.writeAll("\n");
|
try writer.writeAll("\n");
|
||||||
}
|
}
|
||||||
@ -187,14 +187,14 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
return self.writebuffer.toOwnedSlice();
|
return self.writebuffer.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn option_brief(_: @This()) []const u8 {
|
fn optionBrief(_: @This()) []const u8 {
|
||||||
return if (comptime help_info.options.len > 0)
|
return if (comptime help_info.options.len > 0)
|
||||||
" [options...]"
|
" [options...]"
|
||||||
else
|
else
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn args_brief(self: @This()) ![]const u8 {
|
fn argsBrief(self: @This()) ![]const u8 {
|
||||||
var buf = std.ArrayList(u8).init(self.writebuffer.allocator);
|
var buf = std.ArrayList(u8).init(self.writebuffer.allocator);
|
||||||
defer buf.deinit();
|
defer buf.deinit();
|
||||||
|
|
||||||
@ -214,19 +214,19 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
return buf.toOwnedSlice();
|
return buf.toOwnedSlice();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subcommands_brief(_: @This(), subcommands: parser.CommandMap) []const u8 {
|
fn subcommandsBrief(_: @This(), subcommands: parser.CommandMap) []const u8 {
|
||||||
return if (subcommands.count() > 0)
|
return if (subcommands.count() > 0)
|
||||||
" <subcommand ...>"
|
" <subcommand ...>"
|
||||||
else
|
else
|
||||||
"";
|
"";
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_arguments(self: @This()) !OptionDescription {
|
fn describeArguments(self: @This()) !OptionDescription {
|
||||||
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
||||||
defer pairs.deinit();
|
defer pairs.deinit();
|
||||||
|
|
||||||
var just: usize = 0;
|
var just: usize = 0;
|
||||||
inline for (comptime help_info.arguments) |arg| {
|
inline for (comptime help_info.arguments) |arg| {
|
||||||
if (comptime arg.description.len == 0) continue;
|
if (comptime arg.description.len == 0) continue;
|
||||||
|
|
||||||
const pair: AlignablePair = .{
|
const pair: AlignablePair = .{
|
||||||
@ -243,13 +243,13 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_options(self: @This()) !OptionDescription {
|
fn describeOptions(self: @This()) !OptionDescription {
|
||||||
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
||||||
defer pairs.deinit();
|
defer pairs.deinit();
|
||||||
|
|
||||||
var just: usize = 0;
|
var just: usize = 0;
|
||||||
inline for (help_info.options) |opt| {
|
inline for (help_info.options) |opt| {
|
||||||
const pair = try self.describe_option(opt);
|
const pair = try self.describeOption(opt);
|
||||||
if (pair.left.len > just) just = pair.left.len;
|
if (pair.left.len > just) just = pair.left.len;
|
||||||
try pairs.append(pair);
|
try pairs.append(pair);
|
||||||
}
|
}
|
||||||
@ -260,7 +260,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_option(self: @This(), comptime opt: OptHelp) !AlignablePair {
|
fn describeOption(self: @This(), comptime opt: OptHelp) !AlignablePair {
|
||||||
var buffer = std.ArrayList(u8).init(self.writebuffer.allocator);
|
var buffer = std.ArrayList(u8).init(self.writebuffer.allocator);
|
||||||
defer buffer.deinit();
|
defer buffer.deinit();
|
||||||
const writer = buffer.writer();
|
const writer = buffer.writer();
|
||||||
@ -321,7 +321,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
return .{ .left = left, .right = right };
|
return .{ .left = left, .right = right };
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_env(self: @This()) !OptionDescription {
|
fn describeEnv(self: @This()) !OptionDescription {
|
||||||
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
||||||
defer pairs.deinit();
|
defer pairs.deinit();
|
||||||
|
|
||||||
@ -343,7 +343,7 @@ pub fn HelpBuilder(comptime command: anytype) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn describe_subcommands(self: @This(), subcommands: parser.CommandMap) !OptionDescription {
|
fn describeSubcommands(self: @This(), subcommands: parser.CommandMap) !OptionDescription {
|
||||||
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
||||||
defer pairs.deinit();
|
defer pairs.deinit();
|
||||||
|
|
||||||
@ -402,7 +402,7 @@ const ArgHelp = struct {
|
|||||||
required: bool = true,
|
required: bool = true,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn opt_info(comptime command: anytype) CommandHelp {
|
pub fn optInfo(comptime command: anytype) CommandHelp {
|
||||||
// TODO: this could be runtime and it would be slightly simpler.
|
// TODO: this could be runtime and it would be slightly simpler.
|
||||||
comptime {
|
comptime {
|
||||||
var options: []const OptHelp = &[_]OptHelp{};
|
var options: []const OptHelp = &[_]OptHelp{};
|
||||||
@ -428,7 +428,7 @@ pub fn opt_info(comptime command: anytype) CommandHelp {
|
|||||||
|
|
||||||
if (!std.mem.eql(u8, last_name, param.name)) {
|
if (!std.mem.eql(u8, last_name, param.name)) {
|
||||||
if (last_name.len > 0) {
|
if (last_name.len > 0) {
|
||||||
if (env_only(last_option)) {
|
if (envOnly(last_option)) {
|
||||||
env_vars = env_vars ++ &[_]EnvHelp{.{
|
env_vars = env_vars ++ &[_]EnvHelp{.{
|
||||||
.env_var = last_option.env_var,
|
.env_var = last_option.env_var,
|
||||||
.description = last_option.description,
|
.description = last_option.description,
|
||||||
@ -471,13 +471,23 @@ pub fn opt_info(comptime command: anytype) CommandHelp {
|
|||||||
// TODO: this is only acceptable for some types. It behaves poorly on
|
// TODO: this is only acceptable for some types. It behaves poorly on
|
||||||
// enum-based choice types because it prints the whole type name rather
|
// enum-based choice types because it prints the whole type name rather
|
||||||
// than just the tag name. Roll our own eventually.
|
// than just the tag name. Roll our own eventually.
|
||||||
writer.print("{any}", .{def}) catch @compileError("whoah");
|
blk: {
|
||||||
|
switch (@typeInfo(@TypeOf(def))) {
|
||||||
|
.Pointer => |info| if (info.size == .Slice and info.child == u8) {
|
||||||
|
writer.print("{s}", .{def}) catch @compileError("no");
|
||||||
|
break :blk;
|
||||||
|
},
|
||||||
|
else => {},
|
||||||
|
}
|
||||||
|
writer.print("{any}", .{def}) catch @compileError("whoah");
|
||||||
|
}
|
||||||
|
|
||||||
last_option.default = buf.buffer;
|
last_option.default = buf.buffer;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (last_name.len > 0) {
|
if (last_name.len > 0) {
|
||||||
if (env_only(last_option)) {
|
if (envOnly(last_option)) {
|
||||||
env_vars = env_vars ++ &[_]EnvHelp{.{
|
env_vars = env_vars ++ &[_]EnvHelp{.{
|
||||||
.env_var = last_option.env_var.?,
|
.env_var = last_option.env_var.?,
|
||||||
.description = last_option.description,
|
.description = last_option.description,
|
||||||
@ -496,7 +506,7 @@ pub fn opt_info(comptime command: anytype) CommandHelp {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn env_only(option: OptHelp) bool {
|
inline fn envOnly(option: OptHelp) bool {
|
||||||
return option.short_truthy == null and
|
return option.short_truthy == null and
|
||||||
option.long_truthy == null and
|
option.long_truthy == null and
|
||||||
option.short_falsy == null and
|
option.short_falsy == null and
|
||||||
|
@ -30,7 +30,7 @@ pub fn UpdateDefaults(comptime input: type, comptime defaults: anytype) type {
|
|||||||
// the appropriate type, which is nice for ergonomics. Not sure
|
// the appropriate type, which is nice for ergonomics. Not sure
|
||||||
// if it introduces weird edge cases. Probably it's fine?
|
// if it introduces weird edge cases. Probably it's fine?
|
||||||
.default_value = if (@hasField(@TypeOf(defaults), field.name))
|
.default_value = if (@hasField(@TypeOf(defaults), field.name))
|
||||||
@ptrCast(?*const anyopaque, &@as(field.field_type, @field(defaults, field.name)))
|
@ptrCast(&@as(field.field_type, @field(defaults, field.name)))
|
||||||
else
|
else
|
||||||
field.default_value,
|
field.default_value,
|
||||||
.is_comptime = field.is_comptime,
|
.is_comptime = field.is_comptime,
|
||||||
@ -48,7 +48,7 @@ pub fn UpdateDefaults(comptime input: type, comptime defaults: anytype) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn enum_length(comptime T: type) comptime_int {
|
pub fn enumLength(comptime T: type) comptime_int {
|
||||||
return @typeInfo(T).Enum.fields.len;
|
return @typeInfo(T).Enum.fields.len;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,7 +174,7 @@ pub fn SliceIterator(comptime T: type) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_struct(comptime T: type, source: T, field_overrides: anytype) T {
|
pub fn copyStruct(comptime T: type, source: T, field_overrides: anytype) T {
|
||||||
var result: T = undefined;
|
var result: T = undefined;
|
||||||
|
|
||||||
comptime inline for (@typeInfo(@TypeOf(field_overrides)).Struct.fields) |field| {
|
comptime inline for (@typeInfo(@TypeOf(field_overrides)).Struct.fields) |field| {
|
||||||
@ -203,7 +203,7 @@ pub const TupleBuilder = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn retrieve(comptime self: @This(), comptime index: comptime_int) self.types[index] {
|
pub fn retrieve(comptime self: @This(), comptime index: comptime_int) self.types[index] {
|
||||||
return @ptrCast(*const self.types[index], @alignCast(@alignOf(*const self.types[index]), self.pointers[index])).*;
|
return @as(*const self.types[index], @ptrCast(@alignCast(self.pointers[index]))).*;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn realTuple(comptime self: @This()) self.TupleType() {
|
pub fn realTuple(comptime self: @This()) self.TupleType() {
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
const command = @import("./command.zig");
|
pub const command = @import("./command.zig");
|
||||||
const converters = @import("./converters.zig");
|
pub const converters = @import("./converters.zig");
|
||||||
const errors = @import("./errors.zig");
|
pub const errors = @import("./errors.zig");
|
||||||
const help = @import("./help.zig");
|
pub const help = @import("./help.zig");
|
||||||
const ncmeta = @import("./meta.zig");
|
pub const ncmeta = @import("./meta.zig");
|
||||||
const parameters = @import("./parameters.zig");
|
pub const parameters = @import("./parameters.zig");
|
||||||
const parser = @import("./parser.zig");
|
pub const parser = @import("./parser.zig");
|
||||||
|
|
||||||
pub const CommandBuilder = command.CommandBuilder;
|
pub const CommandBuilder = command.CommandBuilder;
|
||||||
|
pub const ParserInterface = parser.ParserInterface;
|
||||||
|
@ -34,6 +34,7 @@ pub const ParameterGenerics = struct {
|
|||||||
/// useful for implementing complex conversion that produces output through its
|
/// useful for implementing complex conversion that produces output through its
|
||||||
/// side effects or by modifying the user context.
|
/// side effects or by modifying the user context.
|
||||||
OutputType: type = void,
|
OutputType: type = void,
|
||||||
|
|
||||||
param_type: ParameterType,
|
param_type: ParameterType,
|
||||||
value_count: ValueCount,
|
value_count: ValueCount,
|
||||||
/// allow this named parameter to be passed multiple times.
|
/// allow this named parameter to be passed multiple times.
|
||||||
@ -44,7 +45,7 @@ pub const ParameterGenerics = struct {
|
|||||||
// many-to-many and the many-to-one cases.
|
// many-to-many and the many-to-one cases.
|
||||||
multi: bool,
|
multi: bool,
|
||||||
|
|
||||||
pub fn fixed_value_count(comptime OutputType: type, comptime value_count: ValueCount) ValueCount {
|
pub fn fixedValueCount(comptime OutputType: type, comptime value_count: ValueCount) ValueCount {
|
||||||
return comptime if (value_count == .fixed)
|
return comptime if (value_count == .fixed)
|
||||||
switch (@typeInfo(OutputType)) {
|
switch (@typeInfo(OutputType)) {
|
||||||
.Struct => |info| .{ .fixed = info.fields.len },
|
.Struct => |info| .{ .fixed = info.fields.len },
|
||||||
@ -58,15 +59,15 @@ pub const ParameterGenerics = struct {
|
|||||||
value_count;
|
value_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_context(comptime self: @This()) bool {
|
pub fn hasContext(comptime self: @This()) bool {
|
||||||
return comptime self.UserContext != void;
|
return comptime self.UserContext != void;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn has_output(comptime self: @This()) bool {
|
pub fn hasOutput(comptime self: @This()) bool {
|
||||||
return self.OutputType != void;
|
return self.OutputType != void;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_flag(comptime self: @This()) bool {
|
pub fn isFlag(comptime self: @This()) bool {
|
||||||
return comptime switch (self.value_count) {
|
return comptime switch (self.value_count) {
|
||||||
.flag, .count => true,
|
.flag, .count => true,
|
||||||
.fixed => false,
|
.fixed => false,
|
||||||
@ -185,10 +186,10 @@ fn OptionType(comptime generics: ParameterGenerics) type {
|
|||||||
return struct {
|
return struct {
|
||||||
pub const G: ParameterGenerics = generics;
|
pub const G: ParameterGenerics = generics;
|
||||||
pub const param_type: ParameterType = generics.param_type;
|
pub const param_type: ParameterType = generics.param_type;
|
||||||
pub const is_flag: bool = generics.is_flag();
|
pub const is_flag: bool = generics.isFlag();
|
||||||
pub const value_count: ValueCount = generics.value_count;
|
pub const value_count: ValueCount = generics.value_count;
|
||||||
pub const multi: bool = generics.multi;
|
pub const multi: bool = generics.multi;
|
||||||
pub const has_output: bool = generics.has_output();
|
pub const has_output: bool = generics.hasOutput();
|
||||||
|
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
short_tag: ?[]const u8,
|
short_tag: ?[]const u8,
|
||||||
@ -228,17 +229,17 @@ fn OptionType(comptime generics: ParameterGenerics) type {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_short(comptime short_tag: ?[]const u8) void {
|
fn checkShort(comptime short_tag: ?[]const u8) void {
|
||||||
const short = comptime short_tag orelse return;
|
const short = comptime short_tag orelse return;
|
||||||
if (short.len != 2 or short[0] != '-') @compileError("bad short tag: " ++ short);
|
if (short.len != 2 or short[0] != '-') @compileError("bad short tag: " ++ short);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn check_long(comptime long_tag: ?[]const u8) void {
|
fn checkLong(comptime long_tag: ?[]const u8) void {
|
||||||
const long = comptime long_tag orelse return;
|
const long = comptime long_tag orelse return;
|
||||||
if (long.len < 3 or long[0] != '-' or long[1] != '-') @compileError("bad long tag: " ++ long);
|
if (long.len < 3 or long[0] != '-' or long[1] != '-') @compileError("bad long tag: " ++ long);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_option(comptime generics: ParameterGenerics, comptime opts: OptionConfig(generics)) OptionType(generics) {
|
pub fn makeOption(comptime generics: ParameterGenerics, comptime opts: OptionConfig(generics)) OptionType(generics) {
|
||||||
if (opts.short_tag == null and opts.long_tag == null and opts.env_var == null) {
|
if (opts.short_tag == null and opts.long_tag == null and opts.env_var == null) {
|
||||||
@compileError(
|
@compileError(
|
||||||
"option " ++
|
"option " ++
|
||||||
@ -247,8 +248,8 @@ pub fn make_option(comptime generics: ParameterGenerics, comptime opts: OptionCo
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
check_short(opts.short_tag);
|
checkShort(opts.short_tag);
|
||||||
check_long(opts.long_tag);
|
checkLong(opts.long_tag);
|
||||||
|
|
||||||
// perform the logic to create the default converter here? Could be done
|
// perform the logic to create the default converter here? Could be done
|
||||||
// when creating the OptionConfig instead. Need to do it here because there
|
// when creating the OptionConfig instead. Need to do it here because there
|
||||||
@ -257,7 +258,7 @@ pub fn make_option(comptime generics: ParameterGenerics, comptime opts: OptionCo
|
|||||||
// whereas the OptionType is an instance of an object that has been
|
// whereas the OptionType is an instance of an object that has been
|
||||||
// validated.
|
// validated.
|
||||||
const converter = opts.converter orelse
|
const converter = opts.converter orelse
|
||||||
(converters.default_converter(generics) orelse @compileError(
|
(converters.DefaultConverter(generics) orelse @compileError(
|
||||||
"no converter provided for " ++
|
"no converter provided for " ++
|
||||||
opts.name ++
|
opts.name ++
|
||||||
"and no default exists",
|
"and no default exists",
|
||||||
@ -284,7 +285,7 @@ pub fn make_option(comptime generics: ParameterGenerics, comptime opts: OptionCo
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn make_argument(
|
pub fn makeArgument(
|
||||||
comptime generics: ParameterGenerics,
|
comptime generics: ParameterGenerics,
|
||||||
comptime opts: OptionConfig(generics),
|
comptime opts: OptionConfig(generics),
|
||||||
) OptionType(generics) {
|
) OptionType(generics) {
|
||||||
@ -298,7 +299,7 @@ pub fn make_argument(
|
|||||||
}
|
}
|
||||||
|
|
||||||
const converter = opts.converter orelse
|
const converter = opts.converter orelse
|
||||||
(converters.default_converter(generics) orelse @compileError(
|
(converters.DefaultConverter(generics) orelse @compileError(
|
||||||
"no converter provided for " ++
|
"no converter provided for " ++
|
||||||
opts.name ++
|
opts.name ++
|
||||||
"and no default exists",
|
"and no default exists",
|
||||||
|
@ -12,13 +12,32 @@ pub const ParserInterface = struct {
|
|||||||
execute: *const fn (parser: *anyopaque, context: *anyopaque) anyerror!void,
|
execute: *const fn (parser: *anyopaque, context: *anyopaque) anyerror!void,
|
||||||
parse: *const fn (parser: *anyopaque, context: *anyopaque, name: []const u8, args: [][:0]u8, env: std.process.EnvMap) anyerror!void,
|
parse: *const fn (parser: *anyopaque, context: *anyopaque, name: []const u8, args: [][:0]u8, env: std.process.EnvMap) anyerror!void,
|
||||||
finish: *const fn (parser: *anyopaque, context: *anyopaque) anyerror!void,
|
finish: *const fn (parser: *anyopaque, context: *anyopaque) anyerror!void,
|
||||||
|
getChild: *const fn (parser: *anyopaque, name: []const u8) ?ParserInterface,
|
||||||
describe: *const fn () []const u8,
|
describe: *const fn () []const u8,
|
||||||
|
deinit: *const fn (parser: *anyopaque) void,
|
||||||
|
deinitTree: *const fn (parser: *anyopaque) void,
|
||||||
};
|
};
|
||||||
|
|
||||||
parser: *anyopaque,
|
parser: *anyopaque,
|
||||||
context: *anyopaque,
|
context: *anyopaque,
|
||||||
methods: *const Vtable,
|
methods: *const Vtable,
|
||||||
|
|
||||||
|
fn create(comptime ParserType: type, parser: *anyopaque, context: *anyopaque) @This() {
|
||||||
|
return .{
|
||||||
|
.parser = parser,
|
||||||
|
.context = context,
|
||||||
|
.methods = &.{
|
||||||
|
.execute = ParserType._wrapExecute,
|
||||||
|
.parse = ParserType._wrapParse,
|
||||||
|
.finish = ParserType._wrapFinish,
|
||||||
|
.getChild = ParserType._wrapGetChild,
|
||||||
|
.describe = ParserType._wrapDescribe,
|
||||||
|
.deinit = ParserType._wrapDeinit,
|
||||||
|
.deinitTree = ParserType._wrapDeinitTree,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(self: @This()) anyerror!void {
|
pub fn execute(self: @This()) anyerror!void {
|
||||||
return try self.methods.execute(self.parser, self.context);
|
return try self.methods.execute(self.parser, self.context);
|
||||||
}
|
}
|
||||||
@ -31,64 +50,101 @@ pub const ParserInterface = struct {
|
|||||||
return try self.methods.finish(self.parser, self.context);
|
return try self.methods.finish(self.parser, self.context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn getChild(self: @This(), name: []const u8) ?ParserInterface {
|
||||||
|
return self.methods.getChild(self.parser, name);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn describe(self: @This()) []const u8 {
|
pub fn describe(self: @This()) []const u8 {
|
||||||
return self.methods.describe();
|
return self.methods.describe();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: @This()) void {
|
||||||
|
self.methods.deinit(self.parser);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinitTree(self: @This()) void {
|
||||||
|
self.methods.deinitTree(self.parser);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
fn InterfaceWrappers(comptime ParserType: type) type {
|
||||||
|
return struct {
|
||||||
|
inline fn castInterfaceParser(parser: *anyopaque) *ParserType {
|
||||||
|
return @ptrCast(@alignCast(parser));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapExecute(parser: *anyopaque, ctx: *anyopaque) anyerror!void {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
|
||||||
|
const context = self.castContext(ctx);
|
||||||
|
return try self.execute(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapParse(
|
||||||
|
parser: *anyopaque,
|
||||||
|
ctx: *anyopaque,
|
||||||
|
name: []const u8,
|
||||||
|
args: [][:0]u8,
|
||||||
|
env: std.process.EnvMap,
|
||||||
|
) anyerror!void {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
const context = self.castContext(ctx);
|
||||||
|
return try self.subparse(context, name, args, env);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapFinish(parser: *anyopaque, ctx: *anyopaque) anyerror!void {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
const context = self.castContext(ctx);
|
||||||
|
return try self.finish(context);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapGetChild(parser: *anyopaque, name: []const u8) ?ParserInterface {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
return self.getChild(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapDeinit(parser: *anyopaque) void {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
self.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapDeinitTree(parser: *anyopaque) void {
|
||||||
|
const self = castInterfaceParser(parser);
|
||||||
|
self.deinitTree();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn _wrapDescribe() []const u8 {
|
||||||
|
return ParserType.command_description;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
|
fn InterfaceGen(comptime ParserType: type, comptime UserContext: type) type {
|
||||||
const CtxInfo = @typeInfo(UserContext);
|
const CtxInfo = @typeInfo(UserContext);
|
||||||
|
|
||||||
return if (CtxInfo == .Void) struct {
|
return if (CtxInfo == .Void) struct {
|
||||||
pub fn interface(self: *ParserType) ParserInterface {
|
pub fn interface(self: *ParserType) ParserInterface {
|
||||||
return .{
|
return ParserInterface.create(ParserType, self, @constCast(&void{}));
|
||||||
.parser = self,
|
|
||||||
.context = @constCast(&void{}),
|
|
||||||
.methods = &.{
|
|
||||||
.execute = ParserType.wrap_execute,
|
|
||||||
.parse = ParserType.wrap_parse,
|
|
||||||
.finish = ParserType.wrap_finish,
|
|
||||||
.describe = ParserType.describe,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_context(_: *anyopaque) void {
|
fn castContext(_: ParserType, _: *anyopaque) void {
|
||||||
return void{};
|
return void{};
|
||||||
}
|
}
|
||||||
} else if (CtxInfo == .Pointer and CtxInfo.Pointer.size != .Slice) struct {
|
} else if (CtxInfo == .Pointer and CtxInfo.Pointer.size != .Slice) struct {
|
||||||
pub fn interface(self: *ParserType, context: UserContext) ParserInterface {
|
pub fn interface(self: *ParserType, context: UserContext) ParserInterface {
|
||||||
return .{
|
return ParserInterface.create(ParserType, self, @constCast(context));
|
||||||
.parser = self,
|
|
||||||
.context = @ptrCast(*anyopaque, @constCast(context)),
|
|
||||||
.methods = &.{
|
|
||||||
.execute = ParserType.wrap_execute,
|
|
||||||
.parse = ParserType.wrap_parse,
|
|
||||||
.finish = ParserType.wrap_finish,
|
|
||||||
.describe = ParserType.describe,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_context(ctx: *anyopaque) UserContext {
|
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
||||||
return @ptrCast(UserContext, @alignCast(std.meta.alignment(UserContext), ctx));
|
return @ptrCast(@alignCast(ctx));
|
||||||
}
|
}
|
||||||
} else struct {
|
} else struct {
|
||||||
pub fn interface(self: *ParserType, context: *const UserContext) ParserInterface {
|
pub fn interface(self: *ParserType, context: *const UserContext) ParserInterface {
|
||||||
return .{
|
return ParserInterface.create(ParserType, self, @ptrCast(@constCast(context)));
|
||||||
.parser = self,
|
|
||||||
.context = @ptrCast(*anyopaque, @constCast(context)),
|
|
||||||
.methods = &.{
|
|
||||||
.execute = ParserType.wrap_execute,
|
|
||||||
.parse = ParserType.wrap_parse,
|
|
||||||
.finish = ParserType.wrap_finish,
|
|
||||||
.describe = ParserType.describe,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cast_context(ctx: *anyopaque) UserContext {
|
fn castContext(_: ParserType, ctx: *anyopaque) UserContext {
|
||||||
return @ptrCast(*const UserContext, @alignCast(@alignOf(UserContext), ctx)).*;
|
return @as(*const UserContext, @ptrCast(@alignCast(ctx))).*;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -104,58 +160,32 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
const Output = command.Output();
|
const Output = command.Output();
|
||||||
|
|
||||||
return struct {
|
return struct {
|
||||||
|
const command_description = command.description;
|
||||||
|
|
||||||
intermediate: Intermediate = .{},
|
intermediate: Intermediate = .{},
|
||||||
output: Output = undefined,
|
output: Output = undefined,
|
||||||
consumed_args: u32 = 0,
|
consumed_args: u32 = 0,
|
||||||
progname: ?[]const u8 = null,
|
progname: ?[]const u8 = null,
|
||||||
has_global_tags: bool = false,
|
has_global_tags: bool = false,
|
||||||
|
arena: *std.heap.ArenaAllocator,
|
||||||
allocator: std.mem.Allocator,
|
allocator: std.mem.Allocator,
|
||||||
subcommands: CommandMap,
|
subcommands: CommandMap,
|
||||||
subcommand: ?ParserInterface = null,
|
subcommand: ?ParserInterface = null,
|
||||||
help_builder: help.HelpBuilder(command),
|
help_builder: help.HelpBuilder(command),
|
||||||
|
|
||||||
pub fn add_subcommand(self: *@This(), verb: []const u8, parser: ParserInterface) !void {
|
pub fn addSubcommand(self: *@This(), verb: []const u8, parser: ParserInterface) !void {
|
||||||
try self.subcommands.put(verb, parser);
|
try self.subcommands.put(verb, parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is a slightly annoying hack to work around the fact that there's no way to
|
// This is a slightly annoying hack to work around the fact that there's no way to
|
||||||
// provide a method signature conditionally.
|
// provide a method signature conditionally.
|
||||||
const Interface = InterfaceGen(@This(), UserContext);
|
pub usingnamespace InterfaceGen(@This(), UserContext);
|
||||||
pub usingnamespace Interface;
|
pub usingnamespace InterfaceWrappers(@This());
|
||||||
|
|
||||||
inline fn cast_interface_parser(parser: *anyopaque) *@This() {
|
|
||||||
return @ptrCast(*@This(), @alignCast(@alignOf(@This()), parser));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap_execute(parser: *anyopaque, ctx: *anyopaque) anyerror!void {
|
|
||||||
const self = cast_interface_parser(parser);
|
|
||||||
|
|
||||||
// this is a slightly annoying hack to work around the problem that void has
|
|
||||||
// 0 alignment, which alignCast chokes on.
|
|
||||||
const context = Interface.cast_context(ctx);
|
|
||||||
return try self.execute(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap_parse(parser: *anyopaque, ctx: *anyopaque, name: []const u8, args: [][:0]u8, env: std.process.EnvMap) anyerror!void {
|
|
||||||
const self = cast_interface_parser(parser);
|
|
||||||
const context = Interface.cast_context(ctx);
|
|
||||||
return try self.subparse(context, name, args, env);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn wrap_finish(parser: *anyopaque, ctx: *anyopaque) anyerror!void {
|
|
||||||
const self = cast_interface_parser(parser);
|
|
||||||
const context = Interface.cast_context(ctx);
|
|
||||||
return try self.finish(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn describe() []const u8 {
|
|
||||||
return command.description;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn subparse(self: *@This(), context: UserContext, name: []const u8, args: [][:0]u8, env: std.process.EnvMap) anyerror!void {
|
pub fn subparse(self: *@This(), context: UserContext, name: []const u8, args: [][:0]u8, env: std.process.EnvMap) anyerror!void {
|
||||||
const sliceto = try self.parse(name, args);
|
const sliceto = try self.parse(name, args);
|
||||||
try self.read_environment(env);
|
try self.readEnvironment(env);
|
||||||
try self.convert_eager(context);
|
try self.convertEager(context);
|
||||||
|
|
||||||
if (self.subcommand) |verb| {
|
if (self.subcommand) |verb| {
|
||||||
const verbname = try std.mem.join(
|
const verbname = try std.mem.join(
|
||||||
@ -168,7 +198,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
const stderr = std.io.getStdErr().writer();
|
const stderr = std.io.getStdErr().writer();
|
||||||
try stderr.writeAll("A subcommand is required.\n\n");
|
try stderr.writeAll("A subcommand is required.\n\n");
|
||||||
|
|
||||||
self.print_help(name);
|
self.printHelp(name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,11 +208,26 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
if (self.subcommand) |verb| try verb.finish();
|
if (self.subcommand) |verb| try verb.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn deinit(self: @This()) void {
|
||||||
|
self.arena.deinit();
|
||||||
|
self.arena.child_allocator.destroy(self.arena);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn deinitTree(self: @This()) void {
|
||||||
|
var iterator = self.subcommands.valueIterator();
|
||||||
|
while (iterator.next()) |subcommand| {
|
||||||
|
subcommand.deinitTree();
|
||||||
|
}
|
||||||
|
self.deinit();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn getChild(self: @This(), name: []const u8) ?ParserInterface {
|
||||||
|
return self.subcommands.get(name);
|
||||||
|
}
|
||||||
|
|
||||||
pub fn execute(self: *@This(), context: UserContext) anyerror!void {
|
pub fn execute(self: *@This(), context: UserContext) anyerror!void {
|
||||||
const args = try std.process.argsAlloc(self.allocator);
|
const args = try std.process.argsAlloc(self.allocator);
|
||||||
defer std.process.argsFree(self.allocator, args);
|
|
||||||
var env = try std.process.getEnvMap(self.allocator);
|
var env = try std.process.getEnvMap(self.allocator);
|
||||||
defer env.deinit();
|
|
||||||
|
|
||||||
if (args.len < 1) return ParseError.EmptyArgs;
|
if (args.len < 1) return ParseError.EmptyArgs;
|
||||||
|
|
||||||
@ -192,11 +237,11 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
try self.finish(context);
|
try self.finish(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_value(self: @This(), value: anytype, comptime indent: []const u8) void {
|
fn printValue(self: @This(), value: anytype, comptime indent: []const u8) void {
|
||||||
if (comptime @hasField(@TypeOf(value), "items")) {
|
if (comptime @hasField(@TypeOf(value), "items")) {
|
||||||
std.debug.print("{s}[\n", .{indent});
|
std.debug.print("{s}[\n", .{indent});
|
||||||
for (value.items) |item| {
|
for (value.items) |item| {
|
||||||
self.print_value(item, indent ++ " ");
|
self.printValue(item, indent ++ " ");
|
||||||
}
|
}
|
||||||
std.debug.print("{s}]\n", .{indent});
|
std.debug.print("{s}]\n", .{indent});
|
||||||
} else {
|
} else {
|
||||||
@ -250,11 +295,11 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
|
|
||||||
if (!forced_ordinal and arg.len > 1 and arg[0] == '-') {
|
if (!forced_ordinal and arg.len > 1 and arg[0] == '-') {
|
||||||
if (arg.len > 2 and arg[1] == '-') {
|
if (arg.len > 2 and arg[1] == '-') {
|
||||||
try self.parse_long_tag(name, arg, &argit);
|
try self.parseLongTag(name, arg, &argit);
|
||||||
continue :argloop;
|
continue :argloop;
|
||||||
} else if (arg.len > 1) {
|
} else if (arg.len > 1) {
|
||||||
for (arg[1..], 1..) |short, idx| {
|
for (arg[1..], 1..) |short, idx| {
|
||||||
try self.parse_short_tag(name, short, arg.len - idx - 1, &argit);
|
try self.parseShortTag(name, short, arg.len - idx - 1, &argit);
|
||||||
}
|
}
|
||||||
continue :argloop;
|
continue :argloop;
|
||||||
}
|
}
|
||||||
@ -264,7 +309,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
forced_ordinal = true;
|
forced_ordinal = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (try self.parse_ordinals(arg, &argit)) |verb| {
|
if (try self.parseOrdinals(arg, &argit)) |verb| {
|
||||||
self.subcommand = verb;
|
self.subcommand = verb;
|
||||||
// TODO: return slice of remaining or offset index
|
// TODO: return slice of remaining or offset index
|
||||||
return argit.index;
|
return argit.index;
|
||||||
@ -274,7 +319,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_long_tag(
|
fn parseLongTag(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
arg: []const u8,
|
arg: []const u8,
|
||||||
@ -282,7 +327,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
) ParseError!void {
|
) ParseError!void {
|
||||||
if (comptime command.help_flag.long_tag) |long|
|
if (comptime command.help_flag.long_tag) |long|
|
||||||
if (std.mem.eql(u8, arg, long))
|
if (std.mem.eql(u8, arg, long))
|
||||||
self.print_help(name);
|
self.printHelp(name);
|
||||||
|
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
const PType = @TypeOf(param);
|
const PType = @TypeOf(param);
|
||||||
@ -292,9 +337,9 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
|
|
||||||
if (std.mem.startsWith(u8, arg, tag)) match: {
|
if (std.mem.startsWith(u8, arg, tag)) match: {
|
||||||
if (arg.len == tag.len) {
|
if (arg.len == tag.len) {
|
||||||
try self.apply_param_values(param, argit, false);
|
try self.applyParamValues(param, argit, false);
|
||||||
} else if (arg[tag.len] == '=') {
|
} else if (arg[tag.len] == '=') {
|
||||||
try self.apply_fused_values(param, arg[tag.len + 1 ..]);
|
try self.applyFusedValues(param, arg[tag.len + 1 ..]);
|
||||||
} else break :match;
|
} else break :match;
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@ -304,7 +349,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
return ParseError.UnknownLongTagParameter;
|
return ParseError.UnknownLongTagParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_short_tag(
|
fn parseShortTag(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
name: []const u8,
|
name: []const u8,
|
||||||
arg: u8,
|
arg: u8,
|
||||||
@ -313,7 +358,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
) ParseError!void {
|
) ParseError!void {
|
||||||
if (comptime command.help_flag.short_tag) |short|
|
if (comptime command.help_flag.short_tag) |short|
|
||||||
if (arg == short[1])
|
if (arg == short[1])
|
||||||
self.print_help(name);
|
self.printHelp(name);
|
||||||
|
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
const PType = @TypeOf(param);
|
const PType = @TypeOf(param);
|
||||||
@ -326,7 +371,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
if (remaining > 0)
|
if (remaining > 0)
|
||||||
return ParseError.FusedShortTagValueMissing;
|
return ParseError.FusedShortTagValueMissing;
|
||||||
|
|
||||||
try self.apply_param_values(param, argit, false);
|
try self.applyParamValues(param, argit, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -334,7 +379,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
return ParseError.UnknownShortTagParameter;
|
return ParseError.UnknownShortTagParameter;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_ordinals(
|
fn parseOrdinals(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
arg: []const u8,
|
arg: []const u8,
|
||||||
argit: *ncmeta.SliceIterator([][:0]u8),
|
argit: *ncmeta.SliceIterator([][:0]u8),
|
||||||
@ -346,9 +391,9 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
if (self.consumed_args == arg_index) {
|
if (self.consumed_args == arg_index) {
|
||||||
argit.rewind();
|
argit.rewind();
|
||||||
if (comptime @TypeOf(param).G.multi) {
|
if (comptime @TypeOf(param).G.multi) {
|
||||||
while (argit.peek()) |_| try self.apply_param_values(param, argit, false);
|
while (argit.peek()) |_| try self.applyParamValues(param, argit, false);
|
||||||
} else {
|
} else {
|
||||||
try self.apply_param_values(param, argit, false);
|
try self.applyParamValues(param, argit, false);
|
||||||
}
|
}
|
||||||
self.consumed_args += 1;
|
self.consumed_args += 1;
|
||||||
return null;
|
return null;
|
||||||
@ -360,7 +405,7 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
return self.subcommands.get(arg) orelse ParseError.ExtraValue;
|
return self.subcommands.get(arg) orelse ParseError.ExtraValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn push_intermediate_value(
|
fn pushIntermediateValue(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
comptime param: anytype,
|
comptime param: anytype,
|
||||||
// @TypeOf(param).G.IntermediateValue() should work but appears to trigger a
|
// @TypeOf(param).G.IntermediateValue() should work but appears to trigger a
|
||||||
@ -381,18 +426,18 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_param_values(
|
fn applyParamValues(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
comptime param: anytype,
|
comptime param: anytype,
|
||||||
argit: anytype,
|
argit: anytype,
|
||||||
bounded: bool,
|
bounded: bool,
|
||||||
) ParseError!void {
|
) ParseError!void {
|
||||||
switch (comptime @TypeOf(param).G.value_count) {
|
switch (comptime @TypeOf(param).G.value_count) {
|
||||||
.flag => try self.push_intermediate_value(param, comptime param.flag_bias.string()),
|
.flag => try self.pushIntermediateValue(param, comptime param.flag_bias.string()),
|
||||||
.count => @field(self.intermediate, param.name) += 1,
|
.count => @field(self.intermediate, param.name) += 1,
|
||||||
.fixed => |count| switch (count) {
|
.fixed => |count| switch (count) {
|
||||||
0 => return ParseError.ExtraValue,
|
0 => return ParseError.ExtraValue,
|
||||||
1 => try self.push_intermediate_value(param, argit.next() orelse return ParseError.MissingValue),
|
1 => try self.pushIntermediateValue(param, argit.next() orelse return ParseError.MissingValue),
|
||||||
else => |total| {
|
else => |total| {
|
||||||
var list = std.ArrayList([]const u8).initCapacity(self.allocator, total) catch
|
var list = std.ArrayList([]const u8).initCapacity(self.allocator, total) catch
|
||||||
return ParseError.UnexpectedFailure;
|
return ParseError.UnexpectedFailure;
|
||||||
@ -404,39 +449,39 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
}
|
}
|
||||||
if (bounded and argit.next() != null) return ParseError.ExtraValue;
|
if (bounded and argit.next() != null) return ParseError.ExtraValue;
|
||||||
|
|
||||||
try self.push_intermediate_value(param, list);
|
try self.pushIntermediateValue(param, list);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_fused_values(
|
fn applyFusedValues(
|
||||||
self: *@This(),
|
self: *@This(),
|
||||||
comptime param: anytype,
|
comptime param: anytype,
|
||||||
value: []const u8,
|
value: []const u8,
|
||||||
) ParseError!void {
|
) ParseError!void {
|
||||||
var iter = std.mem.split(u8, value, ",");
|
var iter = std.mem.split(u8, value, ",");
|
||||||
return try self.apply_param_values(param, &iter, true);
|
return try self.applyParamValues(param, &iter, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_environment(self: *@This(), env: std.process.EnvMap) !void {
|
fn readEnvironment(self: *@This(), env: std.process.EnvMap) !void {
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
if (comptime param.env_var) |env_var| blk: {
|
if (comptime param.env_var) |env_var| blk: {
|
||||||
if (@field(self.intermediate, param.name) != null) break :blk;
|
if (@field(self.intermediate, param.name) != null) break :blk;
|
||||||
const val = env.get(env_var) orelse break :blk;
|
const val = env.get(env_var) orelse break :blk;
|
||||||
if (comptime @TypeOf(param).G.value_count == .flag) {
|
if (comptime @TypeOf(param).G.value_count == .flag) {
|
||||||
try self.push_intermediate_value(param, val);
|
try self.pushIntermediateValue(param, val);
|
||||||
} else {
|
} else {
|
||||||
try self.apply_fused_values(param, val);
|
try self.applyFusedValues(param, val);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_eager(self: *@This(), context: UserContext) NoclipError!void {
|
fn convertEager(self: *@This(), context: UserContext) NoclipError!void {
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
if (comptime param.eager) {
|
if (comptime param.eager) {
|
||||||
try self.convert_param(param, context);
|
try self.convertParam(param, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -444,12 +489,12 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
fn convert(self: *@This(), context: UserContext) NoclipError!void {
|
fn convert(self: *@This(), context: UserContext) NoclipError!void {
|
||||||
inline for (comptime parameters) |param| {
|
inline for (comptime parameters) |param| {
|
||||||
if (comptime !param.eager) {
|
if (comptime !param.eager) {
|
||||||
try self.convert_param(param, context);
|
try self.convertParam(param, context);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_param(self: *@This(), comptime param: anytype, context: UserContext) NoclipError!void {
|
fn convertParam(self: *@This(), comptime param: anytype, context: UserContext) NoclipError!void {
|
||||||
if (@field(self.intermediate, param.name)) |intermediate| {
|
if (@field(self.intermediate, param.name)) |intermediate| {
|
||||||
var buffer = std.ArrayList(u8).init(self.allocator);
|
var buffer = std.ArrayList(u8).init(self.allocator);
|
||||||
const writer = buffer.writer();
|
const writer = buffer.writer();
|
||||||
@ -484,11 +529,11 @@ pub fn Parser(comptime command: anytype, comptime callback: anytype) type {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_help(self: *@This(), name: []const u8) noreturn {
|
fn printHelp(self: *@This(), name: []const u8) noreturn {
|
||||||
defer std.process.exit(0);
|
defer std.process.exit(0);
|
||||||
|
|
||||||
const stderr = std.io.getStdErr().writer();
|
const stderr = std.io.getStdErr().writer();
|
||||||
if (self.help_builder.build_message(name, self.subcommands)) |message|
|
if (self.help_builder.buildMessage(name, self.subcommands)) |message|
|
||||||
stderr.writeAll(message) catch return
|
stderr.writeAll(message) catch return
|
||||||
else |_|
|
else |_|
|
||||||
stderr.writeAll("There was a problem generating the help.") catch return;
|
stderr.writeAll("There was a problem generating the help.") catch return;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user