mcc_keyvalue.h: developing ...

This commit is contained in:
2026-05-16 15:08:09 +03:00
parent 537207cd16
commit 3dbe4c12b7
4 changed files with 223 additions and 24 deletions

View File

@@ -355,6 +355,9 @@ if(BUILD_TESTS)
add_executable(mcc_pcm_z1000_test tests/mcc_pcm_z1000_test.cpp)
target_link_libraries(mcc_pcm_z1000_test PRIVATE ${PROJECT_NAME})
add_executable(mcc_keyvalue_test tests/mcc_keyvalue_test.cpp)
target_link_libraries(mcc_keyvalue_test PRIVATE ${PROJECT_NAME})
else()
# This is just a stub to allow access to the path and library settings for the ${PROJECT_NAME} target during development
add_executable(just_stub EXCLUDE_FROM_ALL main.cpp)

View File

@@ -407,6 +407,5 @@ protected:
static_assert(mcc_coord_epoch_c<MccCelestialCoordEpoch>, "!!!");
static_assert(requires(MccCelestialCoordEpoch ep) { ep = std::chrono::system_clock::now(); });
} // namespace mcc::impl

View File

@@ -11,6 +11,8 @@
****************************************************************************************/
#include <unordered_map>
#include "mcc_constants.h"
#include "mcc_deserializer.h"
#include "mcc_serializer.h"
@@ -19,7 +21,13 @@
namespace mcc::impl
{
enum class MccKeyValueHolderErrorCode : int { ERROR_OK, ERROR_INVALID_KEY, ERROR_INCOMPATIBLE_TYPE, ERROR_DESERIAL };
enum class MccKeyValueHolderErrorCode : int {
ERROR_OK,
ERROR_INVALID_KEY,
ERROR_INCOMPATIBLE_TYPE,
ERROR_DESERIAL,
ERROR_SERIAL
};
} // namespace mcc::impl
@@ -59,6 +67,8 @@ struct MccKeyValueHolderCategory : public std::error_category {
return "incompatible type";
case MccKeyValueHolderErrorCode::ERROR_DESERIAL:
return "deserialization error";
case MccKeyValueHolderErrorCode::ERROR_SERIAL:
return "serialization error";
default:
return "UNKNOWN";
}
@@ -85,7 +95,7 @@ concept mcc_variant_valid_type_c = requires { !std::is_array_v<T> && !std::is_vo
template <typename T>
concept mcc_keyvalue_record_c = requires(T t) {
requires std::same_as<decltype(t.key), std::string_view>;
requires std::same_as<decltype(t.key), const std::string_view>; // immutable key!!!
requires mcc_variant_valid_type_c<decltype(t.value)>;
};
@@ -133,6 +143,12 @@ public:
}
template <typename T, std::ranges::contiguous_range R>
std::expected<T, std::error_code> getValue(R const& key) const
{
return getValue<T>(std::string_view{key});
}
template <typename T>
std::expected<T, std::error_code> getValue(std::string_view key) const
{
@@ -156,29 +172,59 @@ public:
}
}
template <std::ranges::contiguous_range R, typename T>
std::error_code setValue(R const& key, const T& value)
{
return setValue<T>(std::string_view{key}, value);
}
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>) {
using r_t = std::invoke_result_t<decltype(std::chrono::system_clock::now)>;
// static_assert(std::convertible_to<r_t, MccCelestialCoordEpoch>);
// static_assert(std::assignable_from<MccCelestialCoordEpoch, r_t>);
// static_assert(std::constructible_from<MccCelestialCoordEpoch, r_t>);
// static_assert(std::destructible<MccCelestialCoordEpoch>);
return forKey(key, [value, key]<typename VT>(VT& val) -> std::error_code {
if constexpr (requires(std::decay_t<VT> v) { v = value; }) {
val = value;
} else if constexpr (std::constructible_from<VT, T>) {
val = VT(value);
} else {
return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
}
std::println("FORKEY_FUN: {} = {}", key, value);
// if constexpr (std::convertible_to<T, VT>) {
// val = value;
// } else if constexpr (std::constructible_from<VT, T>) {
// val = VT(value);
// } else if constexpr (std::assignable_from<VT, T>) {
// val = value;
// } else {
// return MccKeyValueHolderErrorCode::ERROR_INCOMPATIBLE_TYPE;
// }
return MccKeyValueHolderErrorCode::ERROR_OK;
// if constexpr (std::convertible_to<T, VT>) {
// val = value;
// } else if constexpr (std::constructible_from<VT, T>) {
// val = VT(value);
// } else if constexpr (std::assignable_from<VT, T>) {
// val = 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,
template <traits::mcc_input_char_range R,
traits::mcc_input_char_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"),
RecDelimT rec_delim = std::string_view("\n"), // records delimiter
SerParamsT const& ser_params = mcc_serialization_params_t{})
requires std::ranges::contiguous_range<R>
{
if constexpr (std::is_array_v<std::decay_t<R>>) { // char*, const char*
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
@@ -193,50 +239,134 @@ public:
}
std::error_code ec{};
std::string_view rec, key, value;
std::string_view rec, key, value, inline_comm;
std::vector<std::string> head_comm;
size_t key_hash;
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
if (found.begin() != rec.end()) { // there was the comment sequence in record
if (found.begin() != rec.begin()) { // inline comment
inline_comm =
std::string_view{found.begin() + COMMENT_SEQ.size(), rec.end()}; // mark inline comment
} else { // head comment
head_comm.push_back({found.end(), rec.end()});
std::println("COMM: <{}>", head_comm.back());
}
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
if (found.begin() != rec.begin()) {
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);
key_hash = utils::FNV1aHash(key);
// 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;
// });
ec = forHash(key_hash, [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;
});
if (ec) { // exit in the case of error
break;
}
// save head comment
_headComment[key_hash] = head_comm;
head_comm.clear();
_inlineComment[key_hash] = inline_comm;
} else { // an empty key
ec = MccKeyValueHolderErrorCode::ERROR_INVALID_KEY;
break;
}
} // just comment string starting from the beginning, just skip it (no error)
} // empty record, just skip it (no error)
if (ec) {
break;
} else { // empty record, just skip it (no error)
head_comm.push_back({el.begin(), el.end()});
std::println("EMPTY COMM: <{}>", head_comm.back());
}
}
return ec;
}
template <traits::mcc_output_char_range R,
traits::mcc_input_char_range RecDelimT = std::string_view,
mcc_serialization_params_c SerParamsT = mcc_serialization_params_t>
std::error_code toCharRange(R& output_buffer,
RecDelimT rec_delim = std::string_view{"\n"},
SerParamsT const& ser_params = mcc_serialization_params_t{})
{
std::error_code ec{};
auto write_rec = [&output_buffer, &rec_delim, &ec, &ser_params, this]<size_t I>() {
auto key = std::get<I>(_keyValue).key;
auto val = std::get<I>(_keyValue).value;
using val_t = std::remove_cvref_t<decltype(val)>;
std::string buff;
auto err = MccSerializer<val_t>{}(buff, val, ser_params);
if (err) {
ec = MccKeyValueHolderErrorCode::ERROR_SERIAL;
} else {
size_t hash = _hashes[I];
// write head comment
if (_headComment[hash].size()) {
for (auto const& comm : _headComment[hash]) {
// if (comm.size()) {
std::format_to(std::back_inserter(output_buffer), "{}{}", COMM_SEQ, comm);
// }
std::ranges::copy(rec_delim, std::back_inserter(output_buffer));
}
}
// key and value
std::format_to(std::back_inserter(output_buffer), "{}{}{}{}", rec_delim, key, KEY_VALUE_DELIM, buff);
// inline comment
if (_inlineComment[hash].size()) {
std::format_to(std::back_inserter(output_buffer), " {}{}", COMMENT_SEQ, _inlineComment[hash]);
}
// record delimiter
std::ranges::copy(rec_delim, std::back_inserter(output_buffer));
}
};
[&write_rec]<size_t... Is>(std::index_sequence<Is...>) {
(write_rec.template operator()<Is>(), ...);
}(std::make_index_sequence<std::tuple_size_v<DESCR_T>>());
return ec;
}
protected:
DESCR_T _keyValue;
std::array<size_t, std::tuple_size_v<DESCR_T>> _hashes;
std::array<size_t, std::tuple_size_v<DESCR_T>> _hashes; // key hashes
std::unordered_map<size_t, std::vector<std::string>> _headComment; // comment string/strings before key
std::unordered_map<size_t, std::string> _inlineComment; // inline (after key=value pair) comment
//
// NOTE: deduced this is needed here to use "forKey" method in getter and setter (const and non-const contexts)!!!
@@ -266,5 +396,10 @@ protected:
}
};
template <mcc_variant_valid_type_c T>
struct mcc_simple_kv_record_t {
const std::string_view key;
T value;
};
} // namespace mcc::impl

View File

@@ -0,0 +1,62 @@
#include <print>
#include <mcc/mcc_keyvalue.h>
using namespace mcc::impl;
static auto kv_desc = std::make_tuple(mcc_simple_kv_record_t{"bb", MccAngle{11.5_degs}},
mcc_simple_kv_record_t{"aaa", std::string("AAA")},
mcc_simple_kv_record_t{"cc", MccCelestialCoordEpoch{}});
static std::string STR = R"--(
aaa = dewl_ewkj23+23998
#
# this is angle
bb=1.24534
cc= 2026-05-15T05:53:20.921723918 # date UTC
)--";
int main()
{
// MccKeyValueHolder<decltype(kv_desc)> kv(kv_desc);
MccKeyValueHolder kv(kv_desc);
auto err = kv.fromCharRange(STR);
if (err) {
std::println("ERR = {}", err);
return 1;
}
std::println("{} = {}", "bb", kv.getValue<MccAngle>(std::string{"bb"}).value().sexagesimal());
std::println("{} = {}", "cc", kv.getValue<MccCelestialCoordEpoch>("cc").value().UTC());
err = kv.setValue("cc", std::chrono::system_clock::now());
if (err) {
std::println("ERR = {}", err);
// return 1;
}
err = kv.setValue("aaa", "OK");
if (err) {
std::println("ERR = {}", err);
// return 1;
}
std::println("------------------------------------");
std::string buff;
err = kv.toCharRange(buff);
if (err) {
std::println("ERR = {}", err);
return 1;
}
std::println("{}", buff);
return 0;
}