events/ → events.log; EventStore takes a file URL directly. Open copies follow the new name.
104 lines
3.3 KiB
Swift
104 lines
3.3 KiB
Swift
import Foundation
|
|
|
|
public struct LumaDocument: Sendable, Equatable {
|
|
public enum Storage: Sendable, Equatable {
|
|
case file(URL)
|
|
case untitled(URL)
|
|
}
|
|
|
|
public var storage: Storage
|
|
|
|
public var url: URL {
|
|
switch storage {
|
|
case .file(let u), .untitled(let u):
|
|
return u
|
|
}
|
|
}
|
|
|
|
public var displayName: String {
|
|
switch storage {
|
|
case .file(let u):
|
|
let name = u.deletingPathExtension().lastPathComponent
|
|
return name.isEmpty ? "Untitled" : name
|
|
case .untitled:
|
|
return "Untitled"
|
|
}
|
|
}
|
|
|
|
public var isUntitled: Bool {
|
|
if case .untitled = storage { return true }
|
|
return false
|
|
}
|
|
|
|
public var sqliteURL: URL { url.appendingPathComponent("db.sqlite") }
|
|
public var sqlitePath: String { sqliteURL.path }
|
|
public var tracesDirectory: URL { url.appendingPathComponent("traces", isDirectory: true) }
|
|
public var eventsLogURL: URL { url.appendingPathComponent("events.log") }
|
|
|
|
public init(storage: Storage) {
|
|
self.storage = storage
|
|
}
|
|
}
|
|
|
|
public enum LumaDocumentError: Error {
|
|
case invalidPath(URL)
|
|
case copyFailed(URL, URL, Error)
|
|
}
|
|
|
|
public enum LumaDocumentLoader {
|
|
public static let fileExtension = "luma"
|
|
|
|
public static func open(at url: URL) throws -> LumaDocument {
|
|
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
throw LumaDocumentError.invalidPath(url)
|
|
}
|
|
return LumaDocument(storage: .file(url))
|
|
}
|
|
|
|
/// Reopen a document that was previously auto-saved at `url` inside
|
|
/// the untitled directory. Keeps the `.untitled` storage kind so
|
|
/// the title bar still shows "Untitled" and Save As prompts for a
|
|
/// new location instead of overwriting the auto-save file.
|
|
public static func openUntitled(at url: URL) throws -> LumaDocument {
|
|
guard FileManager.default.fileExists(atPath: url.path) else {
|
|
throw LumaDocumentError.invalidPath(url)
|
|
}
|
|
return LumaDocument(storage: .untitled(url))
|
|
}
|
|
|
|
public static func makeUntitled(in directory: URL) throws -> LumaDocument {
|
|
let fm = FileManager.default
|
|
try fm.createDirectory(at: directory, withIntermediateDirectories: true)
|
|
|
|
let url: URL = {
|
|
for index in 0..<4096 {
|
|
let name = index == 0 ? "Untitled.luma" : "Untitled \(index).luma"
|
|
let candidate = directory.appendingPathComponent(name)
|
|
if !fm.fileExists(atPath: candidate.path) {
|
|
return candidate
|
|
}
|
|
}
|
|
return directory.appendingPathComponent("Untitled-\(UUID().uuidString).luma")
|
|
}()
|
|
|
|
try fm.createDirectory(at: url, withIntermediateDirectories: true)
|
|
return LumaDocument(storage: .untitled(url))
|
|
}
|
|
|
|
public static func saveAs(
|
|
_ document: LumaDocument,
|
|
to destination: URL
|
|
) throws -> LumaDocument {
|
|
let fm = FileManager.default
|
|
if fm.fileExists(atPath: destination.path) {
|
|
try fm.removeItem(at: destination)
|
|
}
|
|
do {
|
|
try fm.copyItem(at: document.url, to: destination)
|
|
} catch {
|
|
throw LumaDocumentError.copyFailed(document.url, destination, error)
|
|
}
|
|
return LumaDocument(storage: .file(destination))
|
|
}
|
|
}
|