载入中...
搜索中...
未找到
EditorDocumentService.cpp
浏览该文件的文档.
3
4#include <algorithm>
5
6namespace eve::editor {
7
8Result<StoredDocument> MemoryAtomicDocumentStore::read(const std::string& resourceUri) const {
9 auto found = documents_.find(resourceUri);
10 if (found == documents_.end())
11 return eve::editing::failed<StoredDocument>(EditorStatus::NotFound, RuleId("editor.document.store-not-found"),
12 "Document resource is not persisted: " + resourceUri);
13 return eve::editing::applied<StoredDocument>(found->second);
14}
15
17 Revision expectedRevision,
18 const std::string& expectedContentHash,
19 const EditorValue& content) {
20 auto found = documents_.find(resourceUri);
21 const Revision current = found == documents_.end() ? 0 : found->second.revision;
22 const std::string currentHash = found == documents_.end() ? std::string{} : found->second.contentHash;
23 if (current != expectedRevision || currentHash != expectedContentHash)
24 return eve::editing::failed<StoredDocument>(EditorStatus::Conflict, RuleId("editor.document.disk-conflict"),
25 "Document store revision changed before save");
27 stored.content = content;
28 stored.revision = current + 1;
29 stored.contentHash = editorValueContentHash(content);
30 documents_.insert_or_assign(resourceUri, stored);
31 return eve::editing::applied<StoredDocument>(std::move(stored));
32}
33
35 EditorValue initialContent) {
36 if (!store_ || key.asset.empty() || resourceUri.empty())
37 return error(EditorStatus::Rejected, "editor.document.invalid-open",
38 "Document store, asset identity and resource URI are required");
39 DocumentId id("document:" + key.asset.value());
40 if (open_.contains(id)) return eve::editing::applied<DocumentSnapshot>(open_.at(id).snapshot);
41
42 OpenDocument document;
43 document.snapshot.id = id;
44 document.snapshot.key = std::move(key);
45 document.snapshot.title = std::move(title);
46 document.snapshot.resourceUri = std::move(resourceUri);
47 document.snapshot.state = DocumentState::Ready;
48 Result<StoredDocument> stored = store_->read(document.snapshot.resourceUri);
49 if (stored.ok()) {
50 document.content = stored.value().content;
51 document.snapshot.revision.disk = stored.value().revision;
52 document.snapshot.revision.edit = stored.value().revision;
53 document.snapshot.revision.saved = stored.value().revision;
54 document.snapshot.diskContentHash = stored.value().contentHash;
55 } else if (stored.code() == EditorStatus::NotFound) {
56 document.content = std::move(initialContent);
57 } else {
58 return error(stored.code(), "editor.document.store-read", "Document store could not be read");
59 }
60 DocumentSnapshot snapshot = document.snapshot;
61 open_.emplace(id, std::move(document));
62 return eve::editing::applied<DocumentSnapshot>(std::move(snapshot));
63}
64
66 std::optional<Revision> expectedRevision) {
67 auto found = open_.find(document);
68 if (found == open_.end()) return error(EditorStatus::NotFound, "editor.document.not-open", "Document is not open");
69 if (expectedRevision && *expectedRevision != found->second.snapshot.revision.edit)
70 return error(EditorStatus::Conflict, "editor.document.edit-conflict",
71 "Document edit revision changed before mutation");
72 found->second.content = std::move(content);
73 ++found->second.snapshot.revision.edit;
74 found->second.snapshot.state = DocumentState::Ready;
75 return eve::editing::applied<DocumentSnapshot>(found->second.snapshot);
76}
77
79 auto found = open_.find(document);
80 if (found == open_.end())
81 return eve::editing::failed<SaveTicket>(EditorStatus::NotFound, RuleId("editor.document.not-open"),
82 "Document is not open");
83 SaveTicket ticket;
84 ticket.id = StableId(document.value() + ".save." + std::to_string(++ticketSequence_));
85 ticket.document = document;
86 ticket.capturedEditRevision = found->second.snapshot.revision.edit;
87 ticket.expectedDiskRevision = found->second.snapshot.revision.disk;
88 ticket.expectedContentHash = found->second.snapshot.diskContentHash;
89 pendingSaves_.emplace(ticket.id, PendingSave{ticket, found->second.content});
90 return eve::editing::applied<SaveTicket>(std::move(ticket));
91}
92
94 auto pending = pendingSaves_.find(ticket.id);
95 auto document = open_.find(ticket.document);
96 if (pending == pendingSaves_.end() || document == open_.end())
97 return error(EditorStatus::NotFound, "editor.document.save-ticket-not-found",
98 "Save ticket or document is no longer available");
99 document->second.snapshot.state = DocumentState::Saving;
101 store_->compareAndSwap(document->second.snapshot.resourceUri, ticket.expectedDiskRevision,
102 ticket.expectedContentHash, pending->second.content);
103 pendingSaves_.erase(pending);
104 if (!stored.ok()) {
105 document->second.snapshot.state =
106 stored.code() == EditorStatus::Conflict ? DocumentState::Conflict : DocumentState::Failed;
107 document->second.snapshot.diagnostics = stored.diagnostics();
109 }
110 document->second.snapshot.revision.disk = stored.value().revision;
111 document->second.snapshot.revision.saved = ticket.capturedEditRevision;
112 document->second.snapshot.diskContentHash = stored.value().contentHash;
113 document->second.snapshot.state = DocumentState::Ready;
114 document->second.snapshot.diagnostics.clear();
115 return eve::editing::applied<DocumentSnapshot>(document->second.snapshot);
116}
117
119 auto found = open_.find(document);
120 if (found == open_.end())
121 return eve::editing::failed<EditorValue>(EditorStatus::NotFound, RuleId("editor.document.not-open"),
122 "Document is not open");
123 return eve::editing::applied<EditorValue>(found->second.content);
124}
125
127 auto found = open_.find(document);
128 if (found == open_.end()) return error(EditorStatus::NotFound, "editor.document.not-open", "Document is not open");
129 return eve::editing::applied<DocumentSnapshot>(found->second.snapshot);
130}
131
132std::vector<DocumentSnapshot> DocumentService::documents() const {
133 std::vector<DocumentSnapshot> result;
134 result.reserve(open_.size());
135 for (const auto& [id, document] : open_) {
136 (void)id;
137 result.push_back(document.snapshot);
138 }
139 std::sort(result.begin(), result.end(),
140 [](const DocumentSnapshot& left, const DocumentSnapshot& right) { return left.id < right.id; });
141 return result;
142}
143
147
149 ContentValidator validator) {
150 auto found = open_.find(document);
151 if (found == open_.end()) return error(EditorStatus::NotFound, "editor.document.not-open", "Document is not open");
152
153 Result<StoredDocument> stored = store_->read(found->second.snapshot.resourceUri);
154 if (!stored.ok()) {
155 const bool expectedMissing = stored.code() == EditorStatus::NotFound &&
156 found->second.snapshot.revision.disk == 0 &&
157 found->second.snapshot.diskContentHash.empty();
158 if (expectedMissing) return eve::editing::applied<DocumentSnapshot>(found->second.snapshot);
159 found->second.snapshot.state = stored.code() == EditorStatus::NotFound ||
160 stored.code() == EditorStatus::Conflict
163 found->second.snapshot.diagnostics = stored.diagnostics();
164 if (found->second.snapshot.diagnostics.empty())
165 found->second.snapshot.diagnostics.push_back(eve::editing::ruleDiagnostic(
166 eve::DiagnosticCode::NotFound, RuleId("editor.document.external-missing"),
167 DiagnosticSeverity::Error, "Persisted document was removed externally"));
168 const EditorStatus status = found->second.snapshot.state == DocumentState::Conflict
169 ? EditorStatus::Conflict
170 : stored.code();
171 return Result<DocumentSnapshot>::failure(eve::Status(status, found->second.snapshot.diagnostics));
172 }
173
174 const bool changed = stored.value().revision != found->second.snapshot.revision.disk ||
175 stored.value().contentHash != found->second.snapshot.diskContentHash;
176 if (!changed) {
177 if (found->second.snapshot.state == DocumentState::Conflict) {
178 found->second.snapshot.state = DocumentState::Ready;
179 found->second.snapshot.diagnostics.clear();
180 }
181 return eve::editing::applied<DocumentSnapshot>(found->second.snapshot);
182 }
183 if (found->second.snapshot.dirty()) {
184 found->second.snapshot.state = DocumentState::Conflict;
185 found->second.snapshot.diagnostics = {eve::editing::ruleDiagnostic(
186 eve::DiagnosticCode::Conflict, RuleId("editor.document.external-conflict"), DiagnosticSeverity::Error,
187 "Document changed on disk while the session has unsaved edits")};
189 eve::Status(EditorStatus::Conflict, found->second.snapshot.diagnostics));
190 }
191
192 if (validator) {
193 auto validated = validator(stored.value().content);
194 if (!validated.ok()) {
195 found->second.snapshot.state = DocumentState::Conflict;
196 found->second.snapshot.diagnostics = validated.diagnostics();
197 if (found->second.snapshot.diagnostics.empty())
198 found->second.snapshot.diagnostics.push_back(eve::editing::ruleDiagnostic(
199 eve::DiagnosticCode::InvalidArgument, RuleId("editor.document.external-invalid"),
200 DiagnosticSeverity::Error, "External document content failed domain validation"));
202 eve::Status(EditorStatus::Conflict, found->second.snapshot.diagnostics));
203 }
204 }
205
206 found->second.content = stored.value().content;
207 const Revision synchronizedEdit =
208 std::max(found->second.snapshot.revision.edit + 1, stored.value().revision);
209 found->second.snapshot.revision.edit = synchronizedEdit;
210 found->second.snapshot.revision.saved = synchronizedEdit;
211 found->second.snapshot.revision.disk = stored.value().revision;
212 found->second.snapshot.diskContentHash = stored.value().contentHash;
213 found->second.snapshot.state = DocumentState::Ready;
214 found->second.snapshot.diagnostics.clear();
215 return eve::editing::applied<DocumentSnapshot>(found->second.snapshot);
216}
217
219 if (!open_.erase(document))
220 return eve::editing::failed<void>(EditorStatus::NotFound, RuleId("editor.document.not-open"),
221 "Document is not open");
222 std::erase_if(pendingSaves_, [&](const auto& entry) { return entry.second.ticket.document == document; });
223 return eve::editing::applied<void>();
224}
225
226Result<DocumentSnapshot> DocumentService::error(EditorStatus status, const char* rule, std::string message) {
227 return eve::editing::failed<DocumentSnapshot>(status, RuleId(rule), std::move(message));
228}
229
230} // namespace eve::editor
std::vector< QuestEvent > pending
std::string title
std::vector< std::uint8_t > stored
Definition Evpack.cpp:171
std::string message
std::uint32_t key
wgpu::PopErrorScopeStatus status
HexVec3 left
HexVec3 right
std::string error
Definition Package.cpp:60
std::string id
Definition PlayHost.cpp:108
bool found
double current
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
UUID-backed identifier adapter for legacy textual boundaries.
Definition Identity.h:314
const std::string & value() const noexcept
Returns the exact legacy or canonical compatibility spelling.
Definition Identity.h:362
Deterministic owning value tree shared by authoring hosts.
std::function< Result< void >(const EditorValue &)> ContentValidator
Synchronous content validator borrowed only for one external reconciliation call.
Result< DocumentSnapshot > reconcileExternal(const DocumentId &document)
Reconcile one open document with the latest store value. Clean documents adopt external content; dirt...
Result< DocumentSnapshot > edit(const DocumentId &document, EditorValue content, std::optional< Revision > expectedRevision=std::nullopt)
Replace the working content using an optional expected edit revision.
Result< DocumentSnapshot > snapshot(const DocumentId &document) const
Return one metadata snapshot.
Result< DocumentSnapshot > executeSave(const SaveTicket &ticket)
Execute a captured save ticket without reading newer working content.
std::vector< DocumentSnapshot > documents() const
Return all open documents in stable id order.
Result< EditorValue > content(const DocumentId &document) const
Return the current working content snapshot.
Result< SaveTicket > requestSave(const DocumentId &document)
Capture an exact content/revision pair for a later CAS save.
Result< void > close(const DocumentId &document)
Close and discard in-memory state; persisted content is unchanged.
Result< DocumentSnapshot > open(DocumentKey key, std::string title, std::string resourceUri, EditorValue initialContent={})
Open an asset-backed document, loading existing persisted content when present.
virtual Result< StoredDocument > read(const std::string &resourceUri) const =0
Read the latest value for a resource URI.
virtual Result< StoredDocument > compareAndSwap(const std::string &resourceUri, Revision expectedRevision, const std::string &expectedContentHash, const EditorValue &content)=0
Replace a value only when the expected disk revision still matches.
Result< StoredDocument > read(const std::string &resourceUri) const override
Reads .
Result< StoredDocument > compareAndSwap(const std::string &resourceUri, Revision expectedRevision, const std::string &expectedContentHash, const EditorValue &content) override
Compare and swap.
Diagnostic ruleDiagnostic(eve::DiagnosticCode code, RuleId rule, DiagnosticSeverity severity, std::string message)
Build a common diagnostic carrying an open editing rule identity.
eve::editing::StableId StableId
Definition EditorIds.h:36
eve::editing::RuleId RuleId
Definition EditorIds.h:32
eve::editing::Revision Revision
StatusCode
Stable outcome category for an operation.
Definition Status.h:27
Stable document identity; paths are deliberately excluded.
Immutable document metadata snapshot.
Immutable save request capturing one exact edit revision.
Value snapshot returned by an atomic document store.