Compare commits

..

2 Commits

2 changed files with 271 additions and 0 deletions

View File

@@ -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
View 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