2024-06-05 19:04:53 -04:00
|
|
|
#ifndef RFL_XML_READER_HPP_
|
|
|
|
#define RFL_XML_READER_HPP_
|
|
|
|
|
|
|
|
#include <array>
|
|
|
|
#include <exception>
|
|
|
|
#include <map>
|
|
|
|
#include <memory>
|
|
|
|
#include <optional>
|
|
|
|
#include <pugixml.hpp>
|
|
|
|
#include <sstream>
|
|
|
|
#include <stdexcept>
|
|
|
|
#include <string>
|
|
|
|
#include <string_view>
|
|
|
|
#include <type_traits>
|
|
|
|
#include <unordered_map>
|
|
|
|
#include <variant>
|
|
|
|
#include <vector>
|
|
|
|
|
|
|
|
#include "../Result.hpp"
|
|
|
|
#include "../always_false.hpp"
|
|
|
|
#include "../parsing/is_view_reader.hpp"
|
|
|
|
|
|
|
|
namespace rfl {
|
2024-06-08 14:10:59 -04:00
|
|
|
namespace xml {
|
|
|
|
|
|
|
|
struct Reader {
|
|
|
|
struct XMLInputArray {
|
|
|
|
XMLInputArray(pugi::xml_node _node) : node_(_node) {}
|
|
|
|
pugi::xml_node node_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct XMLInputObject {
|
|
|
|
XMLInputObject(pugi::xml_node _node) : node_(_node) {}
|
|
|
|
pugi::xml_node node_;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct XMLInputVar {
|
|
|
|
XMLInputVar() : node_or_attribute_(pugi::xml_node()) {}
|
|
|
|
XMLInputVar(pugi::xml_attribute _attr) : node_or_attribute_(_attr) {}
|
|
|
|
XMLInputVar(pugi::xml_node _node) : node_or_attribute_(_node) {}
|
|
|
|
std::variant<pugi::xml_node, pugi::xml_attribute> node_or_attribute_;
|
|
|
|
};
|
|
|
|
|
|
|
|
using InputArrayType = XMLInputArray;
|
|
|
|
using InputObjectType = XMLInputObject;
|
|
|
|
using InputVarType = XMLInputVar;
|
|
|
|
|
|
|
|
// TODO
|
|
|
|
template <class T>
|
|
|
|
static constexpr bool has_custom_constructor = false;
|
|
|
|
|
|
|
|
/// XML-only helper function. This is needed because XML distinguishes
|
|
|
|
/// between nodes and attributes.
|
|
|
|
static rfl::Result<pugi::xml_node> cast_as_node(
|
|
|
|
const std::variant<pugi::xml_node, pugi::xml_attribute>&
|
2024-06-08 15:53:06 -04:00
|
|
|
_node_or_attribute
|
|
|
|
) {
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto cast = [](const auto& _n) -> Result<pugi::xml_node> {
|
|
|
|
using Type = std::remove_cvref_t<decltype(_n)>;
|
|
|
|
if constexpr (std::is_same<Type, pugi::xml_node>()) {
|
|
|
|
return _n;
|
|
|
|
} else {
|
2024-06-08 15:53:06 -04:00
|
|
|
return Error(
|
|
|
|
"Field '" + std::string(_n.name()) + "' is an attribute."
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
return std::visit(cast, _node_or_attribute);
|
2024-06-05 19:04:53 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
rfl::Result<InputVarType> get_field(
|
2024-06-08 15:53:06 -04:00
|
|
|
const std::string& _name,
|
|
|
|
const InputObjectType _obj
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto node = _obj.node_.child(_name.c_str());
|
|
|
|
if (!node) {
|
|
|
|
return rfl::Error("Object contains no field named '" + _name + "'.");
|
|
|
|
}
|
|
|
|
return InputVarType(node);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool is_empty(const InputVarType _var) const noexcept {
|
|
|
|
const auto wrap = [](const auto& _node) { return !_node; };
|
|
|
|
return std::visit(cast_as_node, _var.node_or_attribute_)
|
|
|
|
.transform(wrap)
|
|
|
|
.value_or(false);
|
2024-06-05 19:04:53 -04:00
|
|
|
}
|
|
|
|
|
2024-06-08 14:10:59 -04:00
|
|
|
template <class T>
|
|
|
|
rfl::Result<T> to_basic_type(const InputVarType _var) const noexcept {
|
|
|
|
const auto get_value = [](const auto& _n) -> std::string {
|
|
|
|
using Type = std::remove_cvref_t<decltype(_n)>;
|
|
|
|
if constexpr (std::is_same<Type, pugi::xml_node>()) {
|
|
|
|
return std::string(_n.child_value());
|
|
|
|
} else {
|
|
|
|
return std::string(_n.value());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if constexpr (std::is_same<std::remove_cvref_t<T>, std::string>()) {
|
|
|
|
return std::visit(get_value, _var.node_or_attribute_);
|
|
|
|
} else if constexpr (std::is_same<std::remove_cvref_t<T>, bool>()) {
|
|
|
|
return std::visit(get_value, _var.node_or_attribute_) == "true";
|
|
|
|
} else if constexpr (std::is_floating_point<std::remove_cvref_t<T>>()) {
|
|
|
|
const auto str = std::visit(get_value, _var.node_or_attribute_);
|
|
|
|
try {
|
|
|
|
return static_cast<T>(std::stod(str));
|
|
|
|
} catch (std::exception& e) {
|
2024-06-08 15:53:06 -04:00
|
|
|
return Error(
|
|
|
|
"Could not cast '" + std::string(str) +
|
|
|
|
"' to floating point value."
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
} else if constexpr (std::is_integral<std::remove_cvref_t<T>>()) {
|
|
|
|
const auto str = std::visit(get_value, _var.node_or_attribute_);
|
|
|
|
try {
|
|
|
|
return static_cast<T>(std::stoi(str));
|
|
|
|
} catch (std::exception& e) {
|
2024-06-08 15:53:06 -04:00
|
|
|
return Error(
|
|
|
|
"Could not cast '" + std::string(str) + "' to integer."
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
static_assert(rfl::always_false_v<T>, "Unsupported type.");
|
|
|
|
}
|
2024-06-05 19:04:53 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
2024-06-08 15:53:06 -04:00
|
|
|
rfl::Result<InputArrayType> to_array(const InputVarType _var
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto wrap = [](const auto& _node) {
|
|
|
|
return InputArrayType(_node);
|
|
|
|
};
|
|
|
|
return std::visit(cast_as_node, _var.node_or_attribute_)
|
|
|
|
.transform(wrap);
|
2024-06-05 19:04:53 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
template <class ArrayReader>
|
|
|
|
std::optional<Error> read_array(
|
2024-06-08 15:53:06 -04:00
|
|
|
const ArrayReader& _array_reader,
|
|
|
|
const InputArrayType& _arr
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto name = _arr.node_.name();
|
|
|
|
for (auto node = _arr.node_; node; node = node.next_sibling(name)) {
|
|
|
|
const auto err = _array_reader.read(InputVarType(node));
|
2024-06-09 18:55:00 -04:00
|
|
|
if (err) {
|
|
|
|
return err;
|
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
return std::nullopt;
|
2024-06-05 19:04:53 -04:00
|
|
|
}
|
2024-06-08 14:10:59 -04:00
|
|
|
|
|
|
|
template <class ObjectReader>
|
|
|
|
std::optional<Error> read_object(
|
2024-06-08 15:53:06 -04:00
|
|
|
const ObjectReader& _object_reader,
|
|
|
|
const InputObjectType& _obj
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
for (auto child = _obj.node_.first_child(); child;
|
|
|
|
child = child.next_sibling()) {
|
2024-06-08 15:53:06 -04:00
|
|
|
_object_reader.read(
|
|
|
|
std::string_view(child.name()), InputVarType(child)
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
for (auto attr = _obj.node_.first_attribute(); attr;
|
|
|
|
attr = attr.next_attribute()) {
|
2024-06-08 15:53:06 -04:00
|
|
|
_object_reader.read(
|
|
|
|
std::string_view(attr.name()), InputVarType(attr)
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
if constexpr (parsing::is_view_reader_v<ObjectReader>) {
|
2024-06-08 15:53:06 -04:00
|
|
|
_object_reader.read(
|
|
|
|
std::string_view("xml_content"), InputVarType(_obj.node_)
|
|
|
|
);
|
2024-06-08 14:10:59 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
return std::nullopt;
|
|
|
|
}
|
|
|
|
|
2024-06-08 15:53:06 -04:00
|
|
|
rfl::Result<InputObjectType> to_object(const InputVarType _var
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
const auto wrap = [](const auto& _node) {
|
|
|
|
return InputObjectType(_node);
|
|
|
|
};
|
|
|
|
return std::visit(cast_as_node, _var.node_or_attribute_)
|
|
|
|
.transform(wrap);
|
|
|
|
}
|
|
|
|
|
|
|
|
template <class T>
|
2024-06-08 15:53:06 -04:00
|
|
|
rfl::Result<T> use_custom_constructor(const InputVarType _var
|
|
|
|
) const noexcept {
|
2024-06-08 14:10:59 -04:00
|
|
|
return rfl::Error("TODO");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace xml
|
|
|
|
} // namespace rfl
|
2024-06-05 19:04:53 -04:00
|
|
|
|
|
|
|
#endif
|