载入中...
搜索中...
未找到
AssetDependency.cpp
浏览该文件的文档.
2
3#include <algorithm>
4#include <functional>
5#include <map>
6#include <set>
7#include <tuple>
8
9namespace eve::asset {
10namespace {
11bool requiresPresence(EvaDependencyKind kind) {
14}
15bool validFallback(const EvaDependency& dependency) {
16 if (dependency.kind != EvaDependencyKind::RuntimeOptional)
17 return dependency.fallback.empty();
18 const auto behavior = dependency.fallback.find("behavior");
19 const auto observable = dependency.fallback.find("observableCode");
20 if (behavior == dependency.fallback.end() || !behavior->second.isString() ||
21 observable == dependency.fallback.end() || !observable->second.isString() ||
22 observable->second.asString().empty())
23 return false;
24 const std::string& name = behavior->second.asString();
25 const auto asset = dependency.fallback.find("asset");
26 if (name == "use-asset")
27 return asset != dependency.fallback.end() && asset->second.isString() &&
28 AssetRef::parse(asset->second.asString()).ok();
29 return (name == "omit-feature" || name == "use-default") &&
30 asset == dependency.fallback.end();
31}
32} // namespace
33
35 const EvaManifest& manifest, std::span<const AvailableAssetDependency> available) {
36 using TypeIdentity = std::pair<std::string, SchemaVersion>;
37 std::map<PersistentId, TypeIdentity> types;
38 std::set<PersistentId> local;
39 for (const auto& asset : manifest.assets) {
40 types.emplace(asset.asset.id(), TypeIdentity{asset.type, asset.schemaVersion});
41 local.emplace(asset.asset.id());
42 }
43 for (const auto& external : available) {
44 if (external.asset.id().isNil() || external.type.empty() || external.schemaVersion.isZero())
46 DiagnosticCode::InvalidArgument, "external dependency fact is invalid", {}, {}, "asset.dependency"));
47 const auto [found, inserted] = types.emplace(
48 external.asset.id(), TypeIdentity{external.type, external.schemaVersion});
49 if (!inserted && found->second != TypeIdentity{external.type, external.schemaVersion})
51 Diagnostic::error(DiagnosticCode::TypeMismatch, "dependency resolver reports conflicting types",
52 external.asset.format(), {}, "asset.dependency"));
53 }
54 for (const auto& [name, entrypoint] : manifest.entrypoints)
55 if (!local.contains(entrypoint.id()))
56 return Result<EvaDependencyValidation>::failure(
57 Diagnostic::error(DiagnosticCode::NotFound, "entrypoint does not resolve to a local asset", name, {},
58 "asset.dependency"));
59
60 using EdgeKey = std::tuple<PersistentId, PersistentId, EvaDependencyKind, std::string>;
61 std::set<EdgeKey> edges;
62 std::map<PersistentId, std::vector<PersistentId>> graph;
63 EvaDependencyValidation result;
64 for (const auto& dependency : manifest.dependencies) {
65 if (!validFallback(dependency))
67 "dependency fallback policy is invalid",
68 dependency.path, {}, "asset.dependency"));
69 if (!local.contains(dependency.from.id()))
71 Diagnostic::error(DiagnosticCode::NotFound, "dependency source is not locally authoritative",
72 dependency.from.format(), {}, "asset.dependency"));
73 if (!edges.emplace(dependency.from.id(), dependency.to.id(), dependency.kind,
74 dependency.path).second)
76 DiagnosticCode::Conflict, "duplicate dependency edge", dependency.path, {}, "asset.dependency"));
77 const auto target = types.find(dependency.to.id());
78 if (target == types.end()) {
79 if (requiresPresence(dependency.kind))
81 Diagnostic::error(DiagnosticCode::NotFound, "required dependency is unavailable",
82 dependency.to.format(), {}, "asset.dependency"));
83 result.omittedOptionalAssets.push_back(dependency.to.id());
84 continue;
85 }
86 const std::string resolvedType = target->second.first + "/" +
87 std::to_string(target->second.second.value());
88 if (!dependency.expectedType.empty() && resolvedType != dependency.expectedType)
90 Diagnostic::error(DiagnosticCode::TypeMismatch, "dependency resolved to the wrong asset type",
91 dependency.path, {}, "asset.dependency"));
92 result.presentAssets.push_back(dependency.to.id());
93 if (dependency.kind == EvaDependencyKind::RuntimeRequired && local.contains(dependency.to.id()))
94 graph[dependency.from.id()].push_back(dependency.to.id());
95 }
96
97 enum class Mark : std::uint8_t { Visiting, Complete };
98 std::map<PersistentId, Mark> marks;
99 std::function<Result<void>(const PersistentId&)> visit = [&](const PersistentId& node) -> Result<void> {
100 const auto marked = marks.find(node);
101 if (marked != marks.end()) {
102 if (marked->second == Mark::Visiting)
104 DiagnosticCode::Conflict, "runtime-required dependency cycle detected",
105 node.format(), {}, "asset.dependency"));
106 return Result<void>::success();
107 }
108 marks.emplace(node, Mark::Visiting);
109 auto& targets = graph[node];
110 std::sort(targets.begin(), targets.end());
111 for (const auto& target : targets) {
112 auto nested = visit(target);
113 if (!nested) return nested;
114 }
115 marks[node] = Mark::Complete;
116 return Result<void>::success();
117 };
118 for (const auto& id : local) {
119 auto visited = visit(id);
120 if (!visited) return Result<EvaDependencyValidation>::failure(visited.status());
121 }
122 std::sort(result.presentAssets.begin(), result.presentAssets.end());
123 result.presentAssets.erase(std::unique(result.presentAssets.begin(), result.presentAssets.end()),
124 result.presentAssets.end());
125 std::sort(result.omittedOptionalAssets.begin(), result.omittedOptionalAssets.end());
126 result.omittedOptionalAssets.erase(
127 std::unique(result.omittedOptionalAssets.begin(), result.omittedOptionalAssets.end()),
128 result.omittedOptionalAssets.end());
129 return Result<EvaDependencyValidation>::success(std::move(result));
130}
131} // namespace eve::asset
LogicalId target
Typed dependency closure validation for `.eva`.
TokenKind kind
std::string local
std::string name
std::string error
Definition Package.cpp:60
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
std::string id
Definition PlayHost.cpp:108
const RoadNode * node
bool found
std::vector< int > edges
static Result< AssetRef > parse(std::string_view text)
Parse an asset://<canonical UUID> reference.
A structured explanation of a failed, degraded, or noteworthy result.
Definition Diagnostic.h:94
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
EvaDependencyKind
Dependency role recorded by a source asset archive.
Definition EvaManifest.h:21
Result< EvaDependencyValidation > validateEvaDependencies(const EvaManifest &manifest, std::span< const AvailableAssetDependency > available)
Validate local/external references, expected types and required-edge acyclicity.
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
@ TypeMismatch
A stable reference resolved to a different canonical domain type.
StrongUuid< detail::PersistentIdTag > PersistentId
Stable instance identity for persistence, networking and process boundaries.
Definition Identity.h:254
Validated, owning .eva manifest.
Definition EvaManifest.h:62
std::vector< EvaAssetEntry > assets
Definition EvaManifest.h:69