cli: suggest the closest enum value on a typo

A misspelt value for an enum-typed flag now names the nearest tag as
did_you_mean. --dump's peeking validator treats a word within two
edits of a format name as that mistake rather than as the url, so
--dump htmx no longer becomes a second url.

tagNames moves from Config into cli so both can use it.
This commit is contained in:
Adrià Arrufat committed 2026-09-08 17:33:48 +02:00
1 parent 88d133a676
commit a32d30fa65
2 files changed
+24 -15

No files matched your search

+17 -8
View File
@@ -190,6 +190,16 @@ const string = @import("string.zig");
/// .help => |tag| printHelp(tag),
/// }
/// ```
pub fn tagNames(comptime E: type) []const []const u8 {
return comptime blk: {
const fields = @typeInfo(E).@"enum".fields;
var names: [fields.len][]const u8 = undefined;
for (fields, &names) |f, *n| n.* = f.name;
const frozen = names;
break :blk &frozen;
};
}
pub fn Builder(comptime commands: anytype) type {
return struct {
const Self = @This();
@@ -211,13 +221,7 @@ pub fn Builder(comptime commands: anytype) type {
break :blk @Enum(Tag, .exhaustive, &names, &std.simd.iota(Tag, len));
};
const command_names: []const []const u8 = blk: {
var names: []const []const u8 = &.{};
for (std.meta.fieldNames(Enum)) |name| {
names = names ++ &[_][]const u8{name};
}
break :blk names;
};
const command_names = tagNames(Enum);
/// Creates an array of `StructField` out of given options.
fn optionsToStructFields(comptime options: anytype) [options.len]std.builtin.Type.StructField {
@@ -701,7 +705,12 @@ pub fn Builder(comptime commands: anytype) type {
const str = args.next() orelse return error.MissingArgument;
const v = std.meta.stringToEnum(E, str) orelse {
log.fatal(.app, "invalid option choice", .{ .arg = kebab_cased, .value = str });
const value = log.red(str);
if (string.closest(str, tagNames(E), 2)) |near| {
log.fatal(.app, "invalid option choice", .{ .arg = kebab_cased, .value = value, .did_you_mean = log.green(near) });
} else {
log.fatal(.app, "invalid option choice", .{ .arg = kebab_cased, .value = value });
}
return error.InvalidArgument;
};