mirror of
https://github.com/Awakenchan/LanternEngine.git
synced 2026-11-11 01:11:43 -10:00
70 lines
2.4 KiB
C++
70 lines
2.4 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
#include "xml.h"
|
|
|
|
#include <expat.h>
|
|
|
|
namespace start {
|
|
|
|
std::optional<std::string> XmlElement::Get(std::string_view key) const {
|
|
for (const auto& [k, v] : attributes)
|
|
if (k == key) return v;
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::string XmlElement::Get(std::string_view key, std::string_view fallback) const {
|
|
const auto value = Get(key);
|
|
return value ? *value : std::string(fallback);
|
|
}
|
|
|
|
void XmlElement::Iter(std::string_view tag, std::vector<const XmlElement*>& out) const {
|
|
if (name == tag) out.push_back(this);
|
|
for (const auto& child : children) child->Iter(tag, out);
|
|
}
|
|
|
|
namespace {
|
|
struct Builder {
|
|
std::unique_ptr<XmlElement> root;
|
|
std::vector<XmlElement*> stack;
|
|
};
|
|
|
|
void XMLCALL OnStart(void* data, const XML_Char* name, const XML_Char** attributes) {
|
|
auto* b = static_cast<Builder*>(data);
|
|
auto element = std::make_unique<XmlElement>();
|
|
element->name = name;
|
|
for (int i = 0; attributes[i]; i += 2) element->attributes.emplace_back(attributes[i], attributes[i + 1]);
|
|
XmlElement* raw = element.get();
|
|
if (b->stack.empty()) b->root = std::move(element);
|
|
else b->stack.back()->children.push_back(std::move(element));
|
|
b->stack.push_back(raw);
|
|
}
|
|
|
|
void XMLCALL OnEnd(void* data, const XML_Char*) {
|
|
static_cast<Builder*>(data)->stack.pop_back();
|
|
}
|
|
|
|
void XMLCALL OnText(void* data, const XML_Char* text, int length) {
|
|
auto* b = static_cast<Builder*>(data);
|
|
if (!b->stack.empty()) b->stack.back()->text.append(text, std::size_t(length));
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<XmlElement> ParseXml(const fs::path& path) {
|
|
const Bytes data = ReadFile(path);
|
|
Builder builder;
|
|
XML_Parser parser = XML_ParserCreate(nullptr);
|
|
XML_SetUserData(parser, &builder);
|
|
XML_SetElementHandler(parser, OnStart, OnEnd);
|
|
XML_SetCharacterDataHandler(parser, OnText);
|
|
const bool ok = XML_Parse(parser, reinterpret_cast<const char*>(data.data()), int(data.size()), XML_TRUE) != XML_STATUS_ERROR;
|
|
std::string message;
|
|
if (!ok)
|
|
message = std::string(XML_ErrorString(XML_GetErrorCode(parser))) + ": line " +
|
|
std::to_string(XML_GetCurrentLineNumber(parser)) + ", column " +
|
|
std::to_string(XML_GetCurrentColumnNumber(parser));
|
|
XML_ParserFree(parser);
|
|
if (!ok || !builder.root) throw Error(ok ? "empty XML document" : message);
|
|
return std::move(builder.root);
|
|
}
|
|
|
|
}
|