Stabilize XCUI schema compiler and phase 2 checkpoint

This commit is contained in:
2026-04-05 05:16:40 +08:00
parent ade5be31d6
commit acdbcdd35b
2 changed files with 831 additions and 14 deletions

View File

@@ -24,6 +24,9 @@ Containers::String ToContainersString(const std::string& value) {
}
std::string ToStdString(const Containers::String& value) {
if (value.Empty() || value.CStr() == nullptr) {
return std::string();
}
return std::string(value.CStr());
}
@@ -151,6 +154,199 @@ bool ReadNode(std::ifstream& stream, UIDocumentNode& outNode) {
return true;
}
struct UIDocumentArtifactSchemaDefinitionHeader {
Core::uint32 elementCount = 0;
Core::uint32 valid = 0;
};
struct UIDocumentArtifactSchemaElementHeader {
Core::uint32 attributeCount = 0;
Core::uint32 childCount = 0;
Core::uint32 line = 1;
Core::uint32 column = 1;
Core::uint32 allowUnknownAttributes = 0;
Core::uint32 allowUnknownChildren = 0;
};
struct UIDocumentArtifactSchemaAttributeHeader {
Core::uint32 valueType = 0;
Core::uint32 documentKind = 0;
Core::uint32 allowedValueCount = 0;
Core::uint32 line = 1;
Core::uint32 column = 1;
Core::uint32 required = 0;
Core::uint32 restrictDocumentKind = 0;
};
bool WriteSchemaAttribute(std::ofstream& stream, const UISchemaAttributeDefinition& attribute) {
WriteString(stream, attribute.name);
UIDocumentArtifactSchemaAttributeHeader header;
header.valueType = static_cast<Core::uint32>(attribute.valueType);
header.documentKind = static_cast<Core::uint32>(attribute.documentKind);
header.allowedValueCount = static_cast<Core::uint32>(attribute.allowedValues.Size());
header.line = attribute.location.line;
header.column = attribute.location.column;
header.required = attribute.required ? 1u : 0u;
header.restrictDocumentKind = attribute.restrictDocumentKind ? 1u : 0u;
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!stream) {
return false;
}
for (const Containers::String& value : attribute.allowedValues) {
WriteString(stream, value);
if (!stream) {
return false;
}
}
return true;
}
bool ReadSchemaAttribute(std::ifstream& stream, UISchemaAttributeDefinition& outAttribute) {
if (!ReadString(stream, outAttribute.name)) {
return false;
}
UIDocumentArtifactSchemaAttributeHeader header;
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!stream) {
return false;
}
outAttribute.valueType = static_cast<UISchemaValueType>(header.valueType);
outAttribute.documentKind = static_cast<UIDocumentKind>(header.documentKind);
outAttribute.location.line = header.line;
outAttribute.location.column = header.column;
outAttribute.required = header.required != 0;
outAttribute.restrictDocumentKind = header.restrictDocumentKind != 0;
outAttribute.allowedValues.Clear();
outAttribute.allowedValues.Reserve(header.allowedValueCount);
for (Core::uint32 index = 0; index < header.allowedValueCount; ++index) {
Containers::String value;
if (!ReadString(stream, value)) {
return false;
}
outAttribute.allowedValues.PushBack(std::move(value));
}
return true;
}
bool WriteSchemaElement(std::ofstream& stream, const UISchemaElementDefinition& element) {
WriteString(stream, element.tagName);
UIDocumentArtifactSchemaElementHeader header;
header.attributeCount = static_cast<Core::uint32>(element.attributes.Size());
header.childCount = static_cast<Core::uint32>(element.children.Size());
header.line = element.location.line;
header.column = element.location.column;
header.allowUnknownAttributes = element.allowUnknownAttributes ? 1u : 0u;
header.allowUnknownChildren = element.allowUnknownChildren ? 1u : 0u;
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!stream) {
return false;
}
for (const UISchemaAttributeDefinition& attribute : element.attributes) {
if (!WriteSchemaAttribute(stream, attribute)) {
return false;
}
}
for (const UISchemaElementDefinition& child : element.children) {
if (!WriteSchemaElement(stream, child)) {
return false;
}
}
return true;
}
bool ReadSchemaElement(std::ifstream& stream, UISchemaElementDefinition& outElement) {
if (!ReadString(stream, outElement.tagName)) {
return false;
}
UIDocumentArtifactSchemaElementHeader header;
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!stream) {
return false;
}
outElement.location.line = header.line;
outElement.location.column = header.column;
outElement.allowUnknownAttributes = header.allowUnknownAttributes != 0;
outElement.allowUnknownChildren = header.allowUnknownChildren != 0;
outElement.attributes.Clear();
outElement.children.Clear();
outElement.attributes.Reserve(header.attributeCount);
outElement.children.Reserve(header.childCount);
for (Core::uint32 index = 0; index < header.attributeCount; ++index) {
UISchemaAttributeDefinition attribute;
if (!ReadSchemaAttribute(stream, attribute)) {
return false;
}
outElement.attributes.PushBack(std::move(attribute));
}
for (Core::uint32 index = 0; index < header.childCount; ++index) {
UISchemaElementDefinition child;
if (!ReadSchemaElement(stream, child)) {
return false;
}
outElement.children.PushBack(std::move(child));
}
return true;
}
bool WriteSchemaDefinition(std::ofstream& stream, const UISchemaDefinition& schemaDefinition) {
WriteString(stream, schemaDefinition.name);
UIDocumentArtifactSchemaDefinitionHeader header;
header.elementCount = static_cast<Core::uint32>(schemaDefinition.elements.Size());
header.valid = schemaDefinition.valid ? 1u : 0u;
stream.write(reinterpret_cast<const char*>(&header), sizeof(header));
if (!stream) {
return false;
}
for (const UISchemaElementDefinition& element : schemaDefinition.elements) {
if (!WriteSchemaElement(stream, element)) {
return false;
}
}
return true;
}
bool ReadSchemaDefinition(std::ifstream& stream, UISchemaDefinition& outSchemaDefinition) {
if (!ReadString(stream, outSchemaDefinition.name)) {
return false;
}
UIDocumentArtifactSchemaDefinitionHeader header;
stream.read(reinterpret_cast<char*>(&header), sizeof(header));
if (!stream) {
return false;
}
outSchemaDefinition.valid = header.valid != 0;
outSchemaDefinition.elements.Clear();
outSchemaDefinition.elements.Reserve(header.elementCount);
for (Core::uint32 index = 0; index < header.elementCount; ++index) {
UISchemaElementDefinition element;
if (!ReadSchemaElement(stream, element)) {
return false;
}
outSchemaDefinition.elements.PushBack(std::move(element));
}
return true;
}
bool IsNameStartChar(char ch) {
return std::isalpha(static_cast<unsigned char>(ch)) != 0 ||
ch == '_' ||
@@ -173,6 +369,560 @@ bool LooksLikeUIDocumentReference(const Containers::String& value) {
trimmed.size() >= 9 && trimmed.rfind(".xcschema") == trimmed.size() - 9);
}
bool ContainsErrorDiagnostics(const Containers::Array<UIDocumentDiagnostic>& diagnostics) {
for (const UIDocumentDiagnostic& diagnostic : diagnostics) {
if (diagnostic.severity == UIDocumentDiagnosticSeverity::Error) {
return true;
}
}
return false;
}
void AppendDiagnostic(
const Containers::String& sourcePath,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage,
UIDocumentDiagnosticSeverity severity,
const UIDocumentSourceLocation& location,
const Containers::String& message) {
UIDocumentDiagnostic diagnostic;
diagnostic.severity = severity;
diagnostic.location = location;
diagnostic.message = message;
diagnostics.PushBack(std::move(diagnostic));
if (severity == UIDocumentDiagnosticSeverity::Error && inOutErrorMessage.Empty()) {
inOutErrorMessage = FormatDiagnosticMessage(sourcePath, location, message);
}
}
bool TryParseBooleanValueExtended(const Containers::String& value, bool& outValue) {
const std::string normalized = ToLowerCopy(ToStdString(value.Trim()));
if (normalized == "true" || normalized == "1" || normalized == "yes") {
outValue = true;
return true;
}
if (normalized == "false" || normalized == "0" || normalized == "no") {
outValue = false;
return true;
}
return false;
}
bool TryParseSchemaValueTypeExtended(const Containers::String& value, UISchemaValueType& outValueType) {
const std::string normalized = ToLowerCopy(ToStdString(value.Trim()));
if (normalized == "string") {
outValueType = UISchemaValueType::String;
return true;
}
if (normalized == "boolean" || normalized == "bool") {
outValueType = UISchemaValueType::Boolean;
return true;
}
if (normalized == "integer" || normalized == "int") {
outValueType = UISchemaValueType::Integer;
return true;
}
if (normalized == "number" || normalized == "float") {
outValueType = UISchemaValueType::Number;
return true;
}
if (normalized == "document") {
outValueType = UISchemaValueType::Document;
return true;
}
if (normalized == "enum") {
outValueType = UISchemaValueType::Enum;
return true;
}
return false;
}
bool TryParseDocumentKindValueExtended(const Containers::String& value, UIDocumentKind& outKind) {
const std::string normalized = ToLowerCopy(ToStdString(value.Trim()));
if (normalized == "view" || normalized == "xcui") {
outKind = UIDocumentKind::View;
return true;
}
if (normalized == "theme" || normalized == "xctheme") {
outKind = UIDocumentKind::Theme;
return true;
}
if (normalized == "schema" || normalized == "xcschema") {
outKind = UIDocumentKind::Schema;
return true;
}
return false;
}
Containers::Array<Containers::String> ParseAllowedValueList(const Containers::String& value) {
Containers::Array<Containers::String> values;
std::string token;
const std::string rawValue = ToStdString(value);
auto flushToken = [&]() {
Containers::String trimmed = ToContainersString(token).Trim();
if (!trimmed.Empty()) {
values.PushBack(std::move(trimmed));
}
token.clear();
};
for (char ch : rawValue) {
if (ch == ',' || ch == '|' || ch == ';') {
flushToken();
continue;
}
token.push_back(ch);
}
flushToken();
return values;
}
bool ValidateNodeTree(
const Containers::String& sourcePath,
const UIDocumentNode& node,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage) {
bool valid = true;
std::unordered_set<std::string> seenAttributeNames;
for (const UIDocumentAttribute& attribute : node.attributes) {
const std::string key = ToStdString(attribute.name);
if (!seenAttributeNames.insert(key).second) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Duplicate attribute '") +
attribute.name +
"' on <" +
node.tagName +
">.");
valid = false;
}
}
for (const UIDocumentNode& child : node.children) {
if (!ValidateNodeTree(sourcePath, child, diagnostics, inOutErrorMessage)) {
valid = false;
}
}
return valid;
}
const UIDocumentAttribute* FindAttributeByName(
const UIDocumentNode& node,
const char* name,
const char* alias = nullptr) {
if (const UIDocumentAttribute* attribute = node.FindAttribute(name)) {
return attribute;
}
if (alias != nullptr) {
return node.FindAttribute(alias);
}
return nullptr;
}
bool ValidateAllowedAttributes(
const Containers::String& sourcePath,
const UIDocumentNode& node,
std::initializer_list<const char*> allowedAttributeNames,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage) {
bool valid = true;
for (const UIDocumentAttribute& attribute : node.attributes) {
bool allowed = false;
for (const char* allowedName : allowedAttributeNames) {
if (attribute.name == allowedName) {
allowed = true;
break;
}
}
if (!allowed) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Unsupported attribute '") +
attribute.name +
"' on schema node <" +
node.tagName +
">.");
valid = false;
}
}
return valid;
}
bool BuildSchemaAttributeDefinition(
const Containers::String& sourcePath,
const UIDocumentNode& node,
UISchemaAttributeDefinition& outDefinition,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage) {
bool valid = true;
if (!node.children.Empty()) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
"Schema <Attribute> nodes cannot contain child elements.");
valid = false;
}
if (!ValidateAllowedAttributes(
sourcePath,
node,
{"name", "type", "required", "documentKind", "kind", "restrictDocumentKind", "allowedValues", "values"},
diagnostics,
inOutErrorMessage)) {
valid = false;
}
outDefinition.location = node.location;
const UIDocumentAttribute* nameAttribute = FindAttributeByName(node, "name");
if (nameAttribute == nullptr || nameAttribute->value.Trim().Empty()) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
"Schema attribute definition is missing required 'name'.");
valid = false;
} else {
outDefinition.name = nameAttribute->value.Trim();
}
if (const UIDocumentAttribute* typeAttribute = FindAttributeByName(node, "type");
typeAttribute != nullptr && !typeAttribute->value.Trim().Empty()) {
if (!TryParseSchemaValueTypeExtended(typeAttribute->value, outDefinition.valueType)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Unsupported schema value type '") +
typeAttribute->value +
"'.");
valid = false;
}
}
if (const UIDocumentAttribute* requiredAttribute = FindAttributeByName(node, "required");
requiredAttribute != nullptr && !requiredAttribute->value.Trim().Empty()) {
if (!TryParseBooleanValueExtended(requiredAttribute->value, outDefinition.required)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Invalid boolean value '") +
requiredAttribute->value +
"' for schema attribute 'required'.");
valid = false;
}
}
const UIDocumentAttribute* documentKindAttribute =
FindAttributeByName(node, "documentKind", "kind");
if (documentKindAttribute != nullptr && !documentKindAttribute->value.Trim().Empty()) {
if (!TryParseDocumentKindValueExtended(documentKindAttribute->value, outDefinition.documentKind)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Unsupported document kind '") +
documentKindAttribute->value +
"' in schema attribute definition.");
valid = false;
} else if (outDefinition.valueType == UISchemaValueType::Document) {
outDefinition.restrictDocumentKind = true;
}
}
if (const UIDocumentAttribute* restrictDocumentKindAttribute = FindAttributeByName(node, "restrictDocumentKind");
restrictDocumentKindAttribute != nullptr && !restrictDocumentKindAttribute->value.Trim().Empty()) {
if (!TryParseBooleanValueExtended(
restrictDocumentKindAttribute->value,
outDefinition.restrictDocumentKind)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Invalid boolean value '") +
restrictDocumentKindAttribute->value +
"' for schema attribute 'restrictDocumentKind'.");
valid = false;
}
}
if (const UIDocumentAttribute* allowedValuesAttribute = FindAttributeByName(node, "allowedValues", "values");
allowedValuesAttribute != nullptr) {
outDefinition.allowedValues = ParseAllowedValueList(allowedValuesAttribute->value);
}
if (outDefinition.valueType == UISchemaValueType::Enum && outDefinition.allowedValues.Empty()) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Enum schema attribute '") +
outDefinition.name +
"' must declare 'allowedValues'.");
valid = false;
}
return valid;
}
bool BuildSchemaElementDefinition(
const Containers::String& sourcePath,
const UIDocumentNode& node,
UISchemaElementDefinition& outDefinition,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage) {
bool valid = ValidateAllowedAttributes(
sourcePath,
node,
{"tag", "name", "allowUnknownAttributes", "allowUnknownChildren"},
diagnostics,
inOutErrorMessage);
outDefinition.location = node.location;
const UIDocumentAttribute* tagAttribute = FindAttributeByName(node, "tag", "name");
if (tagAttribute == nullptr || tagAttribute->value.Trim().Empty()) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
"Schema element definition is missing required 'tag'.");
valid = false;
} else {
outDefinition.tagName = tagAttribute->value.Trim();
}
if (const UIDocumentAttribute* allowUnknownAttributes = FindAttributeByName(node, "allowUnknownAttributes");
allowUnknownAttributes != nullptr && !allowUnknownAttributes->value.Trim().Empty()) {
if (!TryParseBooleanValueExtended(
allowUnknownAttributes->value,
outDefinition.allowUnknownAttributes)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Invalid boolean value '") +
allowUnknownAttributes->value +
"' for schema element 'allowUnknownAttributes'.");
valid = false;
}
}
if (const UIDocumentAttribute* allowUnknownChildren = FindAttributeByName(node, "allowUnknownChildren");
allowUnknownChildren != nullptr && !allowUnknownChildren->value.Trim().Empty()) {
if (!TryParseBooleanValueExtended(
allowUnknownChildren->value,
outDefinition.allowUnknownChildren)) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
node.location,
Containers::String("Invalid boolean value '") +
allowUnknownChildren->value +
"' for schema element 'allowUnknownChildren'.");
valid = false;
}
}
std::unordered_set<std::string> seenAttributeDefinitions;
std::unordered_set<std::string> seenChildElementDefinitions;
for (const UIDocumentNode& child : node.children) {
if (child.tagName == "Attribute") {
UISchemaAttributeDefinition attributeDefinition;
if (!BuildSchemaAttributeDefinition(
sourcePath,
child,
attributeDefinition,
diagnostics,
inOutErrorMessage)) {
valid = false;
}
const std::string attributeKey = ToStdString(attributeDefinition.name);
if (!attributeDefinition.name.Empty() &&
!seenAttributeDefinitions.insert(attributeKey).second) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
child.location,
Containers::String("Duplicate schema attribute definition '") +
attributeDefinition.name +
"' on <" +
outDefinition.tagName +
">.");
valid = false;
}
outDefinition.attributes.PushBack(std::move(attributeDefinition));
continue;
}
if (child.tagName == "Element") {
UISchemaElementDefinition childElementDefinition;
if (!BuildSchemaElementDefinition(
sourcePath,
child,
childElementDefinition,
diagnostics,
inOutErrorMessage)) {
valid = false;
}
const std::string childKey = ToStdString(childElementDefinition.tagName);
if (!childElementDefinition.tagName.Empty() &&
!seenChildElementDefinitions.insert(childKey).second) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
child.location,
Containers::String("Duplicate child schema element definition '") +
childElementDefinition.tagName +
"' on <" +
outDefinition.tagName +
">.");
valid = false;
}
outDefinition.children.PushBack(std::move(childElementDefinition));
continue;
}
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
child.location,
Containers::String("Unsupported schema child <") +
child.tagName +
"> inside <Element>.");
valid = false;
}
return valid;
}
bool BuildSchemaDefinition(
const Containers::String& sourcePath,
const UIDocumentNode& rootNode,
const Containers::String& defaultName,
UISchemaDefinition& outSchemaDefinition,
Containers::Array<UIDocumentDiagnostic>& diagnostics,
Containers::String& inOutErrorMessage) {
outSchemaDefinition.Clear();
if (rootNode.tagName != "Schema") {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
rootNode.location,
"UI schema root element must be <Schema>.");
return false;
}
bool valid = ValidateAllowedAttributes(
sourcePath,
rootNode,
{"name"},
diagnostics,
inOutErrorMessage);
if (const UIDocumentAttribute* nameAttribute = FindAttributeByName(rootNode, "name");
nameAttribute != nullptr && !nameAttribute->value.Trim().Empty()) {
outSchemaDefinition.name = nameAttribute->value.Trim();
} else {
outSchemaDefinition.name = defaultName;
}
std::unordered_set<std::string> seenRootElements;
for (const UIDocumentNode& child : rootNode.children) {
if (child.tagName != "Element") {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
child.location,
Containers::String("Unsupported schema child <") +
child.tagName +
"> inside <Schema>.");
valid = false;
continue;
}
UISchemaElementDefinition elementDefinition;
if (!BuildSchemaElementDefinition(
sourcePath,
child,
elementDefinition,
diagnostics,
inOutErrorMessage)) {
valid = false;
}
const std::string elementKey = ToStdString(elementDefinition.tagName);
if (!elementDefinition.tagName.Empty() &&
!seenRootElements.insert(elementKey).second) {
AppendDiagnostic(
sourcePath,
diagnostics,
inOutErrorMessage,
UIDocumentDiagnosticSeverity::Error,
child.location,
Containers::String("Duplicate root schema element definition '") +
elementDefinition.tagName +
"'.");
valid = false;
}
outSchemaDefinition.elements.PushBack(std::move(elementDefinition));
}
outSchemaDefinition.valid = valid && !ContainsErrorDiagnostics(diagnostics);
return outSchemaDefinition.valid;
}
void AppendUniqueDependency(const Containers::String& dependencyPath,
std::unordered_set<std::string>& seenDependencies,
Containers::Array<Containers::String>& outDependencies) {
@@ -345,6 +1095,18 @@ public:
outResult.document.displayName = nameAttribute->value;
}
if (m_kind == UIDocumentKind::Schema) {
if (!BuildSchemaDefinition(
NormalizePathString(m_resolvedPath),
outResult.document.rootNode,
outResult.document.displayName,
outResult.document.schemaDefinition,
outResult.document.diagnostics,
m_errorMessage)) {
return Finalize(outResult, false);
}
}
std::unordered_set<std::string> seenDependencies;
if (!CollectUIDocumentDependencies(
m_resolvedPath,
@@ -356,6 +1118,18 @@ public:
return Finalize(outResult, false);
}
if (!ValidateNodeTree(
outResult.document.sourcePath,
outResult.document.rootNode,
outResult.document.diagnostics,
m_errorMessage)) {
return Finalize(outResult, false);
}
if (ContainsErrorDiagnostics(outResult.document.diagnostics)) {
return Finalize(outResult, false);
}
outResult.document.valid = true;
outResult.succeeded = true;
return Finalize(outResult, true);
@@ -752,6 +1526,12 @@ bool WriteUIDocumentArtifact(const Containers::String& artifactPath,
}
return false;
}
if (!WriteSchemaDefinition(output, compileResult.document.schemaDefinition)) {
if (outErrorMessage != nullptr) {
*outErrorMessage = Containers::String("Failed to write UI document schema definition: ") + artifactPath;
}
return false;
}
for (const Containers::String& dependency : compileResult.document.dependencies) {
WriteString(output, dependency);
@@ -805,7 +1585,8 @@ bool LoadUIDocumentArtifact(const Containers::String& artifactPath,
return false;
}
if (header.schemaVersion != kUIDocumentArtifactSchemaVersion) {
if (header.schemaVersion != 1u &&
header.schemaVersion != kUIDocumentArtifactSchemaVersion) {
outResult.errorMessage = Containers::String("Unsupported UI document artifact schema version: ") + artifactPath;
return false;
}
@@ -822,6 +1603,13 @@ bool LoadUIDocumentArtifact(const Containers::String& artifactPath,
outResult.errorMessage = Containers::String("Failed to read UI document artifact body: ") + artifactPath;
return false;
}
outResult.document.schemaDefinition.Clear();
if (header.schemaVersion >= 2u) {
if (!ReadSchemaDefinition(input, outResult.document.schemaDefinition)) {
outResult.errorMessage = Containers::String("Failed to read UI document artifact schema definition: ") + artifactPath;
return false;
}
}
outResult.document.dependencies.Clear();
outResult.document.dependencies.Reserve(header.dependencyCount);
@@ -856,6 +1644,25 @@ bool LoadUIDocumentArtifact(const Containers::String& artifactPath,
outResult.document.diagnostics.PushBack(std::move(diagnostic));
}
if (expectedKind == UIDocumentKind::Schema) {
const Containers::String sourcePath =
outResult.document.sourcePath.Empty()
? artifactPath
: outResult.document.sourcePath;
if (!outResult.document.schemaDefinition.valid &&
!BuildSchemaDefinition(
sourcePath,
outResult.document.rootNode,
outResult.document.displayName,
outResult.document.schemaDefinition,
outResult.document.diagnostics,
outResult.errorMessage)) {
outResult.succeeded = false;
outResult.document.valid = false;
return false;
}
}
outResult.document.valid = true;
outResult.succeeded = true;
return true;