2023-04-02 15:11:50 -07:00
|
|
|
const std = @import("std");
|
2023-03-30 17:00:49 -07:00
|
|
|
|
2023-04-05 01:45:21 -07:00
|
|
|
const NoclipError = @import("./errors.zig").NoclipError;
|
2023-04-02 15:11:50 -07:00
|
|
|
const ncmeta = @import("./meta.zig");
|
|
|
|
const FixedCount = @import("./parameters.zig").FixedCount;
|
2023-04-05 01:45:21 -07:00
|
|
|
const parser = @import("./parser.zig");
|
2023-04-02 15:11:50 -07:00
|
|
|
|
|
|
|
const AlignablePair = struct {
|
|
|
|
left: []const u8,
|
|
|
|
right: []const u8,
|
|
|
|
};
|
|
|
|
|
|
|
|
const OptionDescription = struct {
|
|
|
|
pairs: []AlignablePair,
|
|
|
|
just: usize,
|
|
|
|
};
|
|
|
|
|
2023-04-05 01:45:21 -07:00
|
|
|
pub fn StructuredPrinter(comptime Writer: type) type {
|
|
|
|
return struct {
|
|
|
|
wrap_width: usize = 100,
|
|
|
|
writer: Writer,
|
|
|
|
|
|
|
|
pub fn print_pair(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
|
|
|
try self.writer.writeByteNTimes(' ', leading_indent);
|
|
|
|
const left = std.mem.trim(u8, pair.left, " \n");
|
|
|
|
try self.writer.writeAll(left);
|
|
|
|
|
|
|
|
const offset: usize = leading_indent + left.len;
|
|
|
|
// TODO: lol return a real error
|
|
|
|
if (offset > tabstop) return NoclipError.UnexpectedFailure;
|
|
|
|
|
|
|
|
try self.writer.writeByteNTimes(' ', tabstop - offset);
|
|
|
|
try self.print_rewrap(std.mem.trim(u8, pair.right, " \n"), tabstop);
|
|
|
|
try self.writer.writeByte('\n');
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn print_pair_brief(self: *@This(), pair: AlignablePair, leading_indent: u8, tabstop: usize) !void {
|
|
|
|
const brief = ncmeta.partition(u8, pair.right, &[_][]const u8{"\n\n"})[0];
|
|
|
|
const simulacrum: AlignablePair = .{
|
|
|
|
.left = pair.left,
|
|
|
|
.right = brief,
|
|
|
|
};
|
|
|
|
|
|
|
|
try self.print_pair(simulacrum, leading_indent, tabstop);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn print_wrapped(self: *@This(), text: []const u8, leading_indent: usize) !void {
|
|
|
|
try self.writer.writeByteNTimes(' ', leading_indent);
|
|
|
|
try self.print_rewrap(std.mem.trim(u8, text, "\n"), leading_indent);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn print_rewrap(self: *@This(), text: []const u8, indent: usize) !void {
|
|
|
|
// TODO: lol return a real error
|
|
|
|
if (indent >= self.wrap_width) return NoclipError.UnexpectedFailure;
|
|
|
|
|
|
|
|
// this assumes output stream has already had the first line properly
|
|
|
|
// indented.
|
|
|
|
var splitter = std.mem.split(u8, text, "\n");
|
|
|
|
|
|
|
|
var location: usize = indent;
|
|
|
|
while (splitter.next()) |line| {
|
|
|
|
if (line.len == 0) {
|
|
|
|
// we have a trailing line that needs to be cleaned up
|
|
|
|
if (location > indent)
|
|
|
|
_ = try self.clear_line(indent);
|
|
|
|
location = try self.clear_line(indent);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
var choppee = line;
|
|
|
|
var need_forced_break = false;
|
|
|
|
choppa: while (choppee.len > 0) {
|
|
|
|
const breakoff = self.wrap_width - location;
|
|
|
|
|
|
|
|
if (breakoff >= choppee.len) {
|
|
|
|
if (location > indent)
|
|
|
|
try self.writer.writeByte(' ');
|
|
|
|
|
|
|
|
try self.writer.writeAll(choppee);
|
|
|
|
location += choppee.len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
var split = breakoff;
|
|
|
|
while (choppee[split] != ' ') : (split -= 1) {
|
|
|
|
if (split == 0) {
|
|
|
|
// we have encountered a word that is too long to break,
|
|
|
|
// so force breaking it
|
|
|
|
if (need_forced_break) {
|
|
|
|
split = breakoff;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (location != indent)
|
|
|
|
location = try self.clear_line(indent);
|
|
|
|
|
|
|
|
need_forced_break = true;
|
|
|
|
continue :choppa;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (location > indent)
|
|
|
|
try self.writer.writeByte(' ');
|
|
|
|
try self.writer.writeAll(choppee[0..split]);
|
|
|
|
location = try self.clear_line(indent);
|
|
|
|
choppee = choppee[split + 1 ..];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn clear_line(self: *@This(), indent: usize) !usize {
|
|
|
|
try self.writer.writeByte('\n');
|
|
|
|
try self.writer.writeByteNTimes(' ', indent);
|
|
|
|
return indent;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2023-04-02 15:11:50 -07:00
|
|
|
pub fn HelpBuilder(comptime command: anytype) type {
|
|
|
|
const help_info = opt_info(command.generate());
|
|
|
|
|
|
|
|
return struct {
|
|
|
|
writebuffer: std.ArrayList(u8),
|
|
|
|
|
|
|
|
pub fn init(allocator: std.mem.Allocator) @This() {
|
|
|
|
return @This(){
|
|
|
|
.writebuffer = std.ArrayList(u8).init(allocator),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn build_message(
|
|
|
|
self: *@This(),
|
|
|
|
name: []const u8,
|
2023-04-02 17:15:37 -07:00
|
|
|
subcommands: parser.CommandMap,
|
2023-04-02 15:11:50 -07:00
|
|
|
) ![]const u8 {
|
|
|
|
const writer = self.writebuffer.writer();
|
|
|
|
try writer.print(
|
|
|
|
"Usage: {s}{s}{s}{s}\n\n",
|
|
|
|
.{
|
|
|
|
name,
|
2023-04-02 17:15:37 -07:00
|
|
|
self.option_brief(),
|
|
|
|
try self.args_brief(),
|
|
|
|
self.subcommands_brief(subcommands),
|
2023-04-02 15:11:50 -07:00
|
|
|
},
|
|
|
|
);
|
|
|
|
|
2023-04-05 01:45:21 -07:00
|
|
|
var printer = StructuredPrinter(@TypeOf(writer)){ .writer = writer };
|
|
|
|
try printer.print_wrapped(command.description, 2);
|
2023-04-02 15:11:50 -07:00
|
|
|
try writer.writeAll("\n\n");
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
const arguments = try self.describe_arguments();
|
2023-04-02 15:11:50 -07:00
|
|
|
const options = try self.describe_options();
|
2023-04-02 17:15:37 -07:00
|
|
|
const env_vars = try self.describe_env();
|
|
|
|
const subcs = try self.describe_subcommands(subcommands);
|
|
|
|
const max_just = @max(arguments.just, @max(options.just, @max(env_vars.just, subcs.just)));
|
|
|
|
|
|
|
|
if (arguments.pairs.len > 0) {
|
|
|
|
try writer.writeAll("Arguments:\n");
|
2023-04-05 01:45:21 -07:00
|
|
|
for (arguments.pairs) |pair|
|
|
|
|
try printer.print_pair(pair, 2, max_just + 4);
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
try writer.writeAll("\n");
|
|
|
|
}
|
|
|
|
|
2023-04-02 15:11:50 -07:00
|
|
|
if (options.pairs.len > 0) {
|
|
|
|
try writer.writeAll("Options:\n");
|
2023-04-05 01:45:21 -07:00
|
|
|
for (options.pairs) |pair|
|
|
|
|
try printer.print_pair(pair, 2, max_just + 4);
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
try writer.writeAll("\n");
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
if (env_vars.pairs.len > 0) {
|
|
|
|
try writer.writeAll("Environment variables:\n");
|
2023-04-05 01:45:21 -07:00
|
|
|
for (env_vars.pairs) |pair|
|
|
|
|
try printer.print_pair(pair, 2, max_just + 4);
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
try writer.writeAll("\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
if (subcs.pairs.len > 0) {
|
|
|
|
try writer.writeAll("Subcommands:\n");
|
2023-04-05 01:45:21 -07:00
|
|
|
for (subcs.pairs) |pair|
|
|
|
|
try printer.print_pair_brief(pair, 2, max_just + 4);
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
try writer.writeAll("\n");
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
return self.writebuffer.toOwnedSlice();
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
fn option_brief(_: @This()) []const u8 {
|
|
|
|
return if (comptime help_info.options.len > 0)
|
|
|
|
" [options...]"
|
|
|
|
else
|
|
|
|
"";
|
|
|
|
}
|
2023-04-02 15:11:50 -07:00
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
fn args_brief(self: @This()) ![]const u8 {
|
|
|
|
var buf = std.ArrayList(u8).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer buf.deinit();
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
const writer = buf.writer();
|
2023-04-02 15:11:50 -07:00
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
for (comptime help_info.arguments) |arg| {
|
|
|
|
try writer.writeAll(" ");
|
|
|
|
if (!arg.required) try writer.writeAll("[");
|
2023-04-05 01:47:17 -07:00
|
|
|
try writer.writeByte('<');
|
|
|
|
try writer.writeAll(arg.name);
|
|
|
|
if (arg.multi)
|
|
|
|
try writer.print(" [{s} ...]", .{arg.name});
|
|
|
|
try writer.writeByte('>');
|
2023-04-02 17:15:37 -07:00
|
|
|
if (!arg.required) try writer.writeAll("]");
|
|
|
|
}
|
2023-04-02 15:11:50 -07:00
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
return buf.toOwnedSlice();
|
|
|
|
}
|
|
|
|
|
2023-04-05 01:47:17 -07:00
|
|
|
fn subcommands_brief(_: @This(), subcommands: parser.CommandMap) []const u8 {
|
2023-04-02 17:15:37 -07:00
|
|
|
return if (subcommands.count() > 0)
|
|
|
|
" <subcommand ...>"
|
|
|
|
else
|
|
|
|
"";
|
|
|
|
}
|
|
|
|
|
|
|
|
fn describe_arguments(self: @This()) !OptionDescription {
|
|
|
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer pairs.deinit();
|
2023-04-02 15:11:50 -07:00
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
var just: usize = 0;
|
|
|
|
for (comptime help_info.arguments) |arg| {
|
|
|
|
if (arg.description.len == 0) continue;
|
|
|
|
|
|
|
|
const pair: AlignablePair = .{
|
|
|
|
.left = arg.name,
|
|
|
|
.right = arg.description,
|
|
|
|
};
|
|
|
|
if (pair.left.len > just) just = pair.left.len;
|
|
|
|
try pairs.append(pair);
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
return .{
|
|
|
|
.pairs = try pairs.toOwnedSlice(),
|
|
|
|
.just = just,
|
|
|
|
};
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
fn describe_options(self: @This()) !OptionDescription {
|
|
|
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer pairs.deinit();
|
2023-04-02 15:11:50 -07:00
|
|
|
|
|
|
|
var just: usize = 0;
|
|
|
|
for (comptime help_info.options) |opt| {
|
|
|
|
const pair = try self.describe_option(opt);
|
|
|
|
if (pair.left.len > just) just = pair.left.len;
|
|
|
|
try pairs.append(pair);
|
|
|
|
}
|
|
|
|
|
|
|
|
return .{
|
|
|
|
.pairs = try pairs.toOwnedSlice(),
|
|
|
|
.just = just,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn describe_option(self: @This(), opt: OptHelp) !AlignablePair {
|
|
|
|
var buffer = std.ArrayList(u8).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer buffer.deinit();
|
2023-04-02 15:11:50 -07:00
|
|
|
const writer = buffer.writer();
|
|
|
|
|
|
|
|
if (comptime opt.short_truthy) |tag| {
|
|
|
|
if (buffer.items.len > 0) try writer.writeAll(", ");
|
|
|
|
try writer.writeAll(tag);
|
|
|
|
}
|
|
|
|
if (comptime opt.long_truthy) |tag| {
|
|
|
|
if (buffer.items.len > 0) try writer.writeAll(", ");
|
|
|
|
try writer.writeAll(tag);
|
|
|
|
}
|
|
|
|
|
|
|
|
var falsy_seen = false;
|
|
|
|
if (comptime opt.short_falsy) |tag| {
|
|
|
|
if (buffer.items.len > 0)
|
|
|
|
try writer.writeAll(" / ")
|
|
|
|
else
|
|
|
|
try writer.writeAll("/ ");
|
|
|
|
try writer.writeAll(tag);
|
|
|
|
falsy_seen = true;
|
|
|
|
}
|
|
|
|
if (comptime opt.long_falsy) |tag| {
|
|
|
|
if (falsy_seen)
|
|
|
|
try writer.writeAll(", ")
|
|
|
|
else if (buffer.items.len > 0)
|
|
|
|
try writer.writeAll(" / ");
|
|
|
|
|
|
|
|
try writer.writeAll(tag);
|
|
|
|
}
|
|
|
|
if (opt.value_count > 0) {
|
|
|
|
try writer.print(" <{s}>", .{opt.type_name});
|
|
|
|
}
|
|
|
|
|
|
|
|
const left = try buffer.toOwnedSlice();
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
if (comptime opt.required) {
|
|
|
|
try writer.writeAll("[required]");
|
|
|
|
}
|
|
|
|
|
2023-04-02 15:11:50 -07:00
|
|
|
if (comptime opt.description.len > 0) {
|
2023-04-02 17:15:37 -07:00
|
|
|
if (buffer.items.len > 0) try writer.writeAll(" ");
|
2023-04-02 15:11:50 -07:00
|
|
|
try writer.writeAll(opt.description);
|
|
|
|
}
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
if (comptime opt.env_var) |env| {
|
2023-04-02 15:11:50 -07:00
|
|
|
if (buffer.items.len > 0) try writer.writeAll(" ");
|
2023-04-02 17:15:37 -07:00
|
|
|
try writer.print("(env: {s})", .{env});
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
|
2023-04-02 17:15:37 -07:00
|
|
|
if (comptime opt.default) |def| {
|
2023-04-02 15:11:50 -07:00
|
|
|
if (buffer.items.len > 0) try writer.writeAll(" ");
|
2023-04-02 17:15:37 -07:00
|
|
|
try writer.print("(default: {s})", .{def});
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
2023-04-02 17:15:37 -07:00
|
|
|
|
2023-04-02 15:11:50 -07:00
|
|
|
const right = try buffer.toOwnedSlice();
|
|
|
|
|
|
|
|
return .{ .left = left, .right = right };
|
|
|
|
}
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
fn describe_env(self: @This()) !OptionDescription {
|
|
|
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer pairs.deinit();
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
var just: usize = 0;
|
|
|
|
for (comptime help_info.env_vars) |env| {
|
|
|
|
if (env.description.len == 0) continue;
|
|
|
|
|
|
|
|
const pair: AlignablePair = .{
|
|
|
|
.left = env.env_var,
|
|
|
|
.right = env.description,
|
|
|
|
};
|
|
|
|
if (pair.left.len > just) just = pair.left.len;
|
|
|
|
try pairs.append(pair);
|
|
|
|
}
|
|
|
|
|
|
|
|
return .{
|
|
|
|
.pairs = try pairs.toOwnedSlice(),
|
|
|
|
.just = just,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
fn describe_subcommands(self: @This(), subcommands: parser.CommandMap) !OptionDescription {
|
|
|
|
var pairs = std.ArrayList(AlignablePair).init(self.writebuffer.allocator);
|
2023-04-05 01:47:17 -07:00
|
|
|
defer pairs.deinit();
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
var just: usize = 0;
|
|
|
|
var iter = subcommands.keyIterator();
|
|
|
|
while (iter.next()) |key| {
|
|
|
|
const pair: AlignablePair = .{
|
|
|
|
.left = key.*,
|
2023-04-05 01:45:21 -07:00
|
|
|
.right = subcommands.get(key.*).?.describe(),
|
2023-04-02 17:15:37 -07:00
|
|
|
};
|
|
|
|
if (pair.left.len > just) just = pair.left.len;
|
|
|
|
try pairs.append(pair);
|
|
|
|
}
|
|
|
|
|
|
|
|
return .{
|
|
|
|
.pairs = try pairs.toOwnedSlice(),
|
|
|
|
.just = just,
|
|
|
|
};
|
|
|
|
}
|
2023-04-02 15:11:50 -07:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
const CommandHelp = struct {
|
|
|
|
options: []const OptHelp,
|
|
|
|
arguments: []const ArgHelp,
|
|
|
|
env_vars: []const EnvHelp,
|
|
|
|
};
|
|
|
|
|
|
|
|
const OptHelp = struct {
|
|
|
|
short_truthy: ?[]const u8 = null,
|
|
|
|
long_truthy: ?[]const u8 = null,
|
|
|
|
short_falsy: ?[]const u8 = null,
|
|
|
|
long_falsy: ?[]const u8 = null,
|
|
|
|
env_var: ?[]const u8 = null,
|
|
|
|
description: []const u8 = "",
|
|
|
|
type_name: []const u8 = "",
|
2023-04-02 17:15:37 -07:00
|
|
|
extra: []const u8 = "",
|
2023-04-02 15:11:50 -07:00
|
|
|
default: ?[]const u8 = null,
|
|
|
|
// this is the pivot
|
|
|
|
value_count: FixedCount = 0,
|
|
|
|
required: bool = false,
|
|
|
|
multi: bool = false,
|
|
|
|
};
|
|
|
|
|
|
|
|
const EnvHelp = struct {
|
|
|
|
env_var: []const u8 = "",
|
|
|
|
description: []const u8 = "",
|
|
|
|
default: ?[]const u8 = null,
|
|
|
|
};
|
|
|
|
|
|
|
|
const ArgHelp = struct {
|
|
|
|
name: []const u8 = "",
|
2023-04-02 17:15:37 -07:00
|
|
|
description: []const u8 = "",
|
2023-04-02 15:11:50 -07:00
|
|
|
type_name: []const u8 = "",
|
|
|
|
multi: bool = false,
|
|
|
|
required: bool = true,
|
|
|
|
};
|
|
|
|
|
|
|
|
pub fn opt_info(comptime command: anytype) CommandHelp {
|
2023-04-05 01:47:17 -07:00
|
|
|
// TODO: this could be runtime and it would be slightly simpler.
|
2023-04-02 15:11:50 -07:00
|
|
|
comptime {
|
|
|
|
var options: []const OptHelp = &[_]OptHelp{};
|
|
|
|
var env_vars: []const EnvHelp = &[_]EnvHelp{};
|
|
|
|
var arguments: []const ArgHelp = &[_]ArgHelp{};
|
|
|
|
|
|
|
|
var last_name: []const u8 = "";
|
|
|
|
var last_option: OptHelp = .{};
|
|
|
|
|
|
|
|
paramloop: for (command) |param| {
|
|
|
|
const PType = @TypeOf(param);
|
2023-04-02 17:15:37 -07:00
|
|
|
if (PType.param_type == .Ordinal) {
|
|
|
|
arguments = arguments ++ &[_]ArgHelp{.{
|
|
|
|
.name = param.name,
|
|
|
|
.description = param.description,
|
|
|
|
.type_name = param.nice_type_name,
|
|
|
|
.multi = PType.multi,
|
|
|
|
.required = param.required,
|
|
|
|
}};
|
|
|
|
|
|
|
|
continue :paramloop;
|
|
|
|
}
|
2023-04-02 15:11:50 -07:00
|
|
|
|
|
|
|
if (!std.mem.eql(u8, last_name, param.name)) {
|
|
|
|
if (last_name.len > 0) {
|
2023-04-02 17:15:37 -07:00
|
|
|
if (env_only(last_option)) {
|
2023-04-02 15:11:50 -07:00
|
|
|
env_vars = env_vars ++ &[_]EnvHelp{.{
|
|
|
|
.env_var = last_option.env_var,
|
|
|
|
.description = last_option.description,
|
|
|
|
.default = last_option.default,
|
|
|
|
}};
|
2023-04-02 17:15:37 -07:00
|
|
|
} else {
|
|
|
|
options = options ++ &[_]OptHelp{last_option};
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
last_name = param.name;
|
|
|
|
last_option = .{};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (PType.is_flag) {
|
|
|
|
switch (param.flag_bias) {
|
|
|
|
.truthy => {
|
|
|
|
last_option.short_truthy = param.short_tag;
|
|
|
|
last_option.long_truthy = param.long_tag;
|
|
|
|
},
|
|
|
|
.falsy => {
|
|
|
|
last_option.short_falsy = param.short_tag;
|
|
|
|
last_option.long_falsy = param.long_tag;
|
|
|
|
},
|
|
|
|
.unbiased => last_option.env_var = param.env_var,
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
last_option.short_truthy = param.short_tag;
|
|
|
|
last_option.long_truthy = param.long_tag;
|
|
|
|
last_option.env_var = param.env_var;
|
|
|
|
last_option.value_count = PType.value_count.fixed;
|
|
|
|
}
|
|
|
|
last_option.type_name = param.nice_type_name;
|
|
|
|
last_option.description = param.description;
|
|
|
|
last_option.required = param.required;
|
|
|
|
last_option.multi = PType.multi;
|
2023-04-02 17:15:37 -07:00
|
|
|
|
2023-04-02 15:11:50 -07:00
|
|
|
if (param.default) |def| {
|
|
|
|
var buf = ncmeta.ComptimeSliceBuffer{};
|
|
|
|
const writer = buf.writer();
|
|
|
|
// TODO: this is only acceptable for some types. It behaves poorly on
|
|
|
|
// enum-based choice types because it prints the whole type name rather
|
|
|
|
// than just the tag name. Roll our own eventually.
|
|
|
|
writer.print("{any}", .{def}) catch @compileError("whoah");
|
|
|
|
last_option.default = buf.buffer;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (last_name.len > 0) {
|
2023-04-02 17:15:37 -07:00
|
|
|
if (env_only(last_option)) {
|
2023-04-02 15:11:50 -07:00
|
|
|
env_vars = env_vars ++ &[_]EnvHelp{.{
|
2023-04-02 17:15:37 -07:00
|
|
|
.env_var = last_option.env_var.?,
|
2023-04-02 15:11:50 -07:00
|
|
|
.description = last_option.description,
|
|
|
|
.default = last_option.default,
|
|
|
|
}};
|
2023-04-02 17:15:37 -07:00
|
|
|
} else {
|
|
|
|
options = options ++ &[_]OptHelp{last_option};
|
2023-04-02 15:11:50 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return .{
|
|
|
|
.options = options,
|
|
|
|
.arguments = arguments,
|
|
|
|
.env_vars = env_vars,
|
|
|
|
};
|
|
|
|
}
|
2023-03-30 17:00:49 -07:00
|
|
|
}
|
2023-04-02 17:15:37 -07:00
|
|
|
|
|
|
|
inline fn env_only(option: OptHelp) bool {
|
|
|
|
return option.short_truthy == null and
|
|
|
|
option.long_truthy == null and
|
|
|
|
option.short_falsy == null and
|
|
|
|
option.long_falsy == null;
|
|
|
|
}
|