Files
snipplib/include/snipplib/utils/snplib_keyvalue_expr.h
2026-07-27 17:13:28 +03:00

238 lines
7.8 KiB
C++

#pragma once
#include "../concepts/snplib_concepts.h"
#include "../serialization/snplib_serialization.h"
namespace snplib
{
/* A SIMPLE KEY-VALUE CHAR-RANGE EXPRESSION PARSER AND MANIPULATOR */
template <typename T>
concept snplib_serializable_c = requires { snplib_serializer_t<std::remove_cvref_t<T>>{}; };
template <snplib_char_range_c CharRangeT>
requires(std::ranges::viewable_range<CharRangeT> && !std::ranges::borrowed_range<CharRangeT>)
class snplib_keyvalue_expr_t
{
public:
static constexpr bool mutableContent = snplib_output_char_range_c<CharRangeT>;
static constexpr bool contiguousContent =
std::ranges::viewable_range<CharRangeT> && std::ranges::contiguous_range<CharRangeT>;
typedef std::error_code error_t;
static constexpr std::string_view DEFAULT_KEYVALUE_DELIM_SEQ{" "};
static constexpr std::string_view DEFAULT_VALSEQ_DELIM_SEQ{" "};
struct config_t {
std::string_view keyval_delim{DEFAULT_KEYVALUE_DELIM_SEQ};
std::string_view valseq_delim{DEFAULT_VALSEQ_DELIM_SEQ};
};
constexpr snplib_keyvalue_expr_t(CharRangeT& bytes, config_t cfg = config_t{}) : _byteSeq(bytes), _config(cfg)
{
fromCharRange(bytes, cfg);
}
virtual ~snplib_keyvalue_expr_t() = default;
// parse from char range
error_t fromCharRange(CharRangeT& bytes, config_t cfg = config_t{})
{
_byteSeq = bytes;
_config = cfg;
_isValid = false;
// key and value
auto bs = snplib_trim_spaces_as_view(bytes, TrimType::TRIM_LEFT);
auto found = std::ranges::search(bs, _config.keyval_delim);
if (found.empty()) { // no value
_key = snplib_trim_spaces_as_view(bs, TrimType::TRIM_RIGHT);
_value = decltype(_value)();
} else {
_key = snplib_trim_spaces_as_view(decltype(_key)(bs.begin(), found.begin()), TrimType::TRIM_RIGHT);
// do not delete space at the end of value-string!
_value = decltype(_value)(found.end(), bs.end());
}
// split value to elements
if (!_value.empty()) {
_valueSeq = snplib_split_char_range<decltype(_valueSeq)>(_value, _config.valseq_delim);
}
if (_key.empty() && !mutableContent) { // at least key must be present
_isValid = false;
return std::make_error_code(std::errc::invalid_argument);
} else { // for mutable content an empty key is allowed!
_isValid = true;
}
return {};
}
bool isValid() const
{
return _isValid;
}
template <snplib_char_range_c R>
constexpr R key() const
{
return snplib_transform_char_range<R>(_key);
}
constexpr auto key() const
{
return _key;
}
template <snplib_char_range_c R>
constexpr R value() const
{
return snplib_transform_char_range<R>(_value);
}
constexpr auto value() const
{
return _value;
}
template <std::ranges::range OR>
requires(std::ranges::output_range<OR, std::ranges::range_value_t<OR>> &&
snplib_char_range_c<std::ranges::range_value_t<OR>>)
OR valueSeq(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
using el_t = std::ranges::range_value_t<OR>;
OR res;
if (N == 0 || _valueSeq.empty() || start >= _valueSeq.size()) {
return res;
}
auto elems = _valueSeq | std::views::drop(start) | std::views::take(N);
if constexpr (std::same_as<OR, decltype(_valueSeq)>) {
std::ranges::copy(elems, std::back_inserter(res));
} else {
std::ranges::for_each(elems, [&res](auto const& e) {
if constexpr (std::constructible_from<el_t, std::string_view::const_iterator,
std::string_view::const_iterator>) {
// std::back_inserter(el_t{e.cbegin(), e.cend()}, std::back_inserter(res));
std::back_inserter(res) = el_t{e.cbegin(), e.cend()};
} else if constexpr (std::constructible_from<el_t, std::string_view>) {
// std::back_inserter(el_t{e}, std::back_inserter(res));
std::back_inserter(res) = el_t{e};
} else {
static_assert(false, "Incompatible types: cannot create output element!");
}
});
}
return res;
}
auto valueSeq(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
// std::vector<std::string_view> valueSeq(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
return valueSeq<decltype(_valueSeq)>(start, N);
}
template <snplib_char_range_c OR>
OR valueJoinSeq(size_t start = 0, size_t N = std::numeric_limits<size_t>::max()) const
{
if (N == 0 || _valueSeq.empty() || start >= _valueSeq.size()) {
return OR();
}
auto last_idx = start + N - 1;
if (last_idx >= _valueSeq.size()) {
last_idx = _valueSeq.size() - 1;
}
if constexpr (std::constructible_from<OR, std::string_view::const_iterator, std::string_view::const_iterator>) {
return OR{_valueSeq[start].cbegin(), _valueSeq[last_idx].cend()};
} else if constexpr (std::constructible_from<OR, std::string_view>) {
return OR{std::string_view{_valueSeq[start].begin(), _valueSeq[last_idx].end()}};
} else if constexpr (snplib_output_char_range_c<OR>) {
OR res;
std::copy(_valueSeq[start].begin(), _valueSeq[last_idx].end(), std::back_inserter(res));
return res;
} else {
static_assert(false, "Incompatible types: cannot create output range!");
}
}
template <snplib_serializable_c KeyT, snplib_serialization_params_c SParT = snplib_serialization_params_t>
requires mutableContent
error_t setKey(KeyT&& key, SParT const& sparams = snplib_serialization_params_t{})
{
_byteSeq.clear();
auto err = snplib_serializer_t<std::remove_cvref_t<KeyT>>{}(_byteSeq, std::forward<KeyT>(key), sparams);
if (err) {
return snplib_deduced_error(err, std::make_error_code(std::errc::invalid_argument));
}
return {};
}
template <snplib_serializable_c ValueT, snplib_serialization_params_c SParT = snplib_serialization_params_t>
requires mutableContent
error_t setValue(ValueT&& value, SParT sparams = snplib_serialization_params_t{})
{
auto err = snplib_serializer_t<std::remove_cvref_t<ValueT>>{}(_byteSeq, std::forward<ValueT>(value), sparams);
if (err) {
return snplib_deduced_error(err, std::make_error_code(std::errc::invalid_argument));
}
return {};
}
template <snplib_serializable_c KeyT,
snplib_serializable_c ValueT,
snplib_serialization_params_c SParT = snplib_serialization_params_t>
requires mutableContent
error_t setKeyValue(KeyT&& key, ValueT&& value, SParT sparams = snplib_serialization_params_t{})
{
auto err = setKey(std::forward<KeyT>(key), sparams);
if (err) {
return err;
}
std::ranges::copy(_config.keyval_delim, std::back_inserter(_byteSeq));
sparams.elem_delim = _config.valseq_delim;
return setValue(std::forward<ValueT>(value), sparams);
}
protected:
CharRangeT& _byteSeq;
std::conditional_t<contiguousContent, std::string_view, std::ranges::subrange<typename CharRangeT::iterator>>
_key{};
std::conditional_t<contiguousContent, std::string_view, std::ranges::subrange<typename CharRangeT::iterator>>
_value{};
std::vector<decltype(_value)> _valueSeq{};
config_t _config{};
bool _isValid{false};
};
} // namespace snplib