FOSSredder 0.5.0
Loading...
Searching...
No Matches
WorkspaceStateStore.cpp
Go to the documentation of this file.
1
6
8
32
33#include <algorithm>
34#include <stdexcept>
35#include <string>
36#include <unordered_map>
37#include <unordered_set>
38#include <utility>
39#include <vector>
40
41namespace {
42
43using IdSet = std::unordered_set<std::string>;
44
45struct SnapshotIds {
46 std::vector<std::string> actors;
47 std::vector<std::string> properties;
48 std::vector<std::string> contracts;
49 std::vector<std::string> statements;
50 std::vector<std::string> transactions;
51 std::vector<std::string> analyses;
52 std::vector<std::string> annuals;
53 std::vector<std::string> statementDrafts;
54 std::vector<std::string> transactionDrafts;
55 std::vector<std::string> importLogs;
56 std::vector<std::string> exportLogs;
57 std::unordered_map<std::string, std::string> transactionStatementById;
58};
59
61createRepositoryBundle(const std::shared_ptr<SqliteDb>& db) {
63 bundle.actors = std::make_shared<SqliteActorRepository>(db);
64 bundle.properties = std::make_shared<SqlitePropertyRepository>(db);
65 bundle.contracts = std::make_shared<SqliteContractRepository>(db);
66 bundle.statements = std::make_shared<SqliteStatementRepository>(db);
67 bundle.transactions = std::make_shared<SqliteTransactionRepository>(db);
68 bundle.statementDrafts = std::make_shared<SqliteStatementDraftRepository>(db);
69 bundle.transactionDrafts =
70 std::make_shared<SqliteTransactionDraftRepository>(db);
71 bundle.analyses = std::make_shared<SqliteAnalysisRepository>(db);
72 bundle.annuals = std::make_shared<SqliteAnnualRepository>(db);
73 bundle.importLogs = std::make_shared<SqliteImportLogRepository>(db);
74 bundle.exportLogs = std::make_shared<SqliteExportLogRepository>(db);
75 return bundle;
76}
77
78template<typename Collection>
79std::vector<std::string> collectSortedIds(const Collection& items) {
80 std::vector<std::string> ids;
81 ids.reserve(items.size());
82 for (const auto& item : items) {
83 if (!item) {
84 continue;
85 }
86
87 if constexpr (requires { item->id(); }) {
88 if (!item->id().empty()) {
89 ids.push_back(item->id());
90 }
91 } else if constexpr (requires { item->id; }) {
92 if (!item->id.empty()) {
93 ids.push_back(item->id);
94 }
95 }
96 }
97
98 std::sort(ids.begin(), ids.end());
99 ids.erase(std::unique(ids.begin(), ids.end()), ids.end());
100 return ids;
101}
102
103template<typename RepoPtr, typename LoadFn>
104std::vector<std::string> collectRepositoryIds(const RepoPtr& repo,
105 LoadFn&& load) {
106 if (!repo) {
107 return {};
108 }
109
110 return collectSortedIds(load(*repo));
111}
112
113IdSet toIdSet(const std::vector<std::string>& ids) {
114 return IdSet(ids.begin(), ids.end());
115}
116
117SnapshotIds loadExistingIds(const core::storage::RepositoryBundle& repos) {
118 SnapshotIds snapshot;
119 snapshot.actors = collectRepositoryIds(repos.actors, [](const auto& repo) {
120 return repo.getActors();
121 });
122 snapshot.properties =
123 collectRepositoryIds(repos.properties, [](const auto& repo) {
124 return repo.getProperties();
125 });
126 snapshot.contracts =
127 collectRepositoryIds(repos.contracts, [](const auto& repo) {
128 return repo.getContracts();
129 });
130 snapshot.statements =
131 collectRepositoryIds(repos.statements, [](const auto& repo) {
132 return repo.getStatements();
133 });
134 snapshot.transactions =
135 collectRepositoryIds(repos.transactions, [](const auto& repo) {
136 return repo.getTransactions();
137 });
138 snapshot.analyses =
139 collectRepositoryIds(repos.analyses, [](const auto& repo) {
140 return repo.getAnalyses();
141 });
142 snapshot.annuals = collectRepositoryIds(repos.annuals, [](const auto& repo) {
143 return repo.getAnnuals();
144 });
145 snapshot.statementDrafts =
146 collectRepositoryIds(repos.statementDrafts, [](const auto& repo) {
147 return repo.getStatementDrafts();
148 });
149 snapshot.transactionDrafts =
150 collectRepositoryIds(repos.transactionDrafts, [](const auto& repo) {
151 return repo.getTransactionDrafts();
152 });
153 snapshot.importLogs =
154 collectRepositoryIds(repos.importLogs, [](const auto& repo) {
155 return repo.getImportLogs();
156 });
157 snapshot.exportLogs =
158 collectRepositoryIds(repos.exportLogs, [](const auto& repo) {
159 return repo.getExportLogs();
160 });
161
162 if (repos.transactions) {
163 for (const auto& transaction : repos.transactions->getTransactions()) {
164 if (!transaction || transaction->id().empty()) {
165 continue;
166 }
167
168 snapshot.transactionStatementById.emplace(transaction->id(),
169 transaction->statementId());
170 }
171 }
172
173 return snapshot;
174}
175
176SnapshotIds loadCurrentIds(
178 SnapshotIds snapshot;
179 const auto& catalog = document.catalog;
180 const auto& workflow = document.workflow;
181 snapshot.actors = collectSortedIds(catalog.actors());
182 snapshot.properties = collectSortedIds(catalog.properties());
183 snapshot.contracts = collectSortedIds(catalog.contracts());
184 snapshot.statements = collectSortedIds(catalog.statements());
185 snapshot.transactions = collectSortedIds(catalog.transactions());
186 snapshot.analyses = collectSortedIds(catalog.analyses());
187 snapshot.annuals = collectSortedIds(catalog.annuals());
188 snapshot.statementDrafts = collectSortedIds(workflow.statementDrafts);
189 snapshot.transactionDrafts = collectSortedIds(workflow.transactionDrafts);
190 snapshot.importLogs = collectSortedIds(workflow.importLogs);
191 snapshot.exportLogs = collectSortedIds(workflow.exportLogs);
192
193 for (const auto& transaction : catalog.transactions()) {
194 if (!transaction || transaction->id().empty()) {
195 continue;
196 }
197
198 snapshot.transactionStatementById.emplace(transaction->id(),
199 transaction->statementId());
200 }
201
202 return snapshot;
203}
204
205template<typename RepoPtr, typename RemoveFn>
206void removeMissingIds(const RepoPtr& repo,
207 const std::vector<std::string>& existingIds,
208 const IdSet& currentIds, RemoveFn&& remove,
209 std::vector<std::string>* deletedIds = nullptr) {
210 if (!repo) {
211 return;
212 }
213
214 for (const auto& id : existingIds) {
215 if (currentIds.contains(id)) {
216 continue;
217 }
218
219 remove(*repo, id);
220 if (deletedIds) {
221 deletedIds->push_back(id);
222 }
223 }
224}
225
226void removeMissingTransactions(
227 const core::storage::RepositoryBundle& repos, const SnapshotIds& existing,
228 const SnapshotIds& current,
229 const std::vector<std::string>& deletedStatements,
230 std::vector<std::string>& deletedTransactions) {
231 if (!repos.transactions) {
232 return;
233 }
234
235 const IdSet currentTransactionIds = toIdSet(current.transactions);
236 const IdSet deletedStatementIds(deletedStatements.begin(),
237 deletedStatements.end());
238
239 for (const auto& transactionId : existing.transactions) {
240 if (currentTransactionIds.contains(transactionId)) {
241 continue;
242 }
243
244 const auto statementIt =
245 existing.transactionStatementById.find(transactionId);
246 if (statementIt != existing.transactionStatementById.end() &&
247 deletedStatementIds.contains(statementIt->second)) {
248 continue;
249 }
250
251 repos.transactions->removeTransaction(transactionId);
252 deletedTransactions.push_back(transactionId);
253 }
254}
255
256void syncStatementTransactions(
257 sqlite3* db,
259 if (!db) {
260 return;
261 }
262
263 persistence::StmtGuard deleteStmt(db, "DELETE FROM statement_transactions;");
264 if (!deleteStmt) {
265 return;
266 }
267 deleteStmt.step();
268
269 persistence::StmtGuard insertStmt(
270 db, "INSERT OR REPLACE INTO statement_transactions (statement_id, "
271 "transaction_id, position) VALUES (?, ?, ?);");
272 if (!insertStmt) {
273 return;
274 }
275
276 for (const auto& statement : document.catalog.statements()) {
277 if (!statement || statement->id().empty()) {
278 continue;
279 }
280
281 const auto& transactionIds = statement->transactionIds();
282 for (std::size_t i = 0; i < transactionIds.size(); ++i) {
283 const auto& transactionId = transactionIds[i];
284 if (transactionId.empty()) {
285 continue;
286 }
287
288 insertStmt.reset();
289 insertStmt.bindText(1, statement->id());
290 insertStmt.bindText(2, transactionId);
291 insertStmt.bindInt(3, static_cast<int>(i));
292 insertStmt.step();
293 }
294 }
295}
296
297} // namespace
298
299WorkspaceStateStore::WorkspaceStateStore(std::shared_ptr<SqliteDb> db)
300 : db_(std::move(db)) {
301 if (!db_) {
302 throw std::runtime_error("db is null");
303 }
304}
305
308 auto repos = createRepositoryBundle(db_);
309 core::application::WorkspaceStateManager manager(std::move(repos));
310 return manager.load();
311}
312
316
317 auto repos = createRepositoryBundle(db_);
318 SqliteTransaction transaction(db_->handle());
319
320 const auto existing = loadExistingIds(repos);
321 const auto current = loadCurrentIds(document);
322
323 removeMissingIds(
324 repos.statements, existing.statements, toIdSet(current.statements),
325 [](auto& repo, const std::string& id) {
326 repo.removeStatement(id);
327 },
328 &impact.deletedStatementIds);
329
330 removeMissingTransactions(repos, existing, current,
331 impact.deletedStatementIds,
332 impact.deletedTransactionIds);
333
334 removeMissingIds(
335 repos.contracts, existing.contracts, toIdSet(current.contracts),
336 [](auto& repo, const std::string& id) {
337 repo.removeContract(id);
338 },
339 &impact.deletedContractIds);
340 removeMissingIds(
341 repos.actors, existing.actors, toIdSet(current.actors),
342 [](auto& repo, const std::string& id) {
343 repo.removeActor(id);
344 },
345 &impact.deletedActorIds);
346 removeMissingIds(
347 repos.properties, existing.properties, toIdSet(current.properties),
348 [](auto& repo, const std::string& id) {
349 repo.removeProperty(id);
350 },
351 &impact.deletedPropertyIds);
352 removeMissingIds(
353 repos.analyses, existing.analyses, toIdSet(current.analyses),
354 [](auto& repo, const std::string& id) {
355 repo.removeAnalysis(id);
356 },
357 &impact.deletedAnalysisIds);
358 removeMissingIds(
359 repos.annuals, existing.annuals, toIdSet(current.annuals),
360 [](auto& repo, const std::string& id) {
361 repo.removeAnnual(id);
362 },
363 &impact.deletedAnnualIds);
364
365 removeMissingIds(
366 repos.statementDrafts, existing.statementDrafts,
367 toIdSet(current.statementDrafts),
368 [](auto& repo, const std::string& id) {
369 repo.removeStatementDraft(id);
370 },
372 removeMissingIds(
373 repos.transactionDrafts, existing.transactionDrafts,
374 toIdSet(current.transactionDrafts),
375 [](auto& repo, const std::string& id) {
376 repo.removeTransactionDraft(id);
377 },
379 removeMissingIds(
380 repos.importLogs, existing.importLogs, toIdSet(current.importLogs),
381 [](auto& repo, const std::string& id) {
382 repo.removeImportLog(id);
383 },
384 &impact.deletedImportLogIds);
385 removeMissingIds(
386 repos.exportLogs, existing.exportLogs, toIdSet(current.exportLogs),
387 [](auto& repo, const std::string& id) {
388 repo.removeExportLog(id);
389 },
390 &impact.deletedExportLogIds);
391
392 core::application::WorkspaceStateManager manager(std::move(repos));
393 manager.save(document);
394 syncStatementTransactions(db_->handle(), document);
395
396 transaction.commit();
397 return impact;
398}
Domain model for persisted analysis definitions.
Domain model for annual aggregates.
Declares the persisted metadata record for an export run.
Declares the persisted metadata record for an import run.
Aggregates repository ports required by workspace state persistence.
Declares the SQLite-backed actor repository.
Declares the SQLite-backed analysis repository.
Declares the SQLite-backed annual repository.
Declares the SQLite-backed contract repository.
Declares the lightweight SQLite connection wrapper.
Declares the SQLite-backed export log repository.
Declares the SQLite-backed import log repository.
Declares the SQLite-backed property repository.
Declares the SQLite-backed statement draft repository.
Declares the SQLite-backed statement repository.
Declares a lightweight SQLite transaction guard.
Declares the SQLite-backed transaction draft repository.
Declares the SQLite-backed transaction repository.
Draft statement model used by import finalization and core application flow.
RAII wrapper for sqlite3_stmt* that auto-finalizes on destruction.
Draft transaction model used by import finalization and core application flow.
Internal mutable workspace session state for application services.
Loader/saver for internal workspace session state using repository abstractions.
Declares SQLite-backed loading and saving of the full application state.
void commit()
Commit the transaction.
core::application::workspace::WorkspaceSessionState load()
Loads the complete persisted application state from the database.
core::domain::DeletionImpact save(const core::application::workspace::WorkspaceSessionState &document)
Saves the complete application state and reports deletion impact details.
WorkspaceStateStore(std::shared_ptr< SqliteDb > db)
Creates a state store over an existing SQLite database connection wrapper.
Loads and saves aggregate workspace state via repository abstractions.
void save(const core::application::workspace::WorkspaceSessionState &document)
Projects, validates and persists workspace session state.
core::application::workspace::WorkspaceSessionState load()
Loads, rehydrates and validates workspace session state.
const StatementList & statements() const noexcept
Returns all statements in the catalog.
core::domain::catalog::WorkspaceCatalog catalog
Lists entity identifiers removed by cascading workspace operations.
std::vector< std::string > deletedAnnualIds
std::vector< std::string > deletedExportLogIds
std::vector< std::string > deletedStatementIds
std::vector< std::string > deletedActorIds
std::vector< std::string > deletedImportLogIds
std::vector< std::string > deletedPropertyIds
std::vector< std::string > deletedTransactionIds
std::vector< std::string > deletedStatementDraftIds
std::vector< std::string > deletedAnalysisIds
std::vector< std::string > deletedContractIds
std::vector< std::string > deletedTransactionDraftIds
Groups repository implementations consumed by WorkspaceStateManager.
std::shared_ptr< core::ports::repositories::IExportLogRepository > exportLogs
std::shared_ptr< core::ports::repositories::IStatementDraftRepository > statementDrafts
std::shared_ptr< core::ports::repositories::IPropertyRepository > properties
std::shared_ptr< core::ports::repositories::IActorRepository > actors
std::shared_ptr< core::ports::repositories::IStatementRepository > statements
std::shared_ptr< core::ports::repositories::IContractRepository > contracts
std::shared_ptr< core::ports::repositories::ITransactionDraftRepository > transactionDrafts
std::shared_ptr< core::ports::repositories::IImportLogRepository > importLogs
std::shared_ptr< core::ports::repositories::IAnalysisRepository > analyses
std::shared_ptr< core::ports::repositories::ITransactionRepository > transactions
std::shared_ptr< core::ports::repositories::IAnnualRepository > annuals