mirror of
https://mzte.de/git/LordMZTE/dotfiles.git
synced 2024-06-18 03:09:37 +02:00
218 lines
6.9 KiB
Zig
218 lines
6.9 KiB
Zig
const std = @import("std");
|
|
|
|
const util = @import("util.zig");
|
|
|
|
/// Commands which are prioritized for correction
|
|
const priority_commands = [_][]const u8{
|
|
"git",
|
|
};
|
|
|
|
/// Commands which wrap another
|
|
const wrapper_commands = std.ComptimeStringMap(void, .{
|
|
.{ "doas", {} },
|
|
.{ "sudo", {} },
|
|
.{ "rbg", {} },
|
|
.{ "rbgd", {} },
|
|
.{ "pkexec", {} },
|
|
});
|
|
|
|
fn populateArgMap(map: *ArgMap) !void {
|
|
try map.put(&.{"git"}, .{ .subcommand = &.{ "push", "pull", "reset", "checkout" } });
|
|
try map.put(&.{ "git", "checkout" }, .file_or_directory);
|
|
}
|
|
|
|
const ArgRequirement = union(enum) {
|
|
subcommand: []const []const u8,
|
|
file,
|
|
directory,
|
|
file_or_directory,
|
|
};
|
|
|
|
const ArgMap = std.HashMap(
|
|
[]const []const u8,
|
|
ArgRequirement,
|
|
struct {
|
|
pub fn hash(self: @This(), v: []const []const u8) u64 {
|
|
_ = self;
|
|
var hasher = std.hash.Wyhash.init(0);
|
|
hasher.update(std.mem.asBytes(&v.len));
|
|
for (v) |s| {
|
|
hasher.update(std.mem.asBytes(&v.len));
|
|
hasher.update(s);
|
|
}
|
|
|
|
return hasher.final();
|
|
}
|
|
|
|
pub fn eql(self: @This(), a: []const []const u8, b: []const []const u8) bool {
|
|
_ = self;
|
|
if (a.len != b.len)
|
|
return false;
|
|
|
|
for (a, b) |va, vb|
|
|
if (!std.mem.eql(u8, va, vb))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
},
|
|
std.hash_map.default_max_load_percentage,
|
|
);
|
|
|
|
pub fn correctCommand(
|
|
arena: *std.heap.ArenaAllocator,
|
|
/// Command to correct in-place
|
|
cmd: [][]const u8,
|
|
/// Set of all valid commands
|
|
commands: *std.StringHashMap(void),
|
|
) !void {
|
|
const alloc = arena.child_allocator;
|
|
|
|
var subslice = cmd;
|
|
|
|
// skip wrapper commands
|
|
while (subslice.len > 0 and wrapper_commands.has(subslice[0]))
|
|
subslice = subslice[1..];
|
|
|
|
// empty command
|
|
if (subslice.len == 0)
|
|
return;
|
|
|
|
if (!commands.contains(subslice[0])) {
|
|
// correct command
|
|
var best: ?struct { []const u8, usize } = null;
|
|
|
|
// do priority commands first and sub 1 from distance
|
|
for (priority_commands) |possible_cmd| {
|
|
const dist = try util.dist(alloc, subslice[0], possible_cmd) -| 1; // prioritize by subtracting 1
|
|
if (best == null or best.?.@"1" > dist)
|
|
best = .{ possible_cmd, dist };
|
|
}
|
|
|
|
if (best != null and best.?.@"1" != 0) {
|
|
var iter = commands.keyIterator();
|
|
while (iter.next()) |possible_cmd| {
|
|
const dist = try util.dist(alloc, subslice[0], possible_cmd.*);
|
|
if (best == null or best.?.@"1" > dist)
|
|
best = .{ possible_cmd.*, dist };
|
|
}
|
|
}
|
|
|
|
if (best) |b| {
|
|
if (!std.mem.eql(u8, subslice[0], b.@"0")) {
|
|
std.log.info("[C] {s} => {s}", .{ subslice[0], b.@"0" });
|
|
subslice[0] = b.@"0";
|
|
}
|
|
}
|
|
}
|
|
|
|
if (subslice.len < 2)
|
|
return;
|
|
|
|
var arg_map = ArgMap.init(alloc);
|
|
defer arg_map.deinit();
|
|
try populateArgMap(&arg_map);
|
|
|
|
// correct args. loop as long as corrections are made
|
|
while (true) {
|
|
var req: ArgRequirement = .file_or_directory;
|
|
|
|
var cmd_slice_end = subslice.len - 1;
|
|
|
|
while (cmd_slice_end >= 1) : (cmd_slice_end -= 1) {
|
|
if (arg_map.get(subslice[0..cmd_slice_end])) |r| {
|
|
req = r;
|
|
break;
|
|
}
|
|
}
|
|
|
|
var new_arg = subslice[cmd_slice_end];
|
|
try correctArgForReq(arena, req, &new_arg);
|
|
if (!std.mem.eql(u8, subslice[cmd_slice_end], new_arg)) {
|
|
std.log.info("[A] {s} => {s}", .{ subslice[cmd_slice_end], new_arg });
|
|
subslice[cmd_slice_end] = new_arg;
|
|
} else break;
|
|
}
|
|
}
|
|
|
|
fn correctArgForReq(arena: *std.heap.ArenaAllocator, req: ArgRequirement, arg: *[]const u8) !void {
|
|
const alloc = arena.child_allocator;
|
|
switch (req) {
|
|
.subcommand => |subcmds| {
|
|
var best: ?struct { []const u8, usize } = null;
|
|
for (subcmds) |possible_cmd| {
|
|
const dist = try util.dist(alloc, arg.*, possible_cmd);
|
|
if (best == null or best.?.@"1" > dist)
|
|
best = .{ possible_cmd, dist };
|
|
}
|
|
|
|
if (best) |b|
|
|
arg.* = b.@"0";
|
|
},
|
|
.file, .directory, .file_or_directory => {
|
|
if (arg.len == 0)
|
|
return;
|
|
|
|
var path_spliter = std.mem.tokenize(u8, arg.*, "/");
|
|
var path_splits = std.ArrayList([]const u8).init(alloc);
|
|
defer path_splits.deinit();
|
|
|
|
// path is absolute
|
|
if (arg.*[0] == '/')
|
|
try path_splits.append("/");
|
|
|
|
while (path_spliter.next()) |split| {
|
|
if (std.mem.eql(u8, split, "~")) {
|
|
try path_splits.append(std.os.getenv("HOME") orelse return error.HomeNotSet);
|
|
} else {
|
|
try path_splits.append(split);
|
|
}
|
|
}
|
|
|
|
for (path_splits.items, 0..) |*split, cur_idx| {
|
|
const dirs = path_splits.items[0..cur_idx];
|
|
const dir_subpath = try std.fs.path.join(arena.allocator(), if (dirs.len == 0) &.{"."} else dirs);
|
|
|
|
var iterable_dir = try std.fs.cwd().openIterableDir(dir_subpath, .{});
|
|
defer iterable_dir.close();
|
|
|
|
// if the given file already exists, there's no point in iterating the dir
|
|
if (iterable_dir.dir.statFile(split.*)) |_| continue else |e| switch (e) {
|
|
error.FileNotFound => {},
|
|
else => return e,
|
|
}
|
|
|
|
var best: ?struct { []const u8, usize } = null;
|
|
|
|
var dir_iter = iterable_dir.iterate();
|
|
|
|
var best_buf: [1024]u8 = undefined;
|
|
|
|
while (try dir_iter.next()) |entry| {
|
|
switch (req) {
|
|
.file => if (entry.kind == .directory) continue,
|
|
.directory => if (entry.kind != .directory and
|
|
entry.kind != .sym_link) continue,
|
|
else => {},
|
|
}
|
|
|
|
const dist = try util.dist(alloc, split.*, entry.name);
|
|
if (best == null or best.?.@"1" > dist) {
|
|
if (entry.name.len > best_buf.len)
|
|
return error.OutOfMemory;
|
|
const buf_slice = best_buf[0..entry.name.len];
|
|
@memcpy(buf_slice, entry.name);
|
|
best = .{ buf_slice, dist };
|
|
}
|
|
}
|
|
|
|
if (best) |b| {
|
|
split.* = try arena.allocator().dupe(u8, b.@"0");
|
|
} else break;
|
|
}
|
|
|
|
arg.* = try std.fs.path.join(arena.allocator(), path_splits.items);
|
|
},
|
|
}
|
|
}
|