149 lines
4.9 KiB
Swift
149 lines
4.9 KiB
Swift
import Foundation
|
|
|
|
struct NoteInfo: Identifiable {
|
|
let id: UUID
|
|
var title: String
|
|
var lastModified: Date
|
|
}
|
|
|
|
class RustBridge {
|
|
static let shared = RustBridge()
|
|
|
|
private var docs: [UUID: OpaquePointer] = [:]
|
|
|
|
private init() {}
|
|
|
|
func newDocument() -> UUID {
|
|
let ptr = swiftly_doc_new()!
|
|
let uuidStr = cacheSaveRaw(ptr)
|
|
let id = UUID(uuidString: uuidStr) ?? UUID()
|
|
docs[id] = ptr
|
|
return id
|
|
}
|
|
|
|
func freeDocument(_ id: UUID) {
|
|
guard let ptr = docs.removeValue(forKey: id) else { return }
|
|
swiftly_doc_free(ptr)
|
|
}
|
|
|
|
func setText(_ id: UUID, text: String) {
|
|
guard let ptr = docs[id] else { return }
|
|
text.withCString { cstr in
|
|
swiftly_doc_set_text(ptr, cstr)
|
|
}
|
|
}
|
|
|
|
func getText(_ id: UUID) -> String {
|
|
guard let ptr = docs[id] else { return "" }
|
|
guard let cstr = swiftly_doc_get_text(ptr) else { return "" }
|
|
let str = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return str
|
|
}
|
|
|
|
func evaluate(_ id: UUID) -> [Int: String] {
|
|
guard let ptr = docs[id] else { return [:] }
|
|
guard let cstr = swiftly_doc_evaluate(ptr) else { return [:] }
|
|
let json = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return parseEvalJSON(json)
|
|
}
|
|
|
|
func evaluateLine(_ line: String) -> String {
|
|
guard let cstr = line.withCString({ swiftly_eval_line($0) }) else { return "" }
|
|
let str = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return str
|
|
}
|
|
|
|
func saveNote(_ id: UUID, path: String) -> Bool {
|
|
guard let ptr = docs[id] else { return false }
|
|
return path.withCString { cstr in
|
|
swiftly_doc_save(ptr, cstr)
|
|
}
|
|
}
|
|
|
|
func loadNote(path: String) -> (UUID, String)? {
|
|
guard let ptr = path.withCString({ swiftly_doc_load($0) }) else { return nil }
|
|
let uuidStr = cacheSaveRaw(ptr)
|
|
guard let id = UUID(uuidString: uuidStr) else {
|
|
swiftly_doc_free(ptr)
|
|
return nil
|
|
}
|
|
if let old = docs[id] { swiftly_doc_free(old) }
|
|
docs[id] = ptr
|
|
|
|
guard let cstr = swiftly_doc_get_text(ptr) else { return (id, "") }
|
|
let text = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return (id, text)
|
|
}
|
|
|
|
func cacheSave(_ id: UUID) -> Bool {
|
|
guard let ptr = docs[id] else { return false }
|
|
guard let cstr = swiftly_cache_save(ptr) else { return false }
|
|
swiftly_free_string(cstr)
|
|
return true
|
|
}
|
|
|
|
func cacheLoad(_ id: UUID) -> Bool {
|
|
let uuidStr = id.uuidString.lowercased()
|
|
guard let ptr = uuidStr.withCString({ swiftly_cache_load($0) }) else { return false }
|
|
if let old = docs[id] { swiftly_doc_free(old) }
|
|
docs[id] = ptr
|
|
return true
|
|
}
|
|
|
|
func listNotes() -> [NoteInfo] {
|
|
guard let cstr = swiftly_list_notes() else { return [] }
|
|
let json = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return parseNoteListJSON(json)
|
|
}
|
|
|
|
func deleteNote(_ id: UUID) {
|
|
freeDocument(id)
|
|
let cacheDir = FileManager.default.homeDirectoryForCurrentUser
|
|
.appendingPathComponent(".swiftly/cache")
|
|
let cacheFile = cacheDir.appendingPathComponent("\(id.uuidString.lowercased()).sw")
|
|
try? FileManager.default.removeItem(at: cacheFile)
|
|
}
|
|
|
|
// MARK: - Internal
|
|
|
|
private func cacheSaveRaw(_ ptr: OpaquePointer) -> String {
|
|
guard let cstr = swiftly_cache_save(ptr) else { return UUID().uuidString }
|
|
let str = String(cString: cstr)
|
|
swiftly_free_string(cstr)
|
|
return str
|
|
}
|
|
|
|
private func parseEvalJSON(_ json: String) -> [Int: String] {
|
|
guard let data = json.data(using: .utf8) else { return [:] }
|
|
guard let obj = try? JSONSerialization.jsonObject(with: data) as? [String: Any] else { return [:] }
|
|
guard let results = obj["results"] as? [[String: Any]] else { return [:] }
|
|
var dict: [Int: String] = [:]
|
|
for item in results {
|
|
if let line = item["line"] as? Int, let result = item["result"] as? String {
|
|
dict[line] = result
|
|
}
|
|
}
|
|
return dict
|
|
}
|
|
|
|
private func parseNoteListJSON(_ json: String) -> [NoteInfo] {
|
|
guard let data = json.data(using: .utf8) else { return [] }
|
|
guard let arr = try? JSONSerialization.jsonObject(with: data) as? [[String: Any]] else { return [] }
|
|
var notes: [NoteInfo] = []
|
|
for item in arr {
|
|
guard let uuidStr = item["uuid"] as? String,
|
|
let uuid = UUID(uuidString: uuidStr),
|
|
let title = item["title"] as? String else { continue }
|
|
let modified = item["modified"] as? Double ?? 0
|
|
let date = Date(timeIntervalSince1970: modified)
|
|
notes.append(NoteInfo(id: uuid, title: title, lastModified: date))
|
|
}
|
|
return notes.sorted { $0.lastModified > $1.lastModified }
|
|
}
|
|
}
|