Compare commits
2 Commits
af0268218a
...
537207cd16
| Author | SHA1 | Date | |
|---|---|---|---|
| 537207cd16 | |||
| f314c6a2c5 |
@@ -253,6 +253,7 @@ set(MCC_SRC
|
||||
include/mcc/mcc_deserializer.h
|
||||
include/mcc/mcc_serializer.h
|
||||
include/mcc/mcc_generic_mount.h
|
||||
include/mcc/mcc_keyvalue.h
|
||||
)
|
||||
|
||||
if(USE_SPDLOG)
|
||||
|
||||
270
include/mcc/mcc_keyvalue.h
Normal file
270
include/mcc/mcc_keyvalue.h
Normal file
@@ -0,0 +1,270 @@
|
||||
#pragma once
|
||||
|
||||
|
||||
/****************************************************************************************
|
||||
* *
|
||||
* MOUNT CONTROL COMPONENTS LIBRARY *
|
||||
* *
|
||||
* *
|
||||
* IMPLEMENTATION OF KEY-VALUE PAIRS HOLDER *
|
||||
* *
|
||||
****************************************************************************************/
|
||||
|
||||
|
||||
#include "mcc_constants.h"
|
||||
#include "mcc_deserializer.h"
|
||||
#include "mcc_serializer.h"
|
||||
#include "mcc_utils.h"
|
||||
|
||||
namespace mcc::impl
|
||||
{
|
||||
|
||||
enum class MccKeyValueHolderErrorCode : int { ERROR_OK, ERROR_INVALID_KEY, ERROR_INCOMPATIBLE_TYPE, ERROR_DESERIAL };
|
||||
|
||||
} // namespace mcc::impl
|
||||
|
||||
|
||||
namespace std
|
||||
{
|
||||
|
||||
template <>
|
||||
class is_error_code_enum<mcc::impl::MccKeyValueHolderErrorCode> : public true_type
|
||||
{
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace mcc::impl
|
||||
{
|
||||
|
||||
// error category
|
||||
struct MccKeyValueHolderCategory : public std::error_category {
|
||||
MccKeyValueHolderCategory() : std::error_category() {}
|
||||
|
||||
const char* name() const noexcept
|
||||
{
|
||||
return "MCC-KEYVALUEHOLDER-ERR-CATEGORY";
|
||||
}
|
||||
|
||||
std::string message(int ec) const
|
||||
{
|
||||
MccKeyValueHolderErrorCode err = static_cast<MccKeyValueHolderErrorCode>(ec);
|
||||
|
||||
switch (err) {
|
||||
case MccKeyValueHolderErrorCode::ERROR_OK:
|
||||
return "OK";
|
||||
case MccKeyValueHolderErrorCode::ERROR_INVALID_KEY:
|
||||
return "invalid key value";
|
||||
case MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE:
|
||||
return "incompatible type";
|
||||
case MccKeyValueHolderErrorCode::ERROR_DESERIAL:
|
||||
return "deserialization error";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static const MccKeyValueHolderCategory& get()
|
||||
{
|
||||
static const MccKeyValueHolderCategory constInst;
|
||||
return constInst;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline std::error_code make_error_code(MccKeyValueHolderErrorCode ec)
|
||||
{
|
||||
return std::error_code(static_cast<int>(ec), MccKeyValueHolderCategory::get());
|
||||
}
|
||||
|
||||
|
||||
// to follow std::variant requirements (not references, not array, not void)
|
||||
template <typename T>
|
||||
concept mcc_variant_valid_type_c = requires { !std::is_array_v<T> && !std::is_void_v<T> && !std::is_reference_v<T>; };
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_keyvalue_record_c = requires(T t) {
|
||||
requires std::same_as<decltype(t.key), std::string_view>;
|
||||
requires mcc_variant_valid_type_c<decltype(t.value)>;
|
||||
};
|
||||
|
||||
|
||||
template <typename T>
|
||||
concept mcc_keyvalue_desc_c = requires(T t) { []<mcc_keyvalue_record_c... Ts>(std::tuple<Ts...>) {}(t); };
|
||||
|
||||
|
||||
namespace constants
|
||||
{
|
||||
|
||||
static constexpr char MCC_KV_COMMENT_SEQ_ARR[] = "#";
|
||||
static constexpr char MCC_KV_KEY_VALUE_DELIM_SEQ_ARR[] = "=";
|
||||
static constexpr char MCC_KV_VALUE_ARRAY_DELIM_SEQ_ARR[] = ",";
|
||||
|
||||
} // namespace constants
|
||||
|
||||
|
||||
template <mcc_keyvalue_desc_c DESCR_T,
|
||||
const char* COMM_SEQ = constants::MCC_KV_COMMENT_SEQ_ARR,
|
||||
const char* KV_DELIM = constants::MCC_KV_KEY_VALUE_DELIM_SEQ_ARR,
|
||||
const char* ARR_DELIM = constants::MCC_KV_VALUE_ARRAY_DELIM_SEQ_ARR>
|
||||
class MccKeyValueHolder
|
||||
{
|
||||
public:
|
||||
static constexpr std::string_view COMMENT_SEQ{COMM_SEQ == nullptr ? constants::MCC_KV_COMMENT_SEQ_ARR
|
||||
: COMM_SEQ[0] == '\0' ? constants::MCC_KV_COMMENT_SEQ_ARR
|
||||
: COMM_SEQ};
|
||||
|
||||
static constexpr std::string_view KEY_VALUE_DELIM{KV_DELIM == nullptr ? constants::MCC_KV_KEY_VALUE_DELIM_SEQ_ARR
|
||||
: KV_DELIM[0] == '\0' ? constants::MCC_KV_KEY_VALUE_DELIM_SEQ_ARR
|
||||
: KV_DELIM};
|
||||
|
||||
static constexpr std::string_view VALUE_ARRAY_DELIM{
|
||||
ARR_DELIM == nullptr ? constants::MCC_KV_VALUE_ARRAY_DELIM_SEQ_ARR
|
||||
: ARR_DELIM[0] == '\0' ? constants::MCC_KV_VALUE_ARRAY_DELIM_SEQ_ARR
|
||||
: ARR_DELIM};
|
||||
|
||||
|
||||
MccKeyValueHolder(DESCR_T desc) : _keyValue(desc)
|
||||
{
|
||||
[this]<size_t... I>(std::index_sequence<I...>) {
|
||||
((_hashes[I] = utils::FNV1aHash(std::get<I>(_keyValue).key)), ...);
|
||||
}(std::make_index_sequence<std::tuple_size_v<DESCR_T>>());
|
||||
}
|
||||
|
||||
|
||||
template <typename T>
|
||||
std::expected<T, std::error_code> getValue(std::string_view key) const
|
||||
{
|
||||
T v;
|
||||
auto err = forKey(key, [&v]<typename VT>(const VT& val) -> std::error_code {
|
||||
if constexpr (std::convertible_to<VT, T>) {
|
||||
v = val;
|
||||
} else if constexpr (std::constructible_from<T, VT>) {
|
||||
v = T(val);
|
||||
} else {
|
||||
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
|
||||
}
|
||||
|
||||
return MccKeyValueHolderErrorCode::ERROR_OK;
|
||||
});
|
||||
|
||||
if (err) {
|
||||
return std::unexpected(err);
|
||||
} else {
|
||||
return v;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::error_code setValue(std::string_view key, const T& value)
|
||||
{
|
||||
return forKey(key, [value]<typename VT>(VT& val) -> std::error_code {
|
||||
if constexpr (std::convertible_to<T, VT>) {
|
||||
val = value;
|
||||
} else if constexpr (std::constructible_from<VT, T>) {
|
||||
val = VT(value);
|
||||
} else {
|
||||
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
|
||||
}
|
||||
|
||||
return MccKeyValueHolderErrorCode::ERROR_OK;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
template <std::ranges::contiguous_range R,
|
||||
std::ranges::input_range RecDelimT = std::string_view,
|
||||
mcc_serialization_params_c SerParamsT = mcc_serialization_params_t>
|
||||
std::error_code fromCharRange(const R& buffer,
|
||||
RecDelimT rec_delim = std::string_view("\n"),
|
||||
SerParamsT const& ser_params = mcc_serialization_params_t{})
|
||||
{
|
||||
if constexpr (std::is_array_v<std::decay_t<R>>) { // char*, const char*
|
||||
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
|
||||
return fromCharRange(std::string_view{buffer}, std::string_view(rec_delim));
|
||||
} else {
|
||||
return fromCharRange(std::string_view{buffer}, std::move(rec_delim));
|
||||
}
|
||||
} else {
|
||||
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
|
||||
return fromCharRange(buffer, std::string_view(rec_delim));
|
||||
}
|
||||
}
|
||||
|
||||
std::error_code ec{};
|
||||
std::string_view rec, key, value;
|
||||
|
||||
|
||||
auto recs = std::views::split(buffer, std::move(rec_delim));
|
||||
for (auto const& el : recs) {
|
||||
rec = mcc::utils::trimSpaces(el, utils::TrimType::TRIM_LEFT);
|
||||
|
||||
if (rec.size()) {
|
||||
auto found = std::ranges::search(rec, COMMENT_SEQ);
|
||||
if (found.begin() != rec.end()) { // there was the comment sequence in record
|
||||
rec = std::string_view(rec.begin(), found.begin());
|
||||
}
|
||||
|
||||
if (rec.size()) {
|
||||
found = std::ranges::search(rec, KEY_VALUE_DELIM);
|
||||
if (found.begin() != rec.begin()) { // ignore an empty key
|
||||
key = trimSpaces(std::string_view(rec.begin(), found.begin()), utils::TrimType::TRIM_RIGHT);
|
||||
value = std::string_view(found.end(), rec.end());
|
||||
|
||||
ec = forKey(key, [value, &ser_params]<typename VT>(VT& v) {
|
||||
auto err = MccDeserializer<VT>(value, v, ser_params);
|
||||
if (err) {
|
||||
return MccKeyValueHolderErrorCode::ERROR_DESERIAL;
|
||||
}
|
||||
|
||||
return MccKeyValueHolderErrorCode::ERROR_OK;
|
||||
});
|
||||
}
|
||||
} // just comment string starting from the beginning, just skip it (no error)
|
||||
|
||||
} // empty record, just skip it (no error)
|
||||
|
||||
if (ec) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return ec;
|
||||
}
|
||||
|
||||
protected:
|
||||
DESCR_T _keyValue;
|
||||
|
||||
std::array<size_t, std::tuple_size_v<DESCR_T>> _hashes;
|
||||
|
||||
//
|
||||
// NOTE: deduced this is needed here to use "forKey" method in getter and setter (const and non-const contexts)!!!
|
||||
//
|
||||
|
||||
std::error_code forKey(this auto&& self, std::string_view key, auto&& func)
|
||||
{
|
||||
return std::forward<decltype(self)>(self).template forHash<0>(utils::FNV1aHash(key),
|
||||
std::forward<decltype(func)>(func));
|
||||
}
|
||||
|
||||
|
||||
template <size_t I = 0>
|
||||
std::error_code forHash(this auto&& self, size_t hash, auto&& func)
|
||||
{
|
||||
if constexpr (I < std::tuple_size_v<DESCR_T>) {
|
||||
if (hash == std::forward<decltype(self)>(self)._hashes[I]) {
|
||||
return std::forward<decltype(func)>(func)(
|
||||
std::get<I>(std::forward<decltype(self)>(self)._keyValue).value);
|
||||
} else {
|
||||
return std::forward<decltype(self)>(self).template forHash<I + 1>(hash,
|
||||
std::forward<decltype(func)>(func));
|
||||
}
|
||||
}
|
||||
|
||||
return MccKeyValueHolderErrorCode::ERROR_INVALID_KEY;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
} // namespace mcc::impl
|
||||
Reference in New Issue
Block a user