#pragma once #include #include #include #include #include #include #include #include #include #include namespace orange::kv { namespace Types { enum KVVariantType { None, String, Int, Float, Ptr, Color, Uint64, KeyValues, Count, }; } using KeyValuesType = Types::KVVariantType; class KeyValues; class KeyValuesVariant { public: using KeyValuesChild = std::unique_ptr; using KeyValuesString = SmallVector; KeyValuesVariant() { } template KeyValuesVariant(const T& arg) { Set(arg); } template KeyValuesVariant(T&& arg) { Set(Move(arg)); } ~KeyValuesVariant() { Clear(); } void Clear() { switch (m_type) { case Types::String: m_str.clear(); break; case Types::KeyValues: m_data.Destruct(); break; default: break; } m_type = Types::None; m_data.Clear(); } template T Get() const; void Set(const std::string& val) { Clear(); m_type = Types::String; m_str = KeyValuesString{ StringView{ val } }; } void Set(StringView val) { Clear(); m_type = Types::String; m_str = KeyValuesString{ val }; } void Set(int32_t val) { Clear(); m_type = Types::Int; m_data.Get() = val; } void Set(int64_t val) { Clear(); m_type = Types::Int; m_data.Get() = val; } void Set(float val) { Clear(); m_type = Types::Float; m_data.Get() = val; } void Set(double val) { Clear(); m_type = Types::Float; m_data.Get() = val; } void Set(void* val) { Clear(); m_type = Types::Ptr; m_data.Get() = val; } void Set(RGBAColor32 val) { Clear(); m_type = Types::Color; m_data.Construct(val); } void Set(uint32_t val) { Clear(); m_type = Types::Uint64; m_data.Get() = val; } void Set(uint64_t val) { Clear(); m_type = Types::Uint64; m_data.Get() = val; } void Set(KeyValuesChild val) { Clear(); m_type = Types::KeyValues; m_data.Get() = Move(val); } KeyValuesVariant& operator [](const char *string); const KeyValuesVariant& operator [](const char *string) const; static KeyValuesVariant& GetEmptyValue() { return s_Nothing; } private: static KeyValuesVariant s_Nothing; KeyValuesType m_type = Types::None; KeyValuesString m_str; Variant< int64_t, double, void*, uint64_t, RGBAColor32, KeyValuesChild> m_data; }; class KeyValues { public: static std::unique_ptr ParseFromUTF8(StringView buffer) { const char *start = buffer.begin(); const char *end = buffer.end(); return ParseChild(start, end); } static KeyValues& Nothing() { return s_Nothing; } KeyValuesVariant& operator [](const char *string) { auto iter = m_children.find(string); if (iter == m_children.end()) { std::cerr << "Returning KeyValuesVariant empty value: " << string << std::endl; return KeyValuesVariant::GetEmptyValue(); } return iter->second; } const KeyValuesVariant& operator [] (const char *string) const { auto iter = m_children.find(string); if (iter == m_children.end()) { std::cerr << "Returning KeyValuesVariant empty value: " << string << std::endl; return KeyValuesVariant::GetEmptyValue(); } return iter->second; } private: static KeyValues s_Nothing; static std::unique_ptr ParseChild(const char*& start, const char* end) { StringView range = StringView(start, end); if (start == end) return nullptr; stream::ConsumeSpaceAndNewLine(start, end); if (*start != '{') return nullptr; auto kv = std::make_unique(); bool inString = false; bool fillingInValue = false; std::string key; std::string value; start++; while (start != end) { if (stream::IsStringToken(range, start)) { inString = !inString; start++; continue; } if (!inString) { if (stream::IsCPPComment(range, start)) { stream::AdvancePast(start, end, "\r\n"); stream::ConsumeSpaceAndNewLine(start, end); continue; } if (*start == '{') { fprintf(stdout, "Adding block child: %s - {\n", key.c_str()); auto child = ParseChild(start, end); kv->m_children.emplace(key, Move(child)); fprintf(stdout, "Adding block child: }\n"); fillingInValue = false; key.clear(); value.clear(); } if (*start == '}') { start++; break; } if (*start == ' ' || *start == '\t' || *start == '\n' || *start == '\r') { fillingInValue = !fillingInValue; if (!fillingInValue) { fprintf(stdout, "Adding child: %s - %s\n", key.c_str(), value.c_str()); kv->m_children.emplace(key, value); key.clear(); value.clear(); } else if (*start == '\n' || *start == '\r') { fillingInValue = false; } stream::ConsumeSpaceAndNewLine(start, end); continue; } } if (fillingInValue) value.push_back(*start); else key.push_back(*start); start++; } return kv; } std::unordered_multimap m_children; }; template <> inline StringView KeyValuesVariant::Get() const { switch (m_type) { case Types::String: return m_str; default: return ""; } } template <> inline KeyValues& KeyValuesVariant::Get() const { switch (m_type) { case Types::KeyValues: { auto value = m_data.Get().get(); if (value) return *value; } [[fallthrough]]; default: return KeyValues::Nothing(); } } inline KeyValuesVariant& KeyValuesVariant::operator [](const char *string) { if (m_type != Types::KeyValues) abort(); return Get()[string]; } inline const KeyValuesVariant& KeyValuesVariant::operator [](const char *string) const { if (m_type != Types::KeyValues) abort(); return Get()[string]; } }