Compare commits

...

2 Commits

Author SHA1 Message Date
Jarred Sumner
89a6b564d2 Merge branch 'main' into claude/replace-exactsizematcher-with-staticstringmap 2025-09-26 19:01:21 -07:00
Claude Bot
a2b70ffdbe Replace ExactSizeMatcher with bun.ComptimeStringMap
This refactoring removes the custom ExactSizeMatcher utility and replaces all usages with bun.ComptimeStringMap, which provides equivalent functionality with better maintainability.

Changes:
- Replace ExactSizeMatcher with bun.ComptimeStringMap in cli.zig
- Replace ExactSizeMatcher with bun.ComptimeStringMap in Arguments.zig
- Replace ExactSizeMatcher with bun.ComptimeStringMap in bunfig.zig
- Replace ExactSizeMatcher with bun.ComptimeStringMap in integrity.zig
- Remove ExactSizeMatcher export from immutable.zig
- Delete exact_size_matcher.zig file

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-25 23:18:55 +00:00
6 changed files with 84 additions and 168 deletions

View File

@@ -108,17 +108,17 @@ pub const Bunfig = struct {
fn loadLogLevel(this: *Parser, expr: js_ast.Expr) !void {
try this.expectString(expr);
const Matcher = strings.ExactSizeMatcher(8);
const LogLevelMap = bun.ComptimeStringMap(api.MessageLevel, .{
.{ "debug", api.MessageLevel.debug },
.{ "error", api.MessageLevel.err },
.{ "warn", api.MessageLevel.warn },
.{ "info", api.MessageLevel.info },
});
this.bunfig.log_level = switch (Matcher.match(expr.asString(this.allocator).?)) {
Matcher.case("debug") => api.MessageLevel.debug,
Matcher.case("error") => api.MessageLevel.err,
Matcher.case("warn") => api.MessageLevel.warn,
Matcher.case("info") => api.MessageLevel.info,
else => {
try this.addError(expr.loc, "Invalid log level, must be one of debug, error, or warn");
unreachable;
},
const log_str = expr.asString(this.allocator).?;
this.bunfig.log_level = LogLevelMap.get(log_str) orelse {
try this.addError(expr.loc, "Invalid log level, must be one of debug, error, or warn");
unreachable;
};
}

View File

@@ -544,79 +544,67 @@ pub const Command = struct {
}
const first_arg_name = next_arg;
const RootCommandMatcher = strings.ExactSizeMatcher(12);
return switch (RootCommandMatcher.match(first_arg_name)) {
RootCommandMatcher.case("init") => .InitCommand,
RootCommandMatcher.case("build"), RootCommandMatcher.case("bun") => .BuildCommand,
RootCommandMatcher.case("discord") => .DiscordCommand,
RootCommandMatcher.case("upgrade") => .UpgradeCommand,
RootCommandMatcher.case("completions") => .InstallCompletionsCommand,
RootCommandMatcher.case("getcompletes") => .GetCompletionsCommand,
RootCommandMatcher.case("link") => .LinkCommand,
RootCommandMatcher.case("unlink") => .UnlinkCommand,
RootCommandMatcher.case("x") => .BunxCommand,
RootCommandMatcher.case("repl") => .ReplCommand,
RootCommandMatcher.case("i"),
RootCommandMatcher.case("install"),
=> brk: {
for (args_iter.buf) |arg| {
if (arg.len > 0 and (strings.eqlComptime(arg, "-g") or strings.eqlComptime(arg, "--global"))) {
break :brk .AddCommand;
}
}
break :brk .InstallCommand;
},
RootCommandMatcher.case("ci") => .InstallCommand,
RootCommandMatcher.case("c"), RootCommandMatcher.case("create") => .CreateCommand,
RootCommandMatcher.case("test") => .TestCommand,
RootCommandMatcher.case("pm") => .PackageManagerCommand,
RootCommandMatcher.case("add"), RootCommandMatcher.case("a") => .AddCommand,
RootCommandMatcher.case("update") => .UpdateCommand,
RootCommandMatcher.case("patch") => .PatchCommand,
RootCommandMatcher.case("patch-commit") => .PatchCommitCommand,
RootCommandMatcher.case("r"),
RootCommandMatcher.case("remove"),
RootCommandMatcher.case("rm"),
RootCommandMatcher.case("uninstall"),
=> .RemoveCommand,
RootCommandMatcher.case("run") => .RunCommand,
RootCommandMatcher.case("help") => .HelpCommand,
RootCommandMatcher.case("exec") => .ExecCommand,
RootCommandMatcher.case("outdated") => .OutdatedCommand,
RootCommandMatcher.case("publish") => .PublishCommand,
RootCommandMatcher.case("audit") => .AuditCommand,
RootCommandMatcher.case("info") => .InfoCommand,
const RootCommandMap = bun.ComptimeStringMap(Command.Tag, .{
.{ "init", .InitCommand },
.{ "build", .BuildCommand },
.{ "bun", .BuildCommand },
.{ "discord", .DiscordCommand },
.{ "upgrade", .UpgradeCommand },
.{ "completions", .InstallCompletionsCommand },
.{ "getcompletes", .GetCompletionsCommand },
.{ "link", .LinkCommand },
.{ "unlink", .UnlinkCommand },
.{ "x", .BunxCommand },
.{ "repl", .ReplCommand },
.{ "ci", .InstallCommand },
.{ "c", .CreateCommand },
.{ "create", .CreateCommand },
.{ "test", .TestCommand },
.{ "pm", .PackageManagerCommand },
.{ "add", .AddCommand },
.{ "a", .AddCommand },
.{ "update", .UpdateCommand },
.{ "patch", .PatchCommand },
.{ "patch-commit", .PatchCommitCommand },
.{ "r", .RemoveCommand },
.{ "remove", .RemoveCommand },
.{ "rm", .RemoveCommand },
.{ "uninstall", .RemoveCommand },
.{ "run", .RunCommand },
.{ "help", .HelpCommand },
.{ "exec", .ExecCommand },
.{ "outdated", .OutdatedCommand },
.{ "publish", .PublishCommand },
.{ "audit", .AuditCommand },
.{ "info", .InfoCommand },
// These are reserved for future use by Bun, so that someone
// doing `bun deploy` to run a script doesn't accidentally break
// when we add our actual command
RootCommandMatcher.case("deploy") => .ReservedCommand,
RootCommandMatcher.case("cloud") => .ReservedCommand,
RootCommandMatcher.case("config") => .ReservedCommand,
RootCommandMatcher.case("use") => .ReservedCommand,
RootCommandMatcher.case("auth") => .ReservedCommand,
RootCommandMatcher.case("login") => .ReservedCommand,
RootCommandMatcher.case("logout") => .ReservedCommand,
RootCommandMatcher.case("whoami") => .PackageManagerCommand,
RootCommandMatcher.case("prune") => .ReservedCommand,
RootCommandMatcher.case("list") => .ReservedCommand,
RootCommandMatcher.case("why") => .WhyCommand,
.{ "deploy", .ReservedCommand },
.{ "cloud", .ReservedCommand },
.{ "config", .ReservedCommand },
.{ "use", .ReservedCommand },
.{ "auth", .ReservedCommand },
.{ "login", .ReservedCommand },
.{ "logout", .ReservedCommand },
.{ "whoami", .PackageManagerCommand },
.{ "prune", .ReservedCommand },
.{ "list", .ReservedCommand },
.{ "why", .WhyCommand },
.{ "-e", .AutoCommand },
});
RootCommandMatcher.case("-e") => .AutoCommand,
// Special case for install/i commands needing to check for global flag
if (strings.eql(first_arg_name, "install") or strings.eql(first_arg_name, "i")) {
for (args_iter.buf) |arg| {
if (arg.len > 0 and (strings.eqlComptime(arg, "-g") or strings.eqlComptime(arg, "--global"))) {
return .AddCommand;
}
}
return .InstallCommand;
}
else => .AutoCommand,
};
return RootCommandMap.get(first_arg_name) orelse .AutoCommand;
}
const default_completions_list = [_]string{

View File

@@ -897,7 +897,12 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
}
}
const TargetMatcher = strings.ExactSizeMatcher(8);
const TargetMap = bun.ComptimeStringMap(Api.Target, .{
.{ "browser", Api.Target.browser },
.{ "node", Api.Target.node },
.{ "macro", if (cmd == .BuildCommand) Api.Target.bun_macro else Api.Target.bun },
.{ "bun", Api.Target.bun },
});
if (args.option("--target")) |_target| brk: {
if (comptime cmd == .BuildCommand) {
if (args.flag("--compile")) {
@@ -913,13 +918,7 @@ pub fn parse(allocator: std.mem.Allocator, ctx: Command.Context, comptime cmd: C
}
}
opts.target = opts.target orelse switch (TargetMatcher.match(_target)) {
TargetMatcher.case("browser") => Api.Target.browser,
TargetMatcher.case("node") => Api.Target.node,
TargetMatcher.case("macro") => if (cmd == .BuildCommand) Api.Target.bun_macro else Api.Target.bun,
TargetMatcher.case("bun") => Api.Target.bun,
else => CLI.invalidTarget(&diag, _target),
};
opts.target = opts.target orelse TargetMap.get(_target) orelse CLI.invalidTarget(&diag, _target);
if (opts.target.? == .bun) {
ctx.debug.run_in_bun = opts.target.? == .bun;

View File

@@ -125,7 +125,12 @@ pub const Integrity = extern struct {
}
pub fn parse(buf: []const u8) struct { Tag, usize } {
const Matcher = strings.ExactSizeMatcher(8);
const TagMap = bun.ComptimeStringMap(Tag, .{
.{ "sha1", Tag.sha1 },
.{ "sha256", Tag.sha256 },
.{ "sha384", Tag.sha384 },
.{ "sha512", Tag.sha512 },
});
const i = strings.indexOfChar(buf[0..@min(buf.len, 7)], '-') orelse return .{ Tag.unknown, 0 };
@@ -133,13 +138,11 @@ pub const Integrity = extern struct {
return .{ Tag.unknown, 0 };
}
return switch (Matcher.match(buf[0..i])) {
Matcher.case("sha1") => .{ Tag.sha1, i + 1 },
Matcher.case("sha256") => .{ Tag.sha256, i + 1 },
Matcher.case("sha384") => .{ Tag.sha384, i + 1 },
Matcher.case("sha512") => .{ Tag.sha512, i + 1 },
else => .{ Tag.unknown, 0 },
};
const tag = TagMap.get(buf[0..i]) orelse Tag.unknown;
if (tag == Tag.unknown) {
return .{ Tag.unknown, 0 };
}
return .{ tag, i + 1 };
}
pub inline fn digestLen(this: Tag) usize {

View File

@@ -1928,8 +1928,6 @@ pub fn moveSlice(slice: string, from: string, to: string) string {
return result;
}
pub const ExactSizeMatcher = @import("./immutable/exact_size_matcher.zig").ExactSizeMatcher;
pub const unicode_replacement = 0xFFFD;
pub const unicode_replacement_str = brk: {
var out: [std.unicode.utf8CodepointSequenceLength(unicode_replacement) catch unreachable]u8 = undefined;

View File

@@ -1,72 +0,0 @@
pub fn ExactSizeMatcher(comptime max_bytes: usize) type {
switch (max_bytes) {
1, 2, 4, 8, 12, 16 => {},
else => {
@compileError("max_bytes must be 1, 2, 4, 8, 12, or 16.");
},
}
const T = std.meta.Int(
.unsigned,
max_bytes * 8,
);
return struct {
pub fn match(str: anytype) T {
switch (str.len) {
1...max_bytes - 1 => {
var tmp: [max_bytes]u8 = undefined;
@memcpy(tmp[0..str.len], str);
@memset(tmp[str.len..], 0);
return std.mem.readInt(T, &tmp, .little);
},
max_bytes => {
return std.mem.readInt(T, str[0..max_bytes], .little);
},
0 => {
return 0;
},
else => {
return std.math.maxInt(T);
},
}
}
pub fn matchLower(str: anytype) T {
switch (str.len) {
1...max_bytes - 1 => {
var tmp: [max_bytes]u8 = undefined;
for (str, 0..) |char, i| {
tmp[i] = std.ascii.toLower(char);
}
@memset(tmp[str.len..], 0);
return std.mem.readInt(T, &tmp, .little);
},
max_bytes => {
return std.mem.readInt(T, str[0..max_bytes], .little);
},
0 => {
return 0;
},
else => {
return std.math.maxInt(T);
},
}
}
pub fn case(comptime str: []const u8) T {
if (str.len < max_bytes) {
var bytes = std.mem.zeroes([max_bytes]u8);
bytes[0..str.len].* = str[0..str.len].*;
return std.mem.readInt(T, &bytes, .little);
} else if (str.len == max_bytes) {
return std.mem.readInt(T, str[0..str.len], .little);
} else {
@compileError("str: \"" ++ str ++ "\" too long");
}
}
};
}
const std = @import("std");