mcc_keyvalue.h: developing ...
This commit is contained in:
@@ -355,6 +355,9 @@ if(BUILD_TESTS)
|
|||||||
|
|
||||||
add_executable(mcc_pcm_z1000_test tests/mcc_pcm_z1000_test.cpp)
|
add_executable(mcc_pcm_z1000_test tests/mcc_pcm_z1000_test.cpp)
|
||||||
target_link_libraries(mcc_pcm_z1000_test PRIVATE ${PROJECT_NAME})
|
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()
|
else()
|
||||||
# This is just a stub to allow access to the path and library settings for the ${PROJECT_NAME} target during development
|
# 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)
|
add_executable(just_stub EXCLUDE_FROM_ALL main.cpp)
|
||||||
|
|||||||
@@ -407,6 +407,5 @@ protected:
|
|||||||
|
|
||||||
|
|
||||||
static_assert(mcc_coord_epoch_c<MccCelestialCoordEpoch>, "!!!");
|
static_assert(mcc_coord_epoch_c<MccCelestialCoordEpoch>, "!!!");
|
||||||
|
static_assert(requires(MccCelestialCoordEpoch ep) { ep = std::chrono::system_clock::now(); });
|
||||||
|
|
||||||
} // namespace mcc::impl
|
} // namespace mcc::impl
|
||||||
|
|||||||
@@ -11,6 +11,8 @@
|
|||||||
****************************************************************************************/
|
****************************************************************************************/
|
||||||
|
|
||||||
|
|
||||||
|
#include <unordered_map>
|
||||||
|
|
||||||
#include "mcc_constants.h"
|
#include "mcc_constants.h"
|
||||||
#include "mcc_deserializer.h"
|
#include "mcc_deserializer.h"
|
||||||
#include "mcc_serializer.h"
|
#include "mcc_serializer.h"
|
||||||
@@ -19,7 +21,13 @@
|
|||||||
namespace mcc::impl
|
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
|
} // namespace mcc::impl
|
||||||
|
|
||||||
@@ -59,6 +67,8 @@ struct MccKeyValueHolderCategory : public std::error_category {
|
|||||||
return "incompatible type";
|
return "incompatible type";
|
||||||
case MccKeyValueHolderErrorCode::ERROR_DESERIAL:
|
case MccKeyValueHolderErrorCode::ERROR_DESERIAL:
|
||||||
return "deserialization error";
|
return "deserialization error";
|
||||||
|
case MccKeyValueHolderErrorCode::ERROR_SERIAL:
|
||||||
|
return "serialization error";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN";
|
return "UNKNOWN";
|
||||||
}
|
}
|
||||||
@@ -85,7 +95,7 @@ concept mcc_variant_valid_type_c = requires { !std::is_array_v<T> && !std::is_vo
|
|||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
concept mcc_keyvalue_record_c = requires(T 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)>;
|
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>
|
template <typename T>
|
||||||
std::expected<T, std::error_code> getValue(std::string_view key) const
|
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>
|
template <typename T>
|
||||||
std::error_code setValue(std::string_view key, const T& value)
|
std::error_code setValue(std::string_view key, const T& value)
|
||||||
{
|
{
|
||||||
return forKey(key, [value]<typename VT>(VT& val) -> std::error_code {
|
using r_t = std::invoke_result_t<decltype(std::chrono::system_clock::now)>;
|
||||||
if constexpr (std::convertible_to<T, VT>) {
|
// 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;
|
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;
|
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,
|
template <traits::mcc_input_char_range R,
|
||||||
std::ranges::input_range RecDelimT = std::string_view,
|
traits::mcc_input_char_range RecDelimT = std::string_view,
|
||||||
mcc_serialization_params_c SerParamsT = mcc_serialization_params_t>
|
mcc_serialization_params_c SerParamsT = mcc_serialization_params_t>
|
||||||
std::error_code fromCharRange(const R& buffer,
|
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{})
|
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<R>>) { // char*, const char*
|
||||||
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
|
if constexpr (std::is_array_v<std::decay_t<RecDelimT>>) {
|
||||||
@@ -193,50 +239,134 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::error_code ec{};
|
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));
|
auto recs = std::views::split(buffer, std::move(rec_delim));
|
||||||
|
|
||||||
for (auto const& el : recs) {
|
for (auto const& el : recs) {
|
||||||
rec = mcc::utils::trimSpaces(el, utils::TrimType::TRIM_LEFT);
|
rec = mcc::utils::trimSpaces(el, utils::TrimType::TRIM_LEFT);
|
||||||
|
|
||||||
if (rec.size()) {
|
if (rec.size()) {
|
||||||
auto found = std::ranges::search(rec, COMMENT_SEQ);
|
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());
|
rec = std::string_view(rec.begin(), found.begin());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (rec.size()) {
|
if (rec.size()) {
|
||||||
found = std::ranges::search(rec, KEY_VALUE_DELIM);
|
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);
|
key = trimSpaces(std::string_view(rec.begin(), found.begin()), utils::TrimType::TRIM_RIGHT);
|
||||||
value = std::string_view(found.end(), rec.end());
|
value = std::string_view(found.end(), rec.end());
|
||||||
|
|
||||||
ec = forKey(key, [value, &ser_params]<typename VT>(VT& v) {
|
key_hash = utils::FNV1aHash(key);
|
||||||
auto err = MccDeserializer<VT>(value, v, ser_params);
|
|
||||||
|
// 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) {
|
if (err) {
|
||||||
return MccKeyValueHolderErrorCode::ERROR_DESERIAL;
|
return MccKeyValueHolderErrorCode::ERROR_DESERIAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return MccKeyValueHolderErrorCode::ERROR_OK;
|
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)
|
} // just comment string starting from the beginning, just skip it (no error)
|
||||||
|
|
||||||
} // empty record, just skip it (no error)
|
} else { // empty record, just skip it (no error)
|
||||||
|
head_comm.push_back({el.begin(), el.end()});
|
||||||
|
std::println("EMPTY COMM: <{}>", head_comm.back());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (ec) {
|
return ec;
|
||||||
break;
|
}
|
||||||
|
|
||||||
|
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;
|
return ec;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
DESCR_T _keyValue;
|
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)!!!
|
// 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
|
} // namespace mcc::impl
|
||||||
62
tests/mcc_keyvalue_test.cpp
Normal file
62
tests/mcc_keyvalue_test.cpp
Normal 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;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user