Wrapping std.Io?

test "file handle leak check, fail expected" {
    const tracker: *FileHandleTracker = try .create(std.testing.io, std.testing.allocator);
    defer tracker.destroy();

    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    _ = try tmp.dir.createFile(tracker.io, "temp", .{});

    try std.testing.expectError(error.FileLeaked, tracker.check());
}

test "file handle leak check, success expected" {
    const tracker: *FileHandleTracker = try .create(std.testing.io, std.testing.allocator);
    defer tracker.destroy();

    var tmp = std.testing.tmpDir(.{});
    defer tmp.cleanup();

    {
        const file = try tmp.dir.createFile(tracker.io, "temp", .{});
        defer file.close(tracker.io);
    }

    try tracker.check();
}

pub const FileHandleTracker = struct {
    const Handles = std.AutoArrayHashMapUnmanaged(File.Handle, void);
    orig: std.Io,
    io: std.Io,
    handles: Handles,
    allocator: std.mem.Allocator,

    pub fn create(orig: std.Io, allocator: std.mem.Allocator) !*FileHandleTracker {
        const tracker = try allocator.create(FileHandleTracker);
        tracker.* = .{
            .orig = orig,
            .io = .{
                .userdata = tracker,
                .vtable = &VTable,
            },
            .handles = .empty,
            .allocator = allocator,
        };
        return tracker;
    }
    pub fn destroy(self: *FileHandleTracker) void {
        self.handles.deinit(self.allocator);
        self.allocator.destroy(self);
    }
    pub fn check(self: *FileHandleTracker) error{FileLeaked}!void {
        if (self.handles.count() > 0) return error.FileLeaked;
    }

    const VTable: std.Io.VTable = blk: {
        var res: std.Io.VTable = undefined;
        const Passthrough = struct {
            fn Apply(comptime func: []const u8) type {
                return @typeInfo(@typeInfo(@FieldType(std.Io.VTable, func)).pointer.child).@"fn".return_type.?;
            }
            inline fn apply(comptime func: []const u8, userdata: ?*anyopaque, args: anytype) Apply(func) {
                const self: *FileHandleTracker = @ptrCast(@alignCast(userdata));
                return @call(.auto, @field(self.orig.vtable, func), .{self.orig.userdata} ++ args);
            }

            pub fn crashHandler(userdata: ?*anyopaque) void {
                return apply("crashHandler", userdata, .{});
            }
            pub fn async(userdata: ?*anyopaque, result: []u8, result_alignment: std.mem.Alignment, context: []const u8, context_alignment: std.mem.Alignment, start: *const fn (context: *const anyopaque, result: *anyopaque) void) ?*AnyFuture {
                return apply("async", userdata, .{ result, result_alignment, context, context_alignment, start });
            }
            pub fn concurrent(userdata: ?*anyopaque, result_len: usize, result_alignment: std.mem.Alignment, context: []const u8, context_alignment: std.mem.Alignment, start: *const fn (context: *const anyopaque, result: *anyopaque) void) ConcurrentError!*AnyFuture {
                return apply("concurrent", userdata, .{ result_len, result_alignment, context, context_alignment, start });
            }
            pub fn await(userdata: ?*anyopaque, any_future: *AnyFuture, result: []u8, result_alignment: std.mem.Alignment) void {
                return apply("await", userdata, .{ any_future, result, result_alignment });
            }
            pub fn cancel(userdata: ?*anyopaque, any_future: *AnyFuture, result: []u8, result_alignment: std.mem.Alignment) void {
                return apply("cancel", userdata, .{ any_future, result, result_alignment });
            }
            pub fn groupAsync(userdata: ?*anyopaque, group: *Group, context: []const u8, context_alignment: std.mem.Alignment, start: *const fn (context: *const anyopaque) void) void {
                return apply("groupAsync", userdata, .{ group, context, context_alignment, start });
            }
            pub fn groupConcurrent(userdata: ?*anyopaque, group: *Group, context: []const u8, context_alignment: std.mem.Alignment, start: *const fn (context: *const anyopaque) void) ConcurrentError!void {
                return apply("groupConcurrent", userdata, .{ group, context, context_alignment, start });
            }
            pub fn groupAwait(userdata: ?*anyopaque, group: *Group, token: *anyopaque) Cancelable!void {
                return apply("groupAwait", userdata, .{ group, token });
            }
            pub fn groupCancel(userdata: ?*anyopaque, group: *Group, token: *anyopaque) void {
                return apply("groupCancel", userdata, .{ group, token });
            }
            pub fn recancel(userdata: ?*anyopaque) void {
                return apply("recancel", userdata, .{});
            }
            pub fn swapCancelProtection(userdata: ?*anyopaque, new: std.Io.CancelProtection) CancelProtection {
                return apply("swapCancelProtection", userdata, .{new});
            }
            pub fn checkCancel(userdata: ?*anyopaque) std.Io.Cancelable!void {
                return apply("checkCancel", userdata, .{});
            }
            pub fn futexWait(userdata: ?*anyopaque, ptr: *const u32, expected: u32, timeout: std.Io.Timeout) Cancelable!void {
                return apply("futexWait", userdata, .{ ptr, expected, timeout });
            }
            pub fn futexWaitUncancelable(userdata: ?*anyopaque, ptr: *const u32, expected: u32) void {
                return apply("futexWaitUncancelable", userdata, .{ ptr, expected });
            }
            pub fn futexWake(userdata: ?*anyopaque, ptr: *const u32, max_waiters: u32) void {
                return apply("futexWake", userdata, .{ ptr, max_waiters });
            }
            pub fn operate(userdata: ?*anyopaque, operation: std.Io.Operation) Cancelable!Operation.Result {
                return apply("operate", userdata, .{operation});
            }
            pub fn batchAwaitAsync(userdata: ?*anyopaque, batch: *Batch) Cancelable!void {
                return apply("batchAwaitAsync", userdata, .{batch});
            }
            pub fn batchAwaitConcurrent(userdata: ?*anyopaque, batch: *Batch, timeout: Timeout) Batch.AwaitConcurrentError!void {
                return apply("batchAwaitConcurrent", userdata, .{ batch, timeout });
            }
            pub fn batchCancel(userdata: ?*anyopaque, batch: *Batch) void {
                return apply("batchCancel", userdata, .{batch});
            }
            pub fn dirCreateDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirError!void {
                return apply("dirCreateDir", userdata, .{ dir, sub_path, permissions });
            }
            pub fn dirCreateDirPath(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions) Dir.CreateDirPathError!Dir.CreatePathStatus {
                return apply("dirCreateDirPath", userdata, .{ dir, sub_path, permissions });
            }
            pub fn dirCreateDirPathOpen(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: Dir.Permissions, options: Dir.OpenOptions) Dir.CreateDirPathOpenError!Dir {
                return apply("dirCreateDirPathOpen", userdata, .{ dir, sub_path, permissions, options });
            }
            pub fn dirOpenDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenOptions) Dir.OpenError!Dir {
                return apply("dirOpenDir", userdata, .{ dir, sub_path, options });
            }
            pub fn dirStat(userdata: ?*anyopaque, dir: Dir) Dir.StatError!Dir.Stat {
                return apply("dirStat", userdata, .{dir});
            }
            pub fn dirStatFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.StatFileOptions) Dir.StatFileError!File.Stat {
                return apply("dirStatFile", userdata, .{ dir, sub_path, options });
            }
            pub fn dirAccess(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.AccessOptions) Dir.AccessError!void {
                return apply("dirAccess", userdata, .{ dir, sub_path, options });
            }
            pub fn dirCreateFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.CreateFileOptions) File.OpenError!File {
                return apply("dirCreateFile", userdata, .{ dir, sub_path, options });
            }
            pub fn dirCreateFileAtomic(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.CreateFileAtomicOptions) Dir.CreateFileAtomicError!File.Atomic {
                return apply("dirCreateFileAtomic", userdata, .{ dir, sub_path, options });
            }
            pub fn dirOpenFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenFileOptions) File.OpenError!File {
                return apply("dirOpenFile", userdata, .{ dir, sub_path, options });
            }
            pub fn dirClose(userdata: ?*anyopaque, sub_path: []const Dir) void {
                return apply("dirClose", userdata, .{sub_path});
            }
            pub fn dirRead(userdata: ?*anyopaque, reader: *Dir.Reader, entry: []Dir.Entry) Dir.Reader.Error!usize {
                return apply("dirRead", userdata, .{ reader, entry });
            }
            pub fn dirRealPath(userdata: ?*anyopaque, dir: Dir, out_buffer: []u8) Dir.RealPathError!usize {
                return apply("dirRealPath", userdata, .{ dir, out_buffer });
            }
            pub fn dirRealPathFile(userdata: ?*anyopaque, dir: Dir, path_name: []const u8, out_buffer: []u8) Dir.RealPathFileError!usize {
                return apply("dirRealPathFile", userdata, .{ dir, path_name, out_buffer });
            }
            pub fn dirDeleteFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteFileError!void {
                return apply("dirDeleteFile", userdata, .{ dir, sub_path });
            }
            pub fn dirDeleteDir(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8) Dir.DeleteDirError!void {
                return apply("dirDeleteDir", userdata, .{ dir, sub_path });
            }
            pub fn dirRename(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenameError!void {
                return apply("dirRename", userdata, .{ old_dir, old_sub_path, new_dir, new_sub_path });
            }
            pub fn dirRenamePreserve(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8) Dir.RenamePreserveError!void {
                return apply("dirRenamePreserve", userdata, .{ old_dir, old_sub_path, new_dir, new_sub_path });
            }
            pub fn dirSymLink(userdata: ?*anyopaque, dir: Dir, target_path: []const u8, sym_link_path: []const u8, flags: Dir.SymLinkFlags) Dir.SymLinkError!void {
                return apply("dirSymLink", userdata, .{ dir, target_path, sym_link_path, flags });
            }
            pub fn dirReadLink(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, buffer: []u8) Dir.ReadLinkError!usize {
                return apply("dirReadLink", userdata, .{ dir, sub_path, buffer });
            }
            pub fn dirSetOwner(userdata: ?*anyopaque, dir: Dir, uid: ?File.Uid, gid: ?File.Gid) Dir.SetOwnerError!void {
                return apply("dirSetOwner", userdata, .{ dir, uid, gid });
            }
            pub fn dirSetFileOwner(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, uid: ?File.Uid, gid: ?File.Gid, options: Dir.SetFileOwnerOptions) Dir.SetFileOwnerError!void {
                return apply("dirSetFileOwner", userdata, .{ dir, sub_path, uid, gid, options });
            }
            pub fn dirSetPermissions(userdata: ?*anyopaque, dir: Dir, permissions: Dir.Permissions) Dir.SetPermissionsError!void {
                return apply("dirSetPermissions", userdata, .{ dir, permissions });
            }
            pub fn dirSetFilePermissions(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, permissions: File.Permissions, options: Dir.SetFilePermissionsOptions) Dir.SetFilePermissionsError!void {
                return apply("dirSetFilePermissions", userdata, .{ dir, sub_path, permissions, options });
            }
            pub fn dirSetTimestamps(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.SetTimestampsOptions) Dir.SetTimestampsError!void {
                return apply("dirSetTimestamps", userdata, .{ dir, sub_path, options });
            }
            pub fn dirHardLink(userdata: ?*anyopaque, old_dir: Dir, old_sub_path: []const u8, new_dir: Dir, new_sub_path: []const u8, options: Dir.HardLinkOptions) Dir.HardLinkError!void {
                return apply("dirHardLink", userdata, .{ old_dir, old_sub_path, new_dir, new_sub_path, options });
            }
            pub fn fileStat(userdata: ?*anyopaque, file: File) File.StatError!File.Stat {
                return apply("fileStat", userdata, .{file});
            }
            pub fn fileLength(userdata: ?*anyopaque, file: File) File.LengthError!u64 {
                return apply("fileLength", userdata, .{file});
            }
            pub fn fileClose(userdata: ?*anyopaque, file: []const File) void {
                return apply("fileClose", userdata, .{file});
            }
            pub fn fileWritePositional(userdata: ?*anyopaque, file: File, header: []const u8, data: []const []const u8, splat: usize, offset: u64) File.WritePositionalError!usize {
                return apply("fileWritePositional", userdata, .{ file, header, data, splat, offset });
            }
            pub fn fileWriteFileStreaming(userdata: ?*anyopaque, file: File, header: []const u8, reader: *Io.File.Reader, limit: Io.Limit) File.Writer.WriteFileError!usize {
                return apply("fileWriteFileStreaming", userdata, .{ file, header, reader, limit });
            }
            pub fn fileWriteFilePositional(userdata: ?*anyopaque, file: File, header: []const u8, reader: *Io.File.Reader, limit: Io.Limit, offset: u64) File.WriteFilePositionalError!usize {
                return apply("fileWriteFilePositional", userdata, .{ file, header, reader, limit, offset });
            }
            pub fn fileReadPositional(userdata: ?*anyopaque, file: File, data: []const []u8, offset: u64) File.ReadPositionalError!usize {
                return apply("fileReadPositional", userdata, .{ file, data, offset });
            }
            pub fn fileSeekBy(userdata: ?*anyopaque, file: File, relative_offset: i64) File.SeekError!void {
                return apply("fileSeekBy", userdata, .{ file, relative_offset });
            }
            pub fn fileSeekTo(userdata: ?*anyopaque, file: File, absolute_offset: u64) File.SeekError!void {
                return apply("fileSeekTo", userdata, .{ file, absolute_offset });
            }
            pub fn fileSync(userdata: ?*anyopaque, file: File) File.SyncError!void {
                return apply("fileSync", userdata, .{file});
            }
            pub fn fileIsTty(userdata: ?*anyopaque, file: File) Cancelable!bool {
                return apply("fileIsTty", userdata, .{file});
            }
            pub fn fileEnableAnsiEscapeCodes(userdata: ?*anyopaque, file: File) File.EnableAnsiEscapeCodesError!void {
                return apply("fileEnableAnsiEscapeCodes", userdata, .{file});
            }
            pub fn fileSupportsAnsiEscapeCodes(userdata: ?*anyopaque, file: File) Cancelable!bool {
                return apply("fileSupportsAnsiEscapeCodes", userdata, .{file});
            }
            pub fn fileSetLength(userdata: ?*anyopaque, file: File, len: u64) File.SetLengthError!void {
                return apply("fileSetLength", userdata, .{ file, len });
            }
            pub fn fileSetOwner(userdata: ?*anyopaque, file: File, uid: ?File.Uid, gid: ?File.Gid) File.SetOwnerError!void {
                return apply("fileSetOwner", userdata, .{ file, uid, gid });
            }
            pub fn fileSetPermissions(userdata: ?*anyopaque, file: File, permissions: File.Permissions) File.SetPermissionsError!void {
                return apply("fileSetPermissions", userdata, .{ file, permissions });
            }
            pub fn fileSetTimestamps(userdata: ?*anyopaque, file: File, options: File.SetTimestampsOptions) File.SetTimestampsError!void {
                return apply("fileSetTimestamps", userdata, .{ file, options });
            }
            pub fn fileLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!void {
                return apply("fileLock", userdata, .{ file, lock });
            }
            pub fn fileTryLock(userdata: ?*anyopaque, file: File, lock: File.Lock) File.LockError!bool {
                return apply("fileTryLock", userdata, .{ file, lock });
            }
            pub fn fileUnlock(userdata: ?*anyopaque, file: File) void {
                return apply("fileUnlock", userdata, .{file});
            }
            pub fn fileDowngradeLock(userdata: ?*anyopaque, file: File) File.DowngradeLockError!void {
                return apply("fileDowngradeLock", userdata, .{file});
            }
            pub fn fileRealPath(userdata: ?*anyopaque, file: File, out_buffer: []u8) File.RealPathError!usize {
                return apply("fileRealPath", userdata, .{ file, out_buffer });
            }
            pub fn fileHardLink(userdata: ?*anyopaque, file: File, dir: Dir, sub_path: []const u8, options: File.HardLinkOptions) File.HardLinkError!void {
                return apply("fileHardLink", userdata, .{ file, dir, sub_path, options });
            }
            pub fn fileMemoryMapCreate(userdata: ?*anyopaque, file: File, options: File.MemoryMap.CreateOptions) File.MemoryMap.CreateError!File.MemoryMap {
                return apply("fileMemoryMapCreate", userdata, .{ file, options });
            }
            pub fn fileMemoryMapDestroy(userdata: ?*anyopaque, map: *File.MemoryMap) void {
                return apply("fileMemoryMapDestroy", userdata, .{map});
            }
            pub fn fileMemoryMapSetLength(userdata: ?*anyopaque, map: *File.MemoryMap, len: usize) File.MemoryMap.SetLengthError!void {
                return apply("fileMemoryMapSetLength", userdata, .{ map, len });
            }
            pub fn fileMemoryMapRead(userdata: ?*anyopaque, map: *File.MemoryMap) File.ReadPositionalError!void {
                return apply("fileMemoryMapRead", userdata, .{map});
            }
            pub fn fileMemoryMapWrite(userdata: ?*anyopaque, map: *File.MemoryMap) File.WritePositionalError!void {
                return apply("fileMemoryMapWrite", userdata, .{map});
            }
            pub fn processExecutableOpen(userdata: ?*anyopaque, options: Dir.OpenFileOptions) std.process.OpenExecutableError!File {
                return apply("processExecutableOpen", userdata, .{options});
            }
            pub fn processExecutablePath(userdata: ?*anyopaque, buffer: []u8) std.process.ExecutablePathError!usize {
                return apply("processExecutablePath", userdata, .{buffer});
            }
            pub fn lockStderr(userdata: ?*anyopaque, mode: ?Terminal.Mode) Cancelable!LockedStderr {
                return apply("lockStderr", userdata, .{mode});
            }
            pub fn tryLockStderr(userdata: ?*anyopaque, mode: ?Terminal.Mode) Cancelable!?LockedStderr {
                return apply("tryLockStderr", userdata, .{mode});
            }
            pub fn unlockStderr(userdata: ?*anyopaque) void {
                return apply("unlockStderr", userdata, .{});
            }
            pub fn processCurrentPath(userdata: ?*anyopaque, buffer: []u8) std.process.CurrentPathError!usize {
                return apply("processCurrentPath", userdata, .{buffer});
            }
            pub fn processSetCurrentDir(userdata: ?*anyopaque, dir: Dir) std.process.SetCurrentDirError!void {
                return apply("processSetCurrentDir", userdata, .{dir});
            }
            pub fn processSetCurrentPath(userdata: ?*anyopaque, sub_path: []const u8) std.process.SetCurrentPathError!void {
                return apply("processSetCurrentPath", userdata, .{sub_path});
            }
            pub fn processReplace(userdata: ?*anyopaque, options: std.process.ReplaceOptions) std.process.ReplaceError {
                return apply("processReplace", userdata, .{options});
            }
            pub fn processReplacePath(userdata: ?*anyopaque, dir: Dir, options: std.process.ReplaceOptions) std.process.ReplaceError {
                return apply("processReplacePath", userdata, .{ dir, options });
            }
            pub fn processSpawn(userdata: ?*anyopaque, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child {
                return apply("processSpawn", userdata, .{options});
            }
            pub fn processSpawnPath(userdata: ?*anyopaque, dir: Dir, options: std.process.SpawnOptions) std.process.SpawnError!std.process.Child {
                return apply("processSpawnPath", userdata, .{ dir, options });
            }
            pub fn childWait(userdata: ?*anyopaque, process: *std.process.Child) std.process.Child.WaitError!std.process.Child.Term {
                return apply("childWait", userdata, .{process});
            }
            pub fn childKill(userdata: ?*anyopaque, child: *std.process.Child) void {
                return apply("childKill", userdata, .{child});
            }
            pub fn progressParentFile(userdata: ?*anyopaque) std.Progress.ParentFileError!File {
                return apply("progressParentFile", userdata, .{});
            }
            pub fn now(userdata: ?*anyopaque, clock: Clock) Timestamp {
                return apply("now", userdata, .{clock});
            }
            pub fn clockResolution(userdata: ?*anyopaque, clock: Clock) Clock.ResolutionError!Duration {
                return apply("clockResolution", userdata, .{clock});
            }
            pub fn sleep(userdata: ?*anyopaque, timeout: Timeout) Cancelable!void {
                return apply("sleep", userdata, .{timeout});
            }
            pub fn random(userdata: ?*anyopaque, buffer: []u8) void {
                return apply("random", userdata, .{buffer});
            }
            pub fn randomSecure(userdata: ?*anyopaque, buffer: []u8) RandomSecureError!void {
                return apply("randomSecure", userdata, .{buffer});
            }
            pub fn netListenIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ListenOptions) net.IpAddress.ListenError!net.Socket {
                return apply("netListenIp", userdata, .{ address, options });
            }
            pub fn netAccept(userdata: ?*anyopaque, server: net.Socket.Handle, options: net.Server.AcceptOptions) net.Server.AcceptError!net.Socket {
                return apply("netAccept", userdata, .{ server, options });
            }
            pub fn netBindIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.BindOptions) net.IpAddress.BindError!net.Socket {
                return apply("netBindIp", userdata, .{ address, options });
            }
            pub fn netConnectIp(userdata: ?*anyopaque, address: *const net.IpAddress, options: net.IpAddress.ConnectOptions) net.IpAddress.ConnectError!net.Socket {
                return apply("netConnectIp", userdata, .{ address, options });
            }
            pub fn netListenUnix(userdata: ?*anyopaque, address: *const net.UnixAddress, options: net.UnixAddress.ListenOptions) net.UnixAddress.ListenError!net.Socket.Handle {
                return apply("netListenUnix", userdata, .{ address, options });
            }
            pub fn netConnectUnix(userdata: ?*anyopaque, address: *const net.UnixAddress) net.UnixAddress.ConnectError!net.Socket.Handle {
                return apply("netConnectUnix", userdata, .{address});
            }
            pub fn netSocketCreatePair(userdata: ?*anyopaque, options: net.Socket.CreatePairOptions) net.Socket.CreatePairError![2]net.Socket {
                return apply("netSocketCreatePair", userdata, .{options});
            }
            pub fn netSend(userdata: ?*anyopaque, handle: net.Socket.Handle, out: []net.OutgoingMessage, flags: net.SendFlags) struct { ?net.Socket.SendError, usize } {
                return apply("netSend", userdata, .{ handle, out, flags });
            }
            pub fn netRead(userdata: ?*anyopaque, src: net.Socket.Handle, data: [][]u8) net.Stream.Reader.Error!usize {
                return apply("netRead", userdata, .{ src, data });
            }
            pub fn netWrite(userdata: ?*anyopaque, dest: net.Socket.Handle, header: []const u8, data: []const []const u8, splat: usize) net.Stream.Writer.Error!usize {
                return apply("netWrite", userdata, .{ dest, header, data, splat });
            }
            pub fn netWriteFile(userdata: ?*anyopaque, handle: net.Socket.Handle, header: []const u8, reader: *Io.File.Reader, limit: Io.Limit) net.Stream.Writer.WriteFileError!usize {
                return apply("netWriteFile", userdata, .{ handle, header, reader, limit });
            }
            pub fn netClose(userdata: ?*anyopaque, handle: []const net.Socket.Handle) void {
                return apply("netClose", userdata, .{handle});
            }
            pub fn netShutdown(userdata: ?*anyopaque, handle: net.Socket.Handle, how: net.ShutdownHow) net.ShutdownError!void {
                return apply("netShutdown", userdata, .{ handle, how });
            }
            pub fn netInterfaceNameResolve(userdata: ?*anyopaque, name: *const net.Interface.Name) net.Interface.Name.ResolveError!net.Interface {
                return apply("netInterfaceNameResolve", userdata, .{name});
            }
            pub fn netInterfaceName(userdata: ?*anyopaque, interface: net.Interface) net.Interface.NameError!net.Interface.Name {
                return apply("netInterfaceName", userdata, .{interface});
            }
            pub fn netLookup(userdata: ?*anyopaque, name: net.HostName, queue: *Queue(net.HostName.LookupResult), options: net.HostName.LookupOptions) net.HostName.LookupError!void {
                return apply("netLookup", userdata, .{ name, queue, options });
            }
        };
        const Overrides = struct {
            pub fn dirCreateFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.CreateFileOptions) File.OpenError!File {
                const self: *FileHandleTracker = @ptrCast(@alignCast(userdata));
                const f = self.orig.vtable.dirCreateFile(self.orig.userdata, dir, sub_path, options) catch |err| return err;
                self.handles.put(self.allocator, f.handle, {}) catch @panic("OOM");
                return f;
            }
            pub fn dirCreateFileAtomic(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.CreateFileAtomicOptions) Dir.CreateFileAtomicError!File.Atomic {
                const self: *FileHandleTracker = @ptrCast(@alignCast(userdata));
                const a = self.orig.vtable.dirCreateFileAtomic(self.orig.userdata, dir, sub_path, options) catch |err| return err;
                if (a.file_open) self.handles.put(self.allocator, a.file.handle, {}) catch @panic("OOM");
                return a;
            }
            pub fn dirOpenFile(userdata: ?*anyopaque, dir: Dir, sub_path: []const u8, options: Dir.OpenFileOptions) File.OpenError!File {
                const self: *FileHandleTracker = @ptrCast(@alignCast(userdata));
                const f = self.orig.vtable.dirOpenFile(self.orig.userdata, dir, sub_path, options) catch |err| return err;
                self.handles.put(self.allocator, f.handle, {}) catch @panic("OOM");
                return f;
            }
            pub fn fileClose(userdata: ?*anyopaque, files: []const File) void {
                const self: *FileHandleTracker = @ptrCast(@alignCast(userdata));
                self.orig.vtable.fileClose(self.orig.userdata, files);
                for (files) |file| {
                    _ = self.handles.swapRemove(file.handle); // TODO should this error on closing a file that wasn't opened (with this Io), if it returns false?
                }
            }
        };
        for (@typeInfo(std.Io.VTable).@"struct".fields) |f| {
            if (@hasDecl(Overrides, f.name)) {
                @field(res, f.name) = &@field(Overrides, f.name);
                continue;
            }
            if (@hasDecl(Passthrough, f.name)) {
                @field(res, f.name) = &@field(Passthrough, f.name);
                continue;
            }
            @compileError("missing io vtable entry for: " ++ f.name);
        }
        break :blk res;
    };
};

const std = @import("std");
const Io = std.Io;
const Dir = Io.Dir;
const File = Io.File;
const Cancelable = Io.Cancelable;
const LockedStderr = Io.LockedStderr;
const Timeout = Io.Timeout;
const Timestamp = Io.Timestamp;
const Clock = Io.Clock;
const Duration = Io.Duration;
const Operation = Io.Operation;
const Queue = Io.Queue;
const RandomSecureError = Io.RandomSecureError;
const ConcurrentError = Io.ConcurrentError;
const CancelProtection = Io.CancelProtection;
const Terminal = Io.Terminal;
const AnyFuture = Io.AnyFuture;
const Group = Io.Group;
const Batch = Io.Batch;
const net = Io.net;

I guess you could use code generation to make it shorter.
Anyone has an idea how to make it simpler?

3 Likes