载入中...
搜索中...
未找到
ConditionCodec.cpp
浏览该文件的文档.
2
3#include "common/Diagnostic.h"
4
5#include <optional>
6#include <set>
7#include <string>
8#include <utility>
9
10namespace eve::emergence {
11namespace {
12
13const eve::Value* field(const eve::Value::Object& object, std::string_view name) {
14 const auto it = object.find(std::string(name));
15 return it == object.end() ? nullptr : &it->second;
16}
17
18bool exactFields(const eve::Value::Object& object, std::initializer_list<std::string_view> allowed) {
19 const std::set<std::string_view> fields(allowed.begin(), allowed.end());
20 for (const auto& [name, unused] : object) {
21 (void)unused;
22 if (!fields.contains(name)) return false;
23 }
24 return true;
25}
26
27eve::Result<std::string> requiredString(const eve::Value::Object& object, std::string_view name) {
28 const auto* value = field(object, name);
29 const auto* text = value == nullptr ? nullptr : value->getIf<std::string>();
30 if (text == nullptr || text->empty())
32 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "condition field must be a non-empty string",
33 std::string(name), {}, "emergence.condition_codec"));
35}
36
38 const auto* text = value.getIf<std::string>();
39 if (text == nullptr)
41 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "condition kind must be a string", "kind", {},
42 "emergence.condition_codec"));
44 static const std::pair<std::string_view, K> names[] = {
45 {"all", K::All},
46 {"any", K::Any},
47 {"not", K::Not},
48 {"compare", K::Compare},
49 {"has_tag", K::HasTag},
50 {"has_attribute", K::HasAttribute},
51 {"has_resource", K::HasResource},
52 {"state_equals", K::StateEquals},
53 {"authority_check", K::AuthorityCheck},
54 {"policy_call", K::PolicyCall},
55 };
56 for (const auto& [name, kind] : names)
57 if (*text == name) return eve::Result<K>::success(kind);
59 eve::DiagnosticCode::InvalidArgument, "unknown condition kind", "kind", {}, "emergence.condition_codec"));
60}
61
63 const auto* text = value.getIf<std::string>();
64 if (text == nullptr)
66 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "condition operator must be a string",
67 "operator", {}, "emergence.condition_codec"));
69 static const std::pair<std::string_view, O> names[] = {
70 {"eq", O::Equal}, {"ne", O::NotEqual}, {"lt", O::Less},
71 {"le", O::LessEqual}, {"gt", O::Greater}, {"ge", O::GreaterEqual},
72 };
73 for (const auto& [name, op] : names)
74 if (*text == name) return eve::Result<O>::success(op);
76 "unknown condition operator", "operator", {},
77 "emergence.condition_codec"));
78}
79
81 const auto* text = value.getIf<std::string>();
82 if (text == nullptr)
85 "scriptDeclaration.determinism", {}, "emergence.condition_codec"));
87 static const std::pair<std::string_view, D> names[] = {
88 {"bit_exact", D::BitExact},
89 {"tick_deterministic", D::TickDeterministic},
90 {"tolerance_bounded", D::ToleranceBounded},
91 {"explicitly_nondeterministic", D::ExplicitlyNondeterministic},
92 };
93 for (const auto& [name, level] : names)
94 if (*text == name) return eve::Result<D>::success(level);
96 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "unknown condition determinism level",
97 "scriptDeclaration.determinism", {}, "emergence.condition_codec"));
98}
99
101
103 const auto* value = field(object, "children");
104 const auto* array = value == nullptr ? nullptr : value->getIf<eve::Value::Array>();
105 if (array == nullptr)
107 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "condition children must be an array",
108 "children", {}, "emergence.condition_codec"));
109 std::vector<decision::Condition> result;
110 result.reserve(array->size());
111 for (const auto& child : *array) {
112 auto decoded = decodeNode(child);
113 if (!decoded) return eve::Result<std::vector<decision::Condition>>::failure(decoded.status());
114 result.push_back(std::move(decoded).takeValue());
115 }
116 return eve::Result<std::vector<decision::Condition>>::success(std::move(result));
117}
118
120 const auto* object = value.getIf<eve::Value::Object>();
121 if (object == nullptr)
123 "condition node must be an object", {},
124 {}, "emergence.condition_codec"));
125 const auto* kindValue = field(*object, "kind");
126 if (kindValue == nullptr)
128 "condition node requires kind", "kind",
129 {}, "emergence.condition_codec"));
130 auto kind = parseKind(*kindValue);
132 const auto parsedKind = kind.value();
133 using K = decision::ConditionKind;
134 if (parsedKind == K::All || parsedKind == K::Any || parsedKind == K::Not) {
135 if (!exactFields(*object, {"kind", "children"}))
137 eve::DiagnosticCode::InvalidArgument, "logical condition contains an unknown field", {}, {},
138 "emergence.condition_codec"));
139 auto children = decodeChildren(*object);
141 auto values = std::move(children).takeValue();
142 if (parsedKind == K::Not && values.size() != 1)
144 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "not condition requires one child",
145 "children", {}, "emergence.condition_codec"));
146 if (parsedKind == K::All)
148 if (parsedKind == K::Any)
151 }
152 if (parsedKind == K::Compare) {
153 if (!exactFields(*object, {"kind", "key", "operator", "expected"}))
155 eve::DiagnosticCode::InvalidArgument, "compare condition contains an unknown field", {}, {},
156 "emergence.condition_codec"));
157 auto key = requiredString(*object, "key");
159 const auto* opValue = field(*object, "operator");
160 if (opValue == nullptr)
162 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "compare requires operator", "operator",
163 {}, "emergence.condition_codec"));
164 auto op = parseOperator(*opValue);
165 if (!op) return eve::Result<decision::Condition>::failure(op.status());
166 const auto* expected = field(*object, "expected");
167 if (expected == nullptr)
169 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "compare requires expected", "expected",
170 {}, "emergence.condition_codec"));
172 decision::Condition::compare(std::move(key).takeValue(), op.value(), *expected));
173 }
174 if (parsedKind == K::HasTag || parsedKind == K::HasAttribute || parsedKind == K::HasResource ||
175 parsedKind == K::AuthorityCheck) {
176 if (!exactFields(*object, {"kind", "key"}))
178 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "leaf condition contains an unknown field",
179 {}, {}, "emergence.condition_codec"));
180 auto key = requiredString(*object, "key");
182 auto name = std::move(key).takeValue();
183 if (parsedKind == K::HasTag)
185 if (parsedKind == K::HasAttribute)
187 if (parsedKind == K::HasResource)
190 }
191 if (parsedKind == K::StateEquals) {
192 if (!exactFields(*object, {"kind", "key", "expected"}))
194 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "state_equals contains an unknown field",
195 {}, {}, "emergence.condition_codec"));
196 auto key = requiredString(*object, "key");
198 const auto* expected = field(*object, "expected");
199 if (expected == nullptr)
201 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "state_equals requires expected",
202 "expected", {}, "emergence.condition_codec"));
204 decision::Condition::stateEquals(std::move(key).takeValue(), *expected));
205 }
206 if (parsedKind == K::PolicyCall) {
207 if (!exactFields(*object, {"kind", "key", "arguments", "scriptDeclaration"}))
209 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "policy_call contains an unknown field",
210 {}, {}, "emergence.condition_codec"));
211 auto key = requiredString(*object, "key");
213 eve::Value arguments = eve::Value::Object{};
214 if (const auto* args = field(*object, "arguments")) arguments = *args;
215 std::optional<decision::ScriptConditionDeclaration> declaration;
216 if (const auto* declValue = field(*object, "scriptDeclaration")) {
217 const auto* declObject = declValue->getIf<eve::Value::Object>();
218 if (declObject == nullptr)
220 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "scriptDeclaration must be an object",
221 "scriptDeclaration", {}, "emergence.condition_codec"));
223 auto name = requiredString(*declObject, "name");
225 parsed.name = std::move(name).takeValue();
226 if (const auto* deps = field(*declObject, "dependencies")) {
227 const auto* array = deps->getIf<eve::Value::Array>();
228 if (array == nullptr)
230 eve::Diagnostic::error(eve::DiagnosticCode::InvalidArgument, "dependencies must be an array",
231 "scriptDeclaration.dependencies", {}, "emergence.condition_codec"));
232 for (const auto& item : *array) {
233 const auto* text = item.getIf<std::string>();
234 if (text == nullptr || text->empty())
236 eve::DiagnosticCode::InvalidArgument, "dependency must be a non-empty string",
237 "scriptDeclaration.dependencies", {}, "emergence.condition_codec"));
238 parsed.dependencies.push_back(*text);
239 }
240 }
241 if (const auto* det = field(*declObject, "determinism")) {
242 auto level = parseDeterminism(*det);
244 parsed.determinism = level.value();
245 }
246 declaration = std::move(parsed);
247 }
249 decision::Condition::policyCall(std::move(key).takeValue(), std::move(arguments), std::move(declaration)));
250 }
252 eve::DiagnosticCode::InvalidArgument, "unsupported condition kind", "kind", {}, "emergence.condition_codec"));
253}
254
255} // namespace
256
258
259} // namespace eve::emergence
double value
Value/JSON codec for decision::Condition trees owned by emergence rules.
Stable, structured diagnostics shared by engine modules.
std::map< std::string, Var > values
std::uint32_t key
std::string text
TokenKind kind
std::string name
int level
std::vector< std::string > fields
Definition PlayHost.cpp:111
Json object
int children
Definition TreeMesh.cpp:295
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
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
const T * getIf() const noexcept
Return a typed pointer, or nullptr when the kind differs.
Definition Value.h:180
static Condition hasAttribute(std::string attribute)
Require an attribute to exist.
static Condition all(std::vector< Condition > children)
Construct a conjunction of child conditions.
static Condition not_(Condition child)
Construct a logical negation of one child condition.
static Condition hasTag(std::string tag)
Require a tag to be present.
static Condition stateEquals(std::string key, Value expected)
Require a named state value to equal the expected value.
static Condition policyCall(std::string name, Value arguments=Value::Object{}, std::optional< ScriptConditionDeclaration > scriptDeclaration=std::nullopt)
Call a named read-only policy and use its ConditionResult.
static Condition compare(std::string key, CompareOperator op, Value expected)
Compare a context value against an expected scalar value.
static Condition authorityCheck(std::string scope)
Require read-only authority for a scope.
static Condition any(std::vector< Condition > children)
Construct a disjunction of child conditions.
static Condition hasResource(std::string resource)
Require a resource to exist.
CompareOperator
Scalar comparison used by a Compare node.
Definition Condition.h:36
ConditionKind
Node kind in the side-effect-free condition AST.
Definition Condition.h:22
DeterminismLevel
Determinism contract declared by a script-backed condition policy.
Definition Condition.h:46
eve::Result< decision::Condition > decodeCondition(const eve::Value &value)
Decode one validated condition tree from an owning Value.
Metadata a script policy must declare before it is used by a condition.
Definition Condition.h:64
std::string name
Stable script policy name.
Definition Condition.h:66
std::vector< std::string > dependencies
Logical state/query names read by the policy.
Definition Condition.h:68
DeterminismLevel determinism
Replay/network determinism contract.
Definition Condition.h:70