载入中...
搜索中...
未找到
SchemaDocumentation.cpp
浏览该文件的文档.
2
3#include <sstream>
4#include <string_view>
5#include <utility>
6
7namespace eve::schema {
8namespace {
9
10eve::Value nodeContract(const SchemaNode& node);
11
12eve::Value fieldContract(const FieldDefinition& field) {
13 eve::Value::Object result;
14 result.emplace("name", eve::Value(field.name));
15 result.emplace("required", eve::Value(field.required));
16 if (!field.title.empty()) result.emplace("title", eve::Value(field.title));
17 if (!field.description.empty()) result.emplace("description", eve::Value(field.description));
18 if (!field.reference.empty()) result.emplace("displayReference", eve::Value(field.reference));
19 if (!field.defaultJson.empty()) result.emplace("defaultJson", eve::Value(field.defaultJson));
20 result.emplace("schema", nodeContract(field));
21 return eve::Value(std::move(result));
22}
23
24eve::Value definitionContract(const SchemaDefinition& definition) {
25 eve::Value::Object result;
26 result.emplace("additionalProperties", eve::Value(definition.additionalProperties));
28 fields.reserve(definition.fields.size());
29 for (const auto& field : definition.fields) fields.push_back(fieldContract(field));
30 result.emplace("fields", eve::Value(std::move(fields)));
31 return eve::Value(std::move(result));
32}
33
34eve::Value nodeContract(const SchemaNode& node) {
35 eve::Value::Object result;
36 result.emplace("type", eve::Value(valueTypeName(node.type)));
37 if (node.elementType != ValueType::Any) result.emplace("elementType", eve::Value(valueTypeName(node.elementType)));
38 if (!node.ref.empty()) {
39 result.emplace("ref", eve::Value(node.ref));
40 if (node.refVersion > 0) result.emplace("refVersion", eve::Value(node.refVersion));
41 }
42 if (node.objectSchema) result.emplace("object", definitionContract(*node.objectSchema));
43 if (node.itemSchema) result.emplace("items", nodeContract(*node.itemSchema));
44 if (!node.variants.empty()) {
45 eve::Value::Array variants;
46 variants.reserve(node.variants.size());
47 for (const auto& variant : node.variants) variants.push_back(nodeContract(variant));
48 result.emplace("union", eve::Value(std::move(variants)));
49 }
50 if (!node.discriminator.empty()) result.emplace("discriminator", eve::Value(node.discriminator));
51 if (!node.discriminatorMapping.empty()) {
52 eve::Value::Object mapping;
53 for (const auto& [value, target] : node.discriminatorMapping) mapping.emplace(value, eve::Value(target));
54 result.emplace("discriminatorMapping", eve::Value(std::move(mapping)));
55 }
56 if (node.minimum) result.emplace("minimum", eve::Value(*node.minimum));
57 if (node.maximum) result.emplace("maximum", eve::Value(*node.maximum));
58 if (node.minLength) result.emplace("minLength", eve::Value(*node.minLength));
59 if (node.maxLength) result.emplace("maxLength", eve::Value(*node.maxLength));
60 if (node.minItems) result.emplace("minItems", eve::Value(*node.minItems));
61 if (node.maxItems) result.emplace("maxItems", eve::Value(*node.maxItems));
62 if (!node.enumValues.empty()) {
64 for (const auto& value : node.enumValues) values.emplace_back(value);
65 result.emplace("enum", eve::Value(std::move(values)));
66 }
67 return eve::Value(std::move(result));
68}
69
70std::string markdownEscape(std::string_view text) {
71 std::string result;
72 result.reserve(text.size());
73 for (const char character : text) {
74 if (character == '|')
75 result += "\\|";
76 else if (character == '\n')
77 result += ' ';
78 else
79 result += character;
80 }
81 return result;
82}
83
84void appendFieldDocumentation(std::ostringstream& output, const FieldDefinition& field, const std::string& parentPath,
85 int depth) {
86 const std::string path = parentPath + "/" + field.name;
87 output << std::string(static_cast<size_t>(depth), ' ') << "- `" << markdownEscape(path) << "` — `"
88 << valueTypeName(field.type) << "`, " << (field.required ? "required" : "optional");
89 if (!field.ref.empty()) {
90 output << ", ref=`" << markdownEscape(field.ref);
91 if (field.refVersion > 0) output << "@" << field.refVersion;
92 output << '`';
93 }
94 if (!field.discriminator.empty()) output << ", discriminator=`" << markdownEscape(field.discriminator) << '`';
95 if (!field.description.empty()) output << " — " << markdownEscape(field.description);
96 output << '\n';
97 if (field.objectSchema) {
98 for (const auto& nested : field.objectSchema->fields) appendFieldDocumentation(output, nested, path, depth + 2);
99 }
100 if (field.itemSchema && field.itemSchema->objectSchema) {
101 output << std::string(static_cast<size_t>(depth + 2), ' ') << "- array item shape:\n";
102 for (const auto& nested : field.itemSchema->objectSchema->fields)
103 appendFieldDocumentation(output, nested, path + "[]", depth + 4);
104 }
105}
106
107} // namespace
108
109eve::Result<std::string> SchemaRegistry::generateDocumentation(const std::string& schemaId, int schemaVersion) {
110 const auto* schema = resolve(schemaId, schemaVersion);
111 if (!schema)
113 eve::DiagnosticCode::UnknownVersion, "schema version is not registered", "schemaVersion"));
114
115 std::ostringstream output;
116 output << "# " << markdownEscape(schema->id) << " (v" << schema->version << ")\n\n";
117 output << "Language: Eve Schema v1\n\n";
118 output << (schema->additionalProperties ? "Unknown object fields: allow\n\n" : "Unknown object fields: reject\n\n");
119 if (!schema->title.empty()) output << "Title: " << markdownEscape(schema->title) << "\n\n";
120 if (!schema->description.empty()) output << markdownEscape(schema->description) << "\n\n";
121 output << "## Fields\n\n";
122 for (const auto& field : schema->fields) appendFieldDocumentation(output, field, {}, 0);
123 if (schema->fields.empty()) output << "(none)\n";
124 output << "\nGenerated contract: `SchemaRegistry::generateBindingContract`\n";
126}
127
128eve::Result<std::string> SchemaRegistry::generateBindingContract(const std::string& schemaId, int schemaVersion) {
129 const auto* schema = resolve(schemaId, schemaVersion);
130 if (!schema)
132 eve::DiagnosticCode::UnknownVersion, "schema version is not registered", "schemaVersion"));
133 eve::Value::Object result;
134 result.emplace("language", eve::Value("eve-schema-v1"));
135 result.emplace("schemaId", eve::Value(schema->id));
136 result.emplace("schemaVersion", eve::Value(schema->version));
137 result.emplace("unknownFields", eve::Value(schema->additionalProperties ? "allow" : "reject"));
138 result.emplace("root", definitionContract(*schema));
139 auto encoded = eve::Value(std::move(result)).toJson();
140 if (!encoded.ok()) return eve::Result<std::string>::failure(encoded.status());
141 return eve::Result<std::string>::success(std::move(encoded).takeValue());
142}
143
144} // namespace eve::schema
LogicalId target
double value
std::string output
std::string variant
std::map< std::string, Var > values
std::string text
std::string path
Definition PlayHost.cpp:110
std::vector< std::string > fields
Definition PlayHost.cpp:111
const RoadNode * node
std::uint32_t depth
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
Result< std::string > toJson() const
Serialize this value as deterministic compact JSON.
Definition Value.cpp:67
std::map< std::string, Value > Object
Definition Value.h:34
std::vector< Value > Array
Definition Value.h:33
Type type() const
Return the active value type.
static eve::Result< std::string > generateDocumentation(const std::string &schemaId, int schemaVersion)
Produce stable Markdown for one exact registered schema.
static const SchemaDefinition * resolve(const std::string &schemaId, int schemaVersion)
Resolves one exact (schemaId, schemaVersion) pair, or nullptr.
static eve::Result< std::string > generateBindingContract(const std::string &schemaId, int schemaVersion)
Produce a stable JSON binding contract for one exact schema.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
const EditorValue * field(const EditorValue &value, const char *name)
const char * valueTypeName(ValueType type)
Returns the stable lowercase name of a schema value type.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16