yalantinglibs/include/ylt/standalone/iguana/value.hpp

204 lines
6.1 KiB
C++

#pragma once
#include <string>
#include <string_view>
#include <unordered_map>
#include <utility>
#include <variant>
#include <vector>
#include "error_code.h"
namespace iguana {
#define GCC_COMPILER (defined(__GNUC__) && !defined(__clang__))
#ifdef GCC_COMPILER
#include <map>
template <class Key, class T, typename... Args>
using json_map = std::map<Key, T, Args...>;
#else
template <class Key, class T, typename... Args>
using json_map = std::unordered_map<Key, T, Args...>;
#endif
enum dom_parse_error { ok, wrong_type };
template <typename CharT>
struct basic_json_value
: std::variant<std::monostate, std::nullptr_t, bool, double, int,
std::basic_string<CharT>,
std::vector<basic_json_value<CharT>>,
json_map<std::basic_string<CharT>, basic_json_value<CharT>>,
std::basic_string_view<CharT>> {
using string_type = std::basic_string<CharT>;
using string_view_type = std::basic_string_view<CharT>;
using array_type = std::vector<basic_json_value<CharT>>;
using object_type = json_map<string_type, basic_json_value<CharT>>;
using base_type =
std::variant<std::monostate, std::nullptr_t, bool, double, int,
string_type, array_type, object_type, string_view_type>;
using base_type::base_type;
inline const static std::unordered_map<size_t, std::string> type_map_ = {
{0, "undefined type"}, {1, "null type"}, {2, "bool type"},
{3, "double type"}, {4, "int type"}, {5, "string type"},
{6, "array type"}, {7, "object type"}, {8, "string_view type"}};
basic_json_value() : base_type(std::in_place_type<std::monostate>) {}
basic_json_value(CharT const *value)
: base_type(std::in_place_type<string_type>, value) {}
base_type &base() { return *this; }
base_type const &base() const { return *this; }
bool is_undefined() const {
return std::holds_alternative<std::monostate>(*this);
}
bool is_null() const { return std::holds_alternative<std::nullptr_t>(*this); }
bool is_bool() const { return std::holds_alternative<bool>(*this); }
bool is_double() const { return std::holds_alternative<double>(*this); }
bool is_int() const { return std::holds_alternative<int>(*this); }
bool is_number() const { return is_double() || is_int(); }
bool is_string() const { return std::holds_alternative<string_type>(*this); }
bool is_array() const { return std::holds_alternative<array_type>(*this); }
bool is_object() const { return std::holds_alternative<object_type>(*this); }
bool is_string_view() const {
return std::holds_alternative<string_view_type>(*this);
}
// if type is not match, will throw exception, if pass std::error_code, won't
// throw exception
template <typename T>
T get() const {
try {
return std::get<T>(*this);
} catch (std::exception &e) {
auto it = type_map_.find(this->index());
if (it == type_map_.end()) {
throw std::invalid_argument("undefined type");
}
else {
throw std::invalid_argument(it->second);
}
} catch (...) {
throw std::invalid_argument("unknown exception");
}
}
template <typename T>
T get(std::error_code &ec) const {
try {
return get<T>();
} catch (std::exception &e) {
ec = iguana::make_error_code(iguana::dom_errc::wrong_type, e.what());
return T{};
}
}
template <typename T>
std::error_code get_to(T &v) const {
std::error_code ec;
v = get<T>(ec);
return ec;
}
template <typename T>
T at(const std::string &key) {
const auto &map = get<object_type>();
auto it = map.find(key);
if (it == map.end()) {
throw std::invalid_argument("the key is unknown");
}
return it->second.template get<T>();
}
template <typename T>
T at(const std::string &key, std::error_code &ec) {
const auto &map = get<object_type>(ec);
if (ec) {
return T{};
}
auto it = map.find(key);
if (it == map.end()) {
ec = std::make_error_code(std::errc::invalid_argument);
return T{};
}
return it->second.template get<T>(ec);
}
template <typename T>
T at(size_t idx) {
const auto &arr = get<array_type>();
if (idx >= arr.size()) {
throw std::out_of_range("idx is out of range");
}
return arr[idx].template get<T>();
}
template <typename T>
T at(size_t idx, std::error_code &ec) {
const auto &arr = get<array_type>(ec);
if (ec) {
return T{};
}
if (idx >= arr.size()) {
ec = std::make_error_code(std::errc::result_out_of_range);
return T{};
}
return arr[idx].template get<T>(ec);
}
object_type to_object() const { return get<object_type>(); }
object_type to_object(std::error_code &ec) const {
return get<object_type>(ec);
}
array_type to_array() const { return get<array_type>(); }
array_type to_array(std::error_code &ec) const { return get<array_type>(ec); }
double to_double() const { return get<double>(); }
double to_double(std::error_code &ec) const { return get<double>(ec); }
int to_int() const { return get<int>(); }
int to_int(std::error_code &ec) const { return get<int>(ec); }
bool to_bool() const { return get<bool>(); }
bool to_bool(std::error_code &ec) const { return get<bool>(ec); }
string_type to_string() const { return get<string_type>(); }
string_type to_string(std::error_code &ec) const {
return get<string_type>(ec);
}
string_view_type to_string_view() const { return get<string_view_type>(); }
string_view_type to_string_view(std::error_code &ec) const {
return get<string_view_type>(ec);
}
};
template <typename CharT>
using basic_jarray = typename basic_json_value<CharT>::array_type;
template <typename CharT>
using basic_jobject = typename basic_json_value<CharT>::object_type;
template <typename CharT>
using basic_jpair = typename basic_jobject<CharT>::value_type;
using jvalue = basic_json_value<char>;
using jarray = basic_jarray<char>;
using jobject = basic_jobject<char>;
using jpair = basic_jpair<char>;
template <typename CharT>
void swap(basic_json_value<CharT> &lhs, basic_json_value<CharT> &rhs) noexcept {
lhs.swap(rhs);
}
} // namespace iguana