import Foundation import FSKit import dlfcn @available(macOS 15.4, *) public class MarkBaseFSModule: NSObject, FSUnaryFileSystemOperations, @unchecked Sendable { private var rustFS: AnyObject? private var rustHandle: UnsafeMutableRawPointer? public required override init() { super.init() loadRustDylib() } deinit { if let handle = rustHandle { dlclose(handle) } } private func loadRustDylib() { // 1. Get Extension bundle path guard let bundlePath = Bundle.main.bundlePath else { print("MarkBaseFSModule: Failed to get bundle path") return } // 2. Dylib path in Frameworks folder let dylibPath = "\(bundlePath)/Frameworks/libmarkbase_fskit.dylib" print("MarkBaseFSModule: Attempting to load Rust dylib from: \(dylibPath)") // 3. dlopen Rust dylib rustHandle = dlopen(dylibPath, RTLD_NOW | RTLD_GLOBAL) guard rustHandle != nil else { if let error = 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") // 4. Get Rust exported MarkBaseFS ObjC class let className = "MarkBaseFS" guard let fsClass = objc_getClass(className) as? AnyClass else { print("MarkBaseFSModule: Failed to get Rust class: \(className)") print("MarkBaseFSModule: Checking if class is registered...") // Try to call registration function let registerFuncName = "__REGISTER_CLASS_MarkBaseFS" if let registerFunc = dlsym(rustHandle!, registerFuncName) { print("MarkBaseFSModule: Found registration function: \(registerFuncName)") // Call registration function let register: @convention(c) () -> Void = unsafeBitCast(registerFunc, to: @convention(c) () -> Void.self) register() print("MarkBaseFSModule: Registration function called") // Try again guard let fsClassAfterRegister = objc_getClass(className) as? AnyClass else { print("MarkBaseFSModule: Still failed to get class after registration") return } print("MarkBaseFSModule: Class obtained after registration") rustFS = (fsClassAfterRegister as! NSObject.Type).init() } else { print("MarkBaseFSModule: Registration function not found") return } } else { print("MarkBaseFSModule: Class obtained directly: \(className)") rustFS = (fsClass as! NSObject.Type).init() } print("MarkBaseFSModule: Rust FSKit instance created successfully") } // MARK: - FSUnaryFileSystemOperations (Forward to Rust) 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 not recognized") let result = FSProbeResult.notRecognizedProbeResult() replyHandler(result, nil) return } print("MarkBaseFSModule: Forwarding probeResource to Rust") // Create block callback let block: @convention(block) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void = { resultPtr, errorPtr in print("MarkBaseFSModule: probeResource callback received") if let resultPtr = resultPtr { // Convert pointer to FSProbeResult let result = Unmanaged.fromOpaque(resultPtr).takeRetainedValue() if let probeResult = result as? FSProbeResult { print("MarkBaseFSModule: probeResource result: \(probeResult)") replyHandler(probeResult, nil) } else { print("MarkBaseFSModule: probeResource result type mismatch") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Result type mismatch"])) } } else { print("MarkBaseFSModule: probeResource result is null") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "No result returned"])) } } // Use ObjC runtime to call Rust method let selector = NSSelectorFromString("probeResource:replyHandler:") let methodIMP = rustFS.method(for: selector) if methodIMP != nil { print("MarkBaseFSModule: Method implementation found") let method: @convention(block) (AnyObject, FSResource, AnyObject) -> Void = unsafeBitCast(methodIMP, to: @convention(block) (AnyObject, FSResource, AnyObject) -> Void.self) method(rustFS, resource, unsafeBitCast(block, to: AnyObject.self)) } else { print("MarkBaseFSModule: Method implementation not found") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -2, userInfo: [NSLocalizedDescriptionKey: "Method not found"])) } } public func loadResource(_ resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (FSVolume?, Error?) -> Void) { print("MarkBaseFSModule: loadResource() 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: Forwarding loadResource to Rust") // Create block callback let block: @convention(block) (UnsafeMutableRawPointer?, UnsafeMutableRawPointer?) -> Void = { volumePtr, errorPtr in print("MarkBaseFSModule: loadResource callback received") if let volumePtr = volumePtr { // Convert pointer to FSVolume let volume = Unmanaged.fromOpaque(volumePtr).takeRetainedValue() if let fsVolume = volume as? FSVolume { print("MarkBaseFSModule: loadResource volume: \(fsVolume)") replyHandler(fsVolume, nil) } else { print("MarkBaseFSModule: loadResource volume type mismatch") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Volume type mismatch"])) } } else { print("MarkBaseFSModule: loadResource volume is null") if let errorPtr = errorPtr { let error = Unmanaged.fromOpaque(errorPtr).takeRetainedValue() replyHandler(nil, error) } else { replyHandler(nil, NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "No volume returned"])) } } } // Use ObjC runtime to call Rust method let selector = NSSelectorFromString("loadResource:options:replyHandler:") let methodIMP = rustFS.method(for: selector) if methodIMP != nil { print("MarkBaseFSModule: Method implementation found") let method: @convention(block) (AnyObject, FSResource, FSTaskOptions, AnyObject) -> Void = unsafeBitCast(methodIMP, to: @convention(block) (AnyObject, FSResource, FSTaskOptions, AnyObject) -> Void.self) method(rustFS, resource, options, unsafeBitCast(block, to: AnyObject.self)) } else { print("MarkBaseFSModule: Method implementation not found") replyHandler(nil, NSError(domain: "MarkBaseFS", code: -2, userInfo: [NSLocalizedDescriptionKey: "Method not found"])) } } public func unloadResource(_ resource: FSResource, options: FSTaskOptions, replyHandler: @escaping (Error?) -> Void) { print("MarkBaseFSModule: unloadResource() called") guard let rustFS = rustFS else { print("MarkBaseFSModule: Rust FS not loaded, returning error") replyHandler(NSError(domain: "MarkBaseFS", code: -1, userInfo: [NSLocalizedDescriptionKey: "Rust FS not loaded"])) return } print("MarkBaseFSModule: Forwarding unloadResource to Rust") // Create block callback let block: @convention(block) (UnsafeMutableRawPointer?) -> Void = { errorPtr in print("MarkBaseFSModule: unloadResource callback received") if let errorPtr = errorPtr { let error = Unmanaged.fromOpaque(errorPtr).takeRetainedValue() replyHandler(error) } else { replyHandler(nil) } } // Use ObjC runtime to call Rust method let selector = NSSelectorFromString("unloadResource:options:replyHandler:") let methodIMP = rustFS.method(for: selector) if methodIMP != nil { print("MarkBaseFSModule: Method implementation found") let method: @convention(block) (AnyObject, FSResource, FSTaskOptions, AnyObject) -> Void = unsafeBitCast(methodIMP, to: @convention(block) (AnyObject, FSResource, FSTaskOptions, AnyObject) -> Void.self) method(rustFS, resource, options, unsafeBitCast(block, to: AnyObject.self)) } else { print("MarkBaseFSModule: Method implementation not found") replyHandler(NSError(domain: "MarkBaseFS", code: -2, userInfo: [NSLocalizedDescriptionKey: "Method not found"])) } } public func didFinishLoading() { print("MarkBaseFSModule: didFinishLoading() called") print(" - Module loaded by FSKit daemon") print(" - Rust FSKit bridge active") print(" - Rust FS instance: \(rustFS != nil ? "available" : "not available")") } }