-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathConversationStorageService.swift
More file actions
142 lines (113 loc) · 4.76 KB
/
ConversationStorageService.swift
File metadata and controls
142 lines (113 loc) · 4.76 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import Foundation
import CryptoKit
import Logger
extension String {
func appendingPathComponents(_ components: String...) -> String {
var url = URL(fileURLWithPath: self)
components.forEach { component in
url = url.appendingPathComponent(component)
}
return url.path
}
}
protocol ConversationStorageServiceProtocol {
func fetchConversationItems(_ type: ConversationFetchType, metadata: StorageMetadata) -> [ConversationItem]
func fetchTurnItems(for conversationID: String, metadata: StorageMetadata) -> [TurnItem]
func operate(_ request: OperationRequest, metadata: StorageMetadata)
func terminate()
}
public struct StorageMetadata: Hashable {
public var workspacePath: String
public var username: String
public init(workspacePath: String, username: String) {
self.workspacePath = workspacePath
self.username = username
}
}
public final class ConversationStorageService: ConversationStorageServiceProtocol {
private var conversationStoragePool: [StorageMetadata: ConversationStorage] = [:]
public static let shared = ConversationStorageService()
private init() { }
// The storage path would be xdgConfigHome/usernameHash/conversations/workspacePathHash.db
private func getPersistenceFile(_ metadata: StorageMetadata) -> String {
let fileName = "\(ConfigPathUtils.toHash(contents: metadata.workspacePath)).db"
let persistenceFileURL = ConfigPathUtils.configFilePath(
userName: metadata.username,
subDirectory: "conversations",
fileName: fileName
)
return persistenceFileURL.path
}
private func getConversationStorage(_ metadata: StorageMetadata) throws -> ConversationStorage {
if let existConversationStorage = conversationStoragePool[metadata] {
return existConversationStorage
}
let persistenceFile = getPersistenceFile(metadata)
let conversationStorage = try ConversationStorage(persistenceFile)
try conversationStorage.createTableIfNeeded()
conversationStoragePool[metadata] = conversationStorage
return conversationStorage
}
private func ensurePathExists(_ path: String) -> Bool {
do {
let fileManager = FileManager.default
let pathURL = URL(fileURLWithPath: path)
if !fileManager.fileExists(atPath: path) {
try fileManager.createDirectory(at: pathURL, withIntermediateDirectories: true)
}
} catch {
Logger.client.error("Failed to create persistence path: \(error)")
return false
}
return true
}
private func withStorage<T>(_ metadata: StorageMetadata, operation: (ConversationStorage) throws -> T) throws -> T {
let storage = try getConversationStorage(metadata)
return try operation(storage)
}
public func fetchConversationItems(_ type: ConversationFetchType, metadata: StorageMetadata) -> [ConversationItem] {
var items: [ConversationItem] = []
do {
try withStorage(metadata) { conversationStorage in
items = try conversationStorage.fetchConversationItems(type)
}
} catch {
Logger.client.error("Failed to fetch conversation items: \(error)")
}
return items
}
public func fetchConversationPreviewItems(metadata: StorageMetadata) -> [ConversationPreviewItem] {
var items: [ConversationPreviewItem] = []
do {
try withStorage(metadata) { conversationStorage in
items = try conversationStorage.fetchConversationPreviewItems()
}
} catch {
Logger.client.error("Failed to fetch conversation preview items: \(error)")
}
return items
}
public func fetchTurnItems(for conversationID: String, metadata: StorageMetadata) -> [TurnItem] {
var items: [TurnItem] = []
do {
try withStorage(metadata) { conversationStorage in
items = try conversationStorage.fetchTurnItems(for: conversationID)
}
} catch {
Logger.client.error("Failed to fetch turn items: \(error)")
}
return items
}
public func operate(_ request: OperationRequest, metadata: StorageMetadata) {
do {
try withStorage(metadata) { conversationStorage in
try conversationStorage.operate(request)
}
} catch {
Logger.client.error("Failed to operate database request: \(error)")
}
}
public func terminate() {
conversationStoragePool = [:]
}
}