<commit_before>#pragma once

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string>
#include <vector>

namespace AhoViewer::xml
{
    class Node
    {
    public:
        Node(xmlNodePtr n) : m_XmlNode{ n } { }
        ~Node() = default;

        std::string get_attribute(const std::string& name) const
        {
            if (!m_XmlNode)
                return "";

            std::string attr;
            xmlChar* prop{ xmlGetProp(m_XmlNode, reinterpret_cast<const xmlChar*>(name.c_str())) };

            if (prop)
            {
                attr = reinterpret_cast<char*>(prop);
                xmlFree(prop);
            }

            return attr;
        }

        void set_attribute(const std::string& a_name, const std::string& a_value)
        {
            if (!m_XmlNode)
                return;

            const auto *name{ reinterpret_cast<const xmlChar*>(a_name.c_str()) },
                *value{ reinterpret_cast<const xmlChar*>(a_value.c_str()) };

            if (!xmlHasProp(m_XmlNode, name))
                xmlNewProp(m_XmlNode, name, value);
            else
                xmlSetProp(m_XmlNode, name, value);
        }

        std::string get_value() const
        {
            if (!m_XmlNode)
                return "";

            std::string val;<commit_msg>booru/xml: Fallback to child value when attribute is empty<commit_after>#pragma once

#include <libxml/parser.h>
#include <libxml/tree.h>
#include <string>
#include <vector>

namespace AhoViewer::xml
{
    class Node
    {
    public:
        Node(xmlNodePtr n) : m_XmlNode{ n } { }
        ~Node() = default;

        std::string get_attribute(const std::string& name) const
        {
            if (!m_XmlNode)
                return "";

            std::string attr;
            xmlChar* prop{ xmlGetProp(m_XmlNode, reinterpret_cast<const xmlChar*>(name.c_str())) };

            if (prop)
            {
                attr = reinterpret_cast<char*>(prop);
                xmlFree(prop);
            }
            // Fallback to value of given name as child if the attribute didnt exist
            // On 2022 Jan. 2nd~ Gelbooru decided to change their API to use values instead of attributes,
            // but kept their naming conventions to not match danbooru.
            else
            {
                attr = get_value(name);
            }

            return attr;
        }

        void set_attribute(const std::string& a_name, const std::string& a_value)
        {
            if (!m_XmlNode)
                return;

            const auto *name{ reinterpret_cast<const xmlChar*>(a_name.c_str()) },
                *value{ reinterpret_cast<const xmlChar*>(a_value.c_str()) };

            if (!xmlHasProp(m_XmlNode, name))
                xmlNewProp(m_XmlNode, name, value);
            else
                xmlSetProp(m_XmlNode, name, value);
        }

        std::string get_value() const
        {
            if (!m_XmlNode)
                return "";

            std::string val;
{'fraction_non_alphanumeric': 0.1078042328042328, 'fraction_numerical': 0.0016534391534391533, 'mean_word_length': 1.660510114335972, 'pattern_counts': {'":': 0, '<': 19, '<?xml version=': 0, '>': 19, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 27}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4775239', 'n_tokens_mistral': 896, 'n_tokens_neox': 852, 'n_words': 206}