import Foundation struct NoteInfo: Identifiable { let id: UUID var title: String var lastModified: Date } class RustBridge { static let shared = RustBridge() private var notes: [UUID: String] = [:] private var noteMeta: [UUID: NoteInfo] = [:] private init() {} // MARK: - Document operations (stubbed) func newDocument() -> UUID { let id = UUID() notes[id] = "" let info = NoteInfo(id: id, title: "Untitled", lastModified: Date()) noteMeta[id] = info return id } func freeDocument(_ id: UUID) { notes.removeValue(forKey: id) noteMeta.removeValue(forKey: id) } func setText(_ id: UUID, text: String) { notes[id] = text if var info = noteMeta[id] { info.title = titleFromText(text) info.lastModified = Date() noteMeta[id] = info } } func getText(_ id: UUID) -> String { return notes[id] ?? "" } func evaluate(_ id: UUID) -> [Int: String] { // Stub: parse lines for /= prefix, return placeholder results guard let text = notes[id] else { return [:] } var results: [Int: String] = [:] let lines = text.components(separatedBy: "\n") for (i, line) in lines.enumerated() { let trimmed = line.trimmingCharacters(in: .whitespaces) if trimmed.hasPrefix("/=") { let expr = String(trimmed.dropFirst(2)).trimmingCharacters(in: .whitespaces) results[i] = stubbedEval(expr) } } return results } func evaluateLine(_ line: String) -> String { let trimmed = line.trimmingCharacters(in: .whitespaces) if trimmed.hasPrefix("/=") { let expr = String(trimmed.dropFirst(2)).trimmingCharacters(in: .whitespaces) return stubbedEval(expr) } return "" } // MARK: - File I/O (stubbed with UserDefaults) func saveNote(_ id: UUID, path: String) -> Bool { guard let text = notes[id] else { return false } do { try text.write(toFile: path, atomically: true, encoding: .utf8) return true } catch { return false } } func loadNote(path: String) -> (UUID, String)? { guard let text = try? String(contentsOfFile: path, encoding: .utf8) else { return nil } let id = UUID() notes[id] = text let info = NoteInfo(id: id, title: titleFromText(text), lastModified: Date()) noteMeta[id] = info return (id, text) } // MARK: - Cache (stubbed with local storage) func cacheDir() -> URL { let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first! let dir = appSupport.appendingPathComponent("Swiftly", isDirectory: true) try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) return dir } func cacheSave(_ id: UUID) -> Bool { guard let text = notes[id], let info = noteMeta[id] else { return false } let dir = cacheDir().appendingPathComponent(id.uuidString, isDirectory: true) try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true) let textFile = dir.appendingPathComponent("content.txt") let metaFile = dir.appendingPathComponent("meta.json") do { try text.write(to: textFile, atomically: true, encoding: .utf8) let meta: [String: String] = [ "title": info.title, "lastModified": ISO8601DateFormatter().string(from: info.lastModified) ] let data = try JSONSerialization.data(withJSONObject: meta) try data.write(to: metaFile, options: .atomic) return true } catch { return false } } func cacheLoad(_ id: UUID) -> Bool { let dir = cacheDir().appendingPathComponent(id.uuidString, isDirectory: true) let textFile = dir.appendingPathComponent("content.txt") let metaFile = dir.appendingPathComponent("meta.json") guard let text = try? String(contentsOf: textFile, encoding: .utf8), let metaData = try? Data(contentsOf: metaFile), let meta = try? JSONSerialization.jsonObject(with: metaData) as? [String: String] else { return false } notes[id] = text let formatter = ISO8601DateFormatter() let date = formatter.date(from: meta["lastModified"] ?? "") ?? Date() noteMeta[id] = NoteInfo(id: id, title: meta["title"] ?? "Untitled", lastModified: date) return true } func listNotes() -> [NoteInfo] { let dir = cacheDir() guard let entries = try? FileManager.default.contentsOfDirectory(at: dir, includingPropertiesForKeys: nil) else { return [] } var result: [NoteInfo] = [] let formatter = ISO8601DateFormatter() for entry in entries { guard entry.hasDirectoryPath, let uuid = UUID(uuidString: entry.lastPathComponent) else { continue } let metaFile = entry.appendingPathComponent("meta.json") guard let data = try? Data(contentsOf: metaFile), let meta = try? JSONSerialization.jsonObject(with: data) as? [String: String] else { continue } let date = formatter.date(from: meta["lastModified"] ?? "") ?? Date() let info = NoteInfo(id: uuid, title: meta["title"] ?? "Untitled", lastModified: date) result.append(info) if noteMeta[uuid] == nil { noteMeta[uuid] = info } } return result.sorted { $0.lastModified > $1.lastModified } } func deleteNote(_ id: UUID) { notes.removeValue(forKey: id) noteMeta.removeValue(forKey: id) let dir = cacheDir().appendingPathComponent(id.uuidString, isDirectory: true) try? FileManager.default.removeItem(at: dir) } // MARK: - Internal private func titleFromText(_ text: String) -> String { let firstLine = text.components(separatedBy: "\n").first ?? "" let trimmed = firstLine.trimmingCharacters(in: .whitespaces) if trimmed.isEmpty { return "Untitled" } let clean = trimmed.replacingOccurrences(of: "^#+\\s*", with: "", options: .regularExpression) let maxLen = 60 if clean.count > maxLen { return String(clean.prefix(maxLen)) + "..." } return clean } private func stubbedEval(_ expr: String) -> String { // Stub: attempt basic arithmetic, otherwise return placeholder let components = expr.components(separatedBy: .whitespaces).filter { !$0.isEmpty } if components.count == 3, let a = Double(components[0]), let b = Double(components[2]) { let op = components[1] switch op { case "+": return formatNumber(a + b) case "-": return formatNumber(a - b) case "*": return formatNumber(a * b) case "/": return b != 0 ? formatNumber(a / b) : "div/0" default: break } } if components.count == 1, let n = Double(components[0]) { return formatNumber(n) } return "..." } private func formatNumber(_ n: Double) -> String { if n == n.rounded() && abs(n) < 1e15 { return String(format: "%.0f", n) } return String(format: "%.6g", n) } }