import Foundation import FSKit @available(macOS 15.4, *) public class MarkBaseFSModule: NSObject, FSUnaryFileSystemOperations, @unchecked Sendable { private var rustFS: NSObject? private var rustHandle: UnsafeMutableRawPointer? public required override init() { super.init() // Don't load dylib here - will be loaded in didFinishLoading() } deinit { if let handle = rustHandle { Darwin.dlclose(handle) } } // MARK: - FSUnaryFileSystemOperations public func probeResource(resource: FSResource, replyHandler: @escaping (FSProbeResult?, Error?) -> Void) { print("MarkBaseFSModule: probeResource() called") guard let rustFS = rustFS else { print("MarkBaseFSModule: Rust FS not loaded, returning error") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Rust FS not loaded"])) return } print("MarkBaseFSModule: Rust FS available, but ObjC interop limitation - cannot forward calls") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -3, userInfo: [NSLocalizedDescriptionKey: "Swift ObjC interop limitation"])) } public func loadResource(resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (FSVolume?, Error?) -> Void) { print("MarkBaseFSModule: loadResource() called") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Swift ObjC interop limitation"])) } public func unloadResource(resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (Error?) -> Void) { print("MarkBaseFSModule: unloadResource() called") replyHandler(NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Swift ObjC interop limitation"])) } public func didFinishLoading() { print("MarkBaseFSModule: didFinishLoading() called") print(" - Module loaded by FSKit daemon") // Load Rust dylib and call registration functions loadRustDylibAndRegister() print(" - Rust FS instance: \(rustFS != nil ? "available" : "not available")") } private func loadRustDylibAndRegister() { // 1. Get Extension bundle path let bundlePath = Bundle.main.bundlePath let dylibPath = "\(bundlePath)/Frameworks/libmarkbase_fskit.dylib" print("MarkBaseFSModule: Loading Rust dylib from: \(dylibPath)") // 2. dlopen Rust dylib rustHandle = Darwin.dlopen(dylibPath, RTLD_NOW | RTLD_LOCAL) guard rustHandle != nil else { if let error = Darwin.dlerror() { let errorString = String(cString: error) print("MarkBaseFSModule: Failed to load Rust dylib: \(errorString)") } else { print("MarkBaseFSModule: Failed to load Rust dylib (unknown error)") } return } print("MarkBaseFSModule: Rust dylib loaded successfully") // 3. Call registration functions to register ObjC classes let registerFuncs = [ "__REGISTER_CLASS_MarkBaseFS", "__REGISTER_CLASS_MarkBaseFSItem", "__REGISTER_CLASS_MarkBaseVolume", ] for funcName in registerFuncs { if let registerFunc = Darwin.dlsym(rustHandle!, funcName) { print("MarkBaseFSModule: Calling registration function: \(funcName)") let registerFuncPtr = unsafeBitCast(registerFunc, to: (@convention(c) () -> Void).self) registerFuncPtr() } else { print("MarkBaseFSModule: Registration function not found: \(funcName)") } } print("MarkBaseFSModule: All ObjC classes registered") // 4. Now create instance of Rust's MarkBaseFS class let className = "MarkBaseFS" as NSString if let utf8String = className.utf8String { if let fsClass = objc_lookUpClass(utf8String) { print("MarkBaseFSModule: Class obtained: \(className)") // Create instance if let instance = (fsClass as? NSObject.Type)?.init() { rustFS = instance print("MarkBaseFSModule: Rust FSKit instance created successfully") // Verify methods let selector1 = NSSelectorFromString("probeResource:replyHandler:") let selector2 = NSSelectorFromString("loadResource:options:replyHandler:") print(" - responds to probeResource: \(rustFS!.responds(to: selector1))") print(" - responds to loadResource: \(rustFS!.responds(to: selector2))") } else { print("MarkBaseFSModule: Failed to create instance") } } else { print("MarkBaseFSModule: Class not found after registration") } } } } // MARK: - Helper function private func objc_lookUpClass(_ name: UnsafePointer) -> AnyClass? { return objc_getClass(name) as? AnyClass }