#include "map/ArtifactProvider.h" #include "common/Capability.h" #include #include #include #include #include #include #include namespace eve::map { const eve::Value* member(const eve::Value::Object& object, const char* name) noexcept { const auto found = object.find(name); return found == object.end() ? nullptr : &found->second; } bool readString(const eve::Value::Object& object, const char* name, std::string& output) { const auto* value = member(object, name); const auto* text = value ? value->getIf() : nullptr; if (!text || text->empty()) return false; output = *text; return true; } bool readU64(const eve::Value::Object& object, const char* name, std::uint64_t& output) { std::string text; if (!readString(object, name, text)) return false; const auto [end, error] = std::from_chars(text.data(), text.data() + text.size(), output); return error == std::errc{} && end == text.data() + text.size(); } bool readI64(const eve::Value::Object& object, const char* name, std::int64_t& output) { const auto* value = member(object, name); if (!value) return false; if (const auto* integer = value->getIf()) { output = *integer; return true; } return false; } bool hasSupportedVersion(const eve::Value::Object& object) noexcept { const auto* encoded = member(object, "version"); const auto* version = encoded ? encoded->getIf() : nullptr; return version && *version == 1; } class MapArtifactStage final : public eve::artifact::PreparedPublication { public: MapArtifactStage(MapArtifactProvider& owner, MapArtifactRecord record) : owner_(&owner), record_(std::move(record)) {} void commit() noexcept override { if (!owner_) return; owner_->commit(std::move(record_)); owner_ = nullptr; } void rollback() noexcept override { owner_ = nullptr; record_ = {}; } private: MapArtifactProvider* owner_ = nullptr; MapArtifactRecord record_; }; eve::Result> MapArtifactProvider::prepare( const eve::artifact::PublicationView& publication) { if (failPrepare_) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact prepare failure was injected")); if (publication.id.isNil() || publication.buildKey.empty()) return eve::Result>::failure(eve::Diagnostic::error( eve::DiagnosticCode::InvalidArgument, "map publication requires identity and build key")); if (find(publication.id) != nullptr) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "map artifact identity is already published")); const eve::artifact::PartView* topology = nullptr; for (const auto& part : publication.parts) { if (part.kind == eve::artifact::PartKind::Grid && (part.role == "topology" || topology == nullptr)) topology = ∂ } if (!topology || topology->width <= 0 || topology->height <= 0 || topology->cells.size() != static_cast(topology->width) * static_cast(topology->height)) return eve::Result>::failure(eve::Diagnostic::error( eve::DiagnosticCode::PreconditionViolation, "map publication requires a complete grid topology")); if (nextIndex_ == MapArtifactHandle::invalidIndex) return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact handle index exhausted")); MapArtifactRecord record; record.id = publication.id; record.buildKey = std::string(publication.buildKey); record.handle = MapArtifactHandle(nextIndex_, 1); record.width = topology->width; record.height = topology->height; try { record.cells.assign(topology->cells.begin(), topology->cells.end()); records_.reserve(records_.size() + 1); return eve::Result>::success( std::make_unique(*this, std::move(record))); } catch (...) { return eve::Result>::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact prepare allocation failed")); } } void MapArtifactProvider::commit(MapArtifactRecord record) noexcept { records_.push_back(std::move(record)); ++nextIndex_; } const MapArtifactRecord* MapArtifactProvider::find(eve::PersistentId id) const noexcept { const auto found = std::find_if(records_.begin(), records_.end(), [id](const auto& record) { return record.id == id; }); return found == records_.end() ? nullptr : &*found; } std::optional MapArtifactProvider::cell(eve::PersistentId id, std::int32_t x, std::int32_t y) const noexcept { const auto* record = find(id); if (!record || x < 0 || y < 0 || x >= record->width || y >= record->height) return std::nullopt; return record ->cells[static_cast(y) * static_cast(record->width) + static_cast(x)]; } void MapArtifactProvider::clear() noexcept { records_.clear(); nextIndex_ = 0; failPrepare_ = false; } eve::Result MapArtifactProvider::snapshotState() const { try { eve::Value::Array records; records.reserve(records_.size()); for (const auto& record : records_) { eve::Value::Array cells; cells.reserve(record.cells.size()); for (const auto value : record.cells) cells.emplace_back(std::int64_t(value)); eve::Value::Object item; item.emplace("artifactId", eve::Value(record.id.format())); item.emplace("buildKey", eve::Value(record.buildKey)); item.emplace("handle", eve::Value(std::to_string(record.handle.packed()))); item.emplace("width", eve::Value(std::int64_t(record.width))); item.emplace("height", eve::Value(std::int64_t(record.height))); item.emplace("cells", eve::Value(std::move(cells))); records.emplace_back(std::move(item)); } eve::Value::Object state; state.emplace("provider", eve::Value("map.cpu-registry")); state.emplace("version", eve::Value(std::int64_t(1))); state.emplace("records", eve::Value(std::move(records))); return eve::Result::success(eve::Value(std::move(state))); } catch (...) { return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact snapshot allocation failed")); } } eve::Result MapArtifactProvider::restoreState(const eve::Value& state) { if (!records_.empty()) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::Conflict, "map artifact restore requires an empty registry")); const auto* object = state.getIf(); const auto* provider = object ? member(*object, "provider") : nullptr; const auto* providerName = provider ? provider->getIf() : nullptr; const auto* encodedRecords = object ? member(*object, "records") : nullptr; const auto* records = encodedRecords ? encodedRecords->getIf() : nullptr; if (!object || !providerName || *providerName != "map.cpu-registry" || !hasSupportedVersion(*object) || !records) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map artifact provider state")); std::vector candidate; std::unordered_set identities; std::uint32_t next = 0; try { candidate.reserve(records->size()); for (const eve::Value& encoded : *records) { const auto* item = encoded.getIf(); std::string idText; std::string buildKey; std::uint64_t packed = 0; std::int64_t width = 0; std::int64_t height = 0; const auto* encodedCells = item ? member(*item, "cells") : nullptr; const auto* cells = encodedCells ? encodedCells->getIf() : nullptr; if (!item || !readString(*item, "artifactId", idText) || !readString(*item, "buildKey", buildKey) || !readU64(*item, "handle", packed) || !readI64(*item, "width", width) || !readI64(*item, "height", height) || width <= 0 || height <= 0 || !cells) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map artifact record")); const auto parsedId = eve::PersistentId::parse(idText); const auto handle = MapArtifactHandle::fromPacked(packed); if (!parsedId || parsedId->isNil() || handle.isInvalid() || width > std::numeric_limits::max() || height > std::numeric_limits::max() || !identities.emplace(*parsedId).second || static_cast(width) * static_cast(height) != cells->size()) return eve::Result::failure(eve::Diagnostic::error( eve::DiagnosticCode::ParseError, "invalid map artifact dimensions or identity")); MapArtifactRecord record; record.id = *parsedId; record.buildKey = std::move(buildKey); record.handle = handle; record.width = static_cast(width); record.height = static_cast(height); record.cells.reserve(cells->size()); for (const eve::Value& value : *cells) { const auto* cell = value.getIf(); if (!cell || *cell < 0 || *cell > std::numeric_limits::max()) return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::ParseError, "invalid map topology cell")); record.cells.push_back(static_cast(*cell)); } next = std::max(next, handle.index() + 1u); candidate.push_back(std::move(record)); } } catch (...) { return eve::Result::failure( eve::Diagnostic::error(eve::DiagnosticCode::Failed, "map artifact restore allocation failed")); } records_.swap(candidate); nextIndex_ = next; return eve::Result::success(eve::Status::success(eve::StatusCode::Applied)); } MapArtifactProvider& mapArtifactProvider() noexcept { static MapArtifactProvider provider; return provider; } void registerMapArtifactProvider() { eve::cap::provide(&mapArtifactProvider()); } } // namespace eve::map