238 lines
7.8 KiB
C++
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
|