#pragma once
#include <string>
#include <unordered_set>
#include <vector>
#include "fwd.hpp"
#include "UniquePtr.hpp"
NAMESPACE_SOUP
{
class xml
{
public:
static XmlMode MODE_XML;
static XmlMode MODE_LAX_XML;
static XmlMode MODE_HTML;
[[nodiscard]] static UniquePtr<XmlTag> parseAndDiscardMetadata(const std::string& xml, const XmlMode& mode = MODE_XML);
[[nodiscard]] static UniquePtr<XmlTag> parseAndDiscardMetadata(const char* begin, const char* end, const XmlMode& mode = MODE_XML);
[[nodiscard]] static std::vector<UniquePtr<XmlNode>> parse(const std::string& xml, const XmlMode& mode = MODE_XML);
[[nodiscard]] static std::vector<UniquePtr<XmlNode>> parse(const char* begin, const char* end, const XmlMode& mode = MODE_XML);
private:
[[nodiscard]] static UniquePtr<XmlNode> parseImpl(const char*& i, const char* end, const XmlMode& mode, int max_depth);
};
struct XmlNode
{
const bool is_text;
constexpr XmlNode(bool is_text) noexcept
: is_text(is_text)
{
}
virtual ~XmlNode() = default;
[[nodiscard]] std::string encode(const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
[[nodiscard]] std::string encodePretty(const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
void encodeAndAppendTo(std::string& str, const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
void encodePrettyAndAppendTo(std::string& str, const XmlMode& mode = xml::MODE_XML, unsigned depth = 0) const SOUP_EXCAL;
[[nodiscard]] bool isTag() const noexcept;
[[nodiscard]] bool isText() const noexcept;
[[nodiscard]] XmlTag& asTag();
[[nodiscard]] XmlText& asText();
[[nodiscard]] const XmlTag& asTag() const;
[[nodiscard]] const XmlText& asText() const;
};
struct XmlTag : public XmlNode
{
std::string name{};
std::vector<UniquePtr<XmlNode>> children{};
std::vector<std::pair<std::string, std::string>> attributes{};
XmlTag() noexcept
: XmlNode(false)
{
}
[[nodiscard]] std::string encode(const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
[[nodiscard]] std::string encodePretty(const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
void encodeAndAppendTo(std::string& str, const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
void encodePrettyAndAppendTo(std::string& str, const XmlMode& mode = xml::MODE_XML, unsigned depth = 0) const SOUP_EXCAL;
void encodeAttributesAndAppendTo(std::string& str, const XmlMode& mode = xml::MODE_XML) const SOUP_EXCAL;
[[nodiscard]] bool hasAttribute(const std::string& name) const noexcept;
[[nodiscard]] const std::string& getAttribute(const std::string& name) const;
[[nodiscard]] XmlTag* findTag(const std::string& name_target);
};
struct XmlText : public XmlNode
{
std::string contents{};
XmlText() noexcept
: XmlNode(true)
{
}
XmlText(std::string&& contents) noexcept;
void encodeAndAppendTo(std::string& str) const SOUP_EXCAL;
};
struct XmlMode
{
std::unordered_set<std::string> self_closing_tags;
bool empty_attribute_syntax = false;
bool unquoted_attributes = false;
};
inline std::string XmlNode::encode(const XmlMode& mode) const SOUP_EXCAL
{
std::string str;
encodeAndAppendTo(str, mode);
return str;
}
inline std::string XmlNode::encodePretty(const XmlMode& mode) const SOUP_EXCAL
{
std::string str;
encodePrettyAndAppendTo(str, mode);
return str;
}
inline std::string XmlTag::encode(const XmlMode& mode) const SOUP_EXCAL
{
std::string str;
encodeAndAppendTo(str, mode);
return str;
}
inline std::string XmlTag::encodePretty(const XmlMode& mode) const SOUP_EXCAL
{
std::string str;
encodePrettyAndAppendTo(str, mode);
return str;
}
}