305 lines
8.0 KiB
C++
305 lines
8.0 KiB
C++
/*
|
|
* Copyright 2015 - 2025 gary@drinkingtea.net
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include <json/json.h>
|
|
|
|
#include <ox/model/fieldcounter.hpp>
|
|
#include <ox/model/modelhandleradaptor.hpp>
|
|
#include <ox/model/optype.hpp>
|
|
#include <ox/model/typenamecatcher.hpp>
|
|
#include <ox/model/types.hpp>
|
|
#include <ox/std/buffer.hpp>
|
|
#include <ox/std/byteswap.hpp>
|
|
#include <ox/std/hashmap.hpp>
|
|
#include <ox/std/memops.hpp>
|
|
#include <ox/std/memory.hpp>
|
|
#include <ox/std/string.hpp>
|
|
#include <ox/std/uuid.hpp>
|
|
|
|
namespace ox {
|
|
|
|
class OrganicClawReader {
|
|
|
|
private:
|
|
Json::Value m_json;
|
|
Json::ArrayIndex m_fieldIt = 0;
|
|
int m_unionIdx = -1;
|
|
|
|
public:
|
|
OrganicClawReader() noexcept = default;
|
|
|
|
OrganicClawReader(const uint8_t *buff, std::size_t buffSize);
|
|
|
|
OrganicClawReader(const char *json, std::size_t buffSize);
|
|
|
|
explicit OrganicClawReader(Json::Value json, int unionIdx = -1) noexcept;
|
|
|
|
Error field(const char *key, bool *val) noexcept;
|
|
|
|
// array handler
|
|
template<typename T>
|
|
Error field(const char *key, T *val, std::size_t len) noexcept;
|
|
|
|
template<typename T>
|
|
Error field(const char*, HashMap<String, T> *val) noexcept;
|
|
|
|
template<typename T>
|
|
Error field(const char *key, T *val) noexcept;
|
|
|
|
template<typename U, bool force = false>
|
|
Error field(const char *key, UnionView<U, force> val) noexcept;
|
|
|
|
template<std::size_t L>
|
|
Error field(const char *key, BasicString<L> *val) noexcept;
|
|
|
|
template<std::size_t L>
|
|
Error field(const char *key, IString<L> *val) noexcept;
|
|
|
|
Error fieldCString(const char *key, char *val, std::size_t buffLen) noexcept;
|
|
|
|
Error fieldCString(const char *key, char **val) noexcept;
|
|
|
|
Error fieldCString(const char *key, char **val, std::size_t buffLen) noexcept;
|
|
|
|
Error field(const char *key, UUID *val) noexcept;
|
|
|
|
/**
|
|
* Reads an array length from the current location in the buffer.
|
|
* @param pass indicates that the parsing should iterate past the array length
|
|
*/
|
|
Result<std::size_t> arrayLength(const char *key, bool pass = true) noexcept;
|
|
|
|
/**
|
|
* Reads an string length from the current location in the buffer.
|
|
*/
|
|
[[nodiscard]]
|
|
std::size_t stringLength(const char *name) noexcept;
|
|
|
|
template<typename T = void>
|
|
constexpr ox::Error setTypeInfo() noexcept {
|
|
return {};
|
|
}
|
|
|
|
template<typename T = void>
|
|
constexpr ox::Error setTypeInfo(const char*) noexcept {
|
|
return {};
|
|
}
|
|
|
|
template<typename T = void>
|
|
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}) noexcept {
|
|
return {};
|
|
}
|
|
|
|
template<typename T = void>
|
|
constexpr ox::Error setTypeInfo(const char*, int, const Vector<String>& = {}, std::size_t = {}) noexcept {
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* Returns a OrganicClawReader to parse a child object.
|
|
*/
|
|
[[nodiscard]]
|
|
OrganicClawReader child(const char *key, int unionIdx = -1) noexcept;
|
|
|
|
// compatibility stub
|
|
constexpr void nextField() noexcept {}
|
|
|
|
[[nodiscard]]
|
|
bool fieldPresent(const char *key) noexcept;
|
|
|
|
[[nodiscard]]
|
|
int whichFieldPresent(const char *name, const ModelUnion &u) const noexcept;
|
|
|
|
[[nodiscard]]
|
|
static constexpr auto opType() noexcept {
|
|
return OpType::Read;
|
|
}
|
|
|
|
private:
|
|
[[nodiscard]]
|
|
Json::Value &value(const char *key) noexcept;
|
|
|
|
[[nodiscard]]
|
|
bool targetValid() const noexcept;
|
|
|
|
};
|
|
|
|
template<typename T>
|
|
Error OrganicClawReader::field(const char *key, T *val) noexcept {
|
|
auto err = ox::Error(0);
|
|
try {
|
|
if constexpr (is_integer_v<T>) {
|
|
if (targetValid()) {
|
|
auto const&jv = value(key);
|
|
auto const rightType = sizeof(T) == 8 ?
|
|
(ox::is_signed_v<T> ? jv.isInt64() : jv.isUInt64()) :
|
|
(ox::is_signed_v<T> ? jv.isInt() : jv.isUInt());
|
|
if (jv.empty()) {
|
|
*val = 0;
|
|
} else if (rightType) {
|
|
*val = static_cast<T>(jv.asUInt());
|
|
} else {
|
|
err = ox::Error(1, "Type mismatch");
|
|
}
|
|
}
|
|
} else if constexpr (isVector_v<T>) {
|
|
const auto&srcVal = value(key);
|
|
const auto srcSize = srcVal.size();
|
|
OX_RETURN_ERROR(ox::resizeVector(*val, srcSize));
|
|
err = field(key, val->data(), val->size());
|
|
} else if constexpr (isArray_v<T>) {
|
|
const auto&srcVal = value(key);
|
|
const auto srcSize = srcVal.size();
|
|
if (srcSize > val->size()) {
|
|
err = ox::Error(1, "Input array is too long");
|
|
} else {
|
|
err = field(key, val->data(), val->size());
|
|
}
|
|
} else if (targetValid()) {
|
|
const auto&jv = value(key);
|
|
if (jv.empty() || jv.isObject()) {
|
|
auto reader = child(key);
|
|
ModelHandlerInterface handler(&reader);
|
|
err = model(&handler, val);
|
|
} else {
|
|
err = ox::Error(1, "Type mismatch");
|
|
}
|
|
}
|
|
} catch (Json::LogicError const&) {
|
|
err = ox::Error(1, "error reading JSON data");
|
|
}
|
|
++m_fieldIt;
|
|
return err;
|
|
}
|
|
|
|
template<typename U, bool force>
|
|
Error OrganicClawReader::field(const char *key, UnionView<U, force> val) noexcept {
|
|
auto err = ox::Error(0);
|
|
if (targetValid()) {
|
|
const auto &jv = value(key);
|
|
if (jv.empty() || jv.isObject()) {
|
|
auto reader = child(key, val.idx());
|
|
ModelHandlerInterface handler(&reader);
|
|
err = model(&handler, val.get());
|
|
} else {
|
|
err = ox::Error(1, "Type mismatch");
|
|
}
|
|
}
|
|
++m_fieldIt;
|
|
return err;
|
|
}
|
|
|
|
template<std::size_t L>
|
|
Error OrganicClawReader::field(const char *key, BasicString<L> *val) noexcept {
|
|
auto err = ox::Error(0);
|
|
if (targetValid()) {
|
|
const auto &jv = value(key);
|
|
if (jv.empty()) {
|
|
*val = BasicString<L>{};
|
|
} else if (jv.isString()) {
|
|
*val = jv.asString().c_str();
|
|
} else {
|
|
err = ox::Error(1, "Type mismatch");
|
|
}
|
|
}
|
|
++m_fieldIt;
|
|
return err;
|
|
}
|
|
|
|
template<std::size_t L>
|
|
Error OrganicClawReader::field(const char *key, IString<L> *val) noexcept {
|
|
auto err = ox::Error(0);
|
|
if (targetValid()) {
|
|
const auto &jv = value(key);
|
|
if (jv.empty()) {
|
|
*val = IString<L>{};
|
|
} else if (jv.isString()) {
|
|
*val = jv.asString().c_str();
|
|
} else {
|
|
err = ox::Error(1, "Type mismatch");
|
|
}
|
|
}
|
|
++m_fieldIt;
|
|
return err;
|
|
}
|
|
|
|
// array handler
|
|
template<typename T>
|
|
Error OrganicClawReader::field(const char *key, T *val, std::size_t valLen) noexcept {
|
|
const auto &srcVal = value(key);
|
|
if (!srcVal.isNull() && !srcVal.isArray()) {
|
|
return ox::Error(1, "Type mismatch");
|
|
}
|
|
auto srcSize = srcVal.size();
|
|
if (srcSize > valLen) {
|
|
return ox::Error(1);
|
|
}
|
|
OrganicClawReader r(srcVal);
|
|
ModelHandlerInterface handler{&r};
|
|
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
|
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
|
OX_RETURN_ERROR(handler.field("", &val[i]));
|
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
|
}
|
|
return ox::Error(0);
|
|
}
|
|
|
|
template<typename T>
|
|
Error OrganicClawReader::field(const char *key, HashMap<String, T> *val) noexcept {
|
|
const auto &srcVal = value(key);
|
|
if (!srcVal.isObject()) {
|
|
return ox::Error(1, "Type mismatch");
|
|
}
|
|
auto keys = srcVal.getMemberNames();
|
|
auto srcSize = srcVal.size();
|
|
OrganicClawReader r(srcVal);
|
|
ModelHandlerInterface handler{&r};
|
|
for (decltype(srcSize) i = 0; i < srcSize; ++i) {
|
|
const auto k = keys[i].c_str();
|
|
OX_RETURN_ERROR(handler.field(k, &val->operator[](k)));
|
|
}
|
|
return ox::Error(0);
|
|
}
|
|
|
|
Error readOC(BufferView buff, auto &val) noexcept {
|
|
// OrganicClawReader constructor can throw, but readOC should return its errors.
|
|
try {
|
|
Json::Value doc;
|
|
Json::CharReaderBuilder parserBuilder;
|
|
auto parser = UniquePtr<Json::CharReader>(parserBuilder.newCharReader());
|
|
OX_ALLOW_UNSAFE_BUFFERS_BEGIN
|
|
if (!parser->parse(buff.data(), buff.data() + buff.size(), &doc, nullptr)) {
|
|
OX_ALLOW_UNSAFE_BUFFERS_END
|
|
return ox::Error(1, "Could not parse JSON");
|
|
}
|
|
OrganicClawReader reader(buff.data(), buff.size());
|
|
ModelHandlerInterface handler(&reader);
|
|
return model(&handler, &val);
|
|
} catch (const Error &err) {
|
|
return err;
|
|
} catch (...) {
|
|
return ox::Error(1, "Unknown Error");
|
|
}
|
|
}
|
|
|
|
template<typename T>
|
|
Result<T> readOC(BufferView buff) noexcept {
|
|
Result<T> val;
|
|
OX_RETURN_ERROR(readOC(buff, val.value));
|
|
return val;
|
|
}
|
|
|
|
template<typename T>
|
|
Result<T> readOC(ox::StringView json) noexcept {
|
|
return readOC<T>(ox::BufferView{json.data(), json.len()});
|
|
}
|
|
|
|
}
|