mirror of
https://github.com/red0124/ssp.git
synced 2025-01-23 13:05:20 +01:00
add trim_left and trim_right, add setup static asserts, update type_traits, refactor some code, add unit tests
This commit is contained in:
parent
ea42948c42
commit
2985027505
19
include/ss/common.hpp
Normal file
19
include/ss/common.hpp
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <vector>
|
||||
|
||||
namespace ss {
|
||||
|
||||
struct none {};
|
||||
|
||||
using string_range = std::pair<const char*, const char*>;
|
||||
using split_data = std::vector<string_range>;
|
||||
|
||||
constexpr inline auto default_delimiter = ",";
|
||||
|
||||
template <bool StringError>
|
||||
inline void assert_string_error_defined() {
|
||||
static_assert(StringError,
|
||||
"'string_error' needs to be enabled to use 'error_msg'");
|
||||
}
|
||||
|
||||
} /* ss */
|
@ -40,19 +40,13 @@ template <typename T>
|
||||
using no_validator_t = typename no_validator<T>::type;
|
||||
|
||||
template <typename... Ts>
|
||||
struct no_validator_tup {
|
||||
using type = typename apply_trait<no_validator, std::tuple<Ts...>>::type;
|
||||
};
|
||||
struct no_validator_tup : apply_trait<no_validator, std::tuple<Ts...>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct no_validator_tup<std::tuple<Ts...>> {
|
||||
using type = typename no_validator_tup<Ts...>::type;
|
||||
};
|
||||
struct no_validator_tup<std::tuple<Ts...>> : no_validator_tup<Ts...> {};
|
||||
|
||||
template <typename T>
|
||||
struct no_validator_tup<std::tuple<T>> {
|
||||
using type = no_validator_t<T>;
|
||||
};
|
||||
struct no_validator_tup<std::tuple<T>> : no_validator<T> {};
|
||||
|
||||
template <typename... Ts>
|
||||
using no_validator_tup_t = typename no_validator_tup<Ts...>::type;
|
||||
@ -62,10 +56,7 @@ using no_validator_tup_t = typename no_validator_tup<Ts...>::type;
|
||||
////////////////
|
||||
|
||||
template <typename... Ts>
|
||||
struct no_void_tup {
|
||||
using type =
|
||||
typename filter_not<std::is_void, no_validator_tup_t<Ts...>>::type;
|
||||
};
|
||||
struct no_void_tup : filter_not<std::is_void, no_validator_tup_t<Ts...>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
using no_void_tup_t = filter_not_t<std::is_void, Ts...>;
|
||||
@ -76,14 +67,11 @@ using no_void_tup_t = filter_not_t<std::is_void, Ts...>;
|
||||
|
||||
// replace 'validators' and remove void from tuple
|
||||
template <typename... Ts>
|
||||
struct no_void_validator_tup {
|
||||
using type = no_validator_tup_t<no_void_tup_t<Ts...>>;
|
||||
};
|
||||
struct no_void_validator_tup : no_validator_tup<no_void_tup_t<Ts...>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
struct no_void_validator_tup<std::tuple<Ts...>> {
|
||||
using type = no_validator_tup_t<no_void_tup_t<Ts...>>;
|
||||
};
|
||||
struct no_void_validator_tup<std::tuple<Ts...>>
|
||||
: no_validator_tup<no_void_tup_t<Ts...>> {};
|
||||
|
||||
template <typename... Ts>
|
||||
using no_void_validator_tup_t = typename no_void_validator_tup<Ts...>::type;
|
||||
@ -131,12 +119,12 @@ public:
|
||||
no_void_validator_tup_t<Ts...> convert(
|
||||
line_ptr_type line, const std::string& delim = default_delimiter) {
|
||||
split(line, delim);
|
||||
return convert<Ts...>(splitter_.split_input_);
|
||||
return convert<Ts...>(splitter_.split_data_);
|
||||
}
|
||||
|
||||
// parses already split line, returns 'T' object with extracted values
|
||||
template <typename T, typename... Ts>
|
||||
T convert_object(const split_input& elems) {
|
||||
T convert_object(const split_data& elems) {
|
||||
return to_object<T>(convert<Ts...>(elems));
|
||||
}
|
||||
|
||||
@ -151,17 +139,12 @@ public:
|
||||
// one argument is given which is a class which has a tied
|
||||
// method which returns a tuple, returns that type
|
||||
template <typename T, typename... Ts>
|
||||
no_void_validator_tup_t<T, Ts...> convert(const split_input& elems) {
|
||||
if constexpr (sizeof...(Ts) == 0 &&
|
||||
is_instance_of<T, std::tuple>::value) {
|
||||
no_void_validator_tup_t<T, Ts...> convert(const split_data& elems) {
|
||||
if constexpr (sizeof...(Ts) == 0 && is_instance_of_v<std::tuple, T>) {
|
||||
return convert_impl(elems, static_cast<T*>(nullptr));
|
||||
|
||||
} else if constexpr (tied_class_v<T, Ts...>) {
|
||||
using arg_ref_tuple =
|
||||
typename std::result_of_t<decltype (&T::tied)(T)>;
|
||||
|
||||
using arg_tuple =
|
||||
typename apply_trait<std::decay, arg_ref_tuple>::type;
|
||||
using arg_ref_tuple = std::result_of_t<decltype (&T::tied)(T)>;
|
||||
using arg_tuple = apply_trait_t<std::decay, arg_ref_tuple>;
|
||||
|
||||
return to_object<T>(
|
||||
convert_impl(elems, static_cast<arg_tuple*>(nullptr)));
|
||||
@ -173,7 +156,7 @@ public:
|
||||
// same as above, but uses cached split line
|
||||
template <typename T, typename... Ts>
|
||||
no_void_validator_tup_t<T, Ts...> convert() {
|
||||
return convert<T, Ts...>(splitter_.split_input_);
|
||||
return convert<T, Ts...>(splitter_.split_data_);
|
||||
}
|
||||
|
||||
bool valid() const {
|
||||
@ -185,8 +168,7 @@ public:
|
||||
}
|
||||
|
||||
const std::string& error_msg() const {
|
||||
static_assert(string_error,
|
||||
"'string_error' needs to be enabled to use 'error_msg'");
|
||||
assert_string_error_defined<string_error>();
|
||||
return error_;
|
||||
}
|
||||
|
||||
@ -196,11 +178,11 @@ public:
|
||||
|
||||
// 'splits' string by given delimiter, returns vector of pairs which
|
||||
// contain the beginnings and the ends of each column of the string
|
||||
const split_input& split(line_ptr_type line,
|
||||
const split_data& split(line_ptr_type line,
|
||||
const std::string& delim = default_delimiter) {
|
||||
splitter_.split_input_.clear();
|
||||
splitter_.split_data_.clear();
|
||||
if (line[0] == '\0') {
|
||||
return splitter_.split_input_;
|
||||
return splitter_.split_data_;
|
||||
}
|
||||
|
||||
return splitter_.split(line, delim);
|
||||
@ -211,7 +193,7 @@ private:
|
||||
// resplit
|
||||
////////////////
|
||||
|
||||
const split_input& resplit(line_ptr_type new_line, ssize_t new_size,
|
||||
const split_data& resplit(line_ptr_type new_line, ssize_t new_size,
|
||||
const std::string& delim = default_delimiter) {
|
||||
return splitter_.resplit(new_line, new_size, delim);
|
||||
}
|
||||
@ -285,7 +267,7 @@ private:
|
||||
////////////////
|
||||
|
||||
template <typename... Ts>
|
||||
no_void_validator_tup_t<Ts...> convert_impl(const split_input& elems) {
|
||||
no_void_validator_tup_t<Ts...> convert_impl(const split_data& elems) {
|
||||
clear_error();
|
||||
|
||||
if (!splitter_.valid()) {
|
||||
@ -306,7 +288,7 @@ private:
|
||||
// do not know how to specialize by return type :(
|
||||
template <typename... Ts>
|
||||
no_void_validator_tup_t<std::tuple<Ts...>> convert_impl(
|
||||
const split_input& elems, const std::tuple<Ts...>*) {
|
||||
const split_data& elems, const std::tuple<Ts...>*) {
|
||||
return convert_impl<Ts...>(elems);
|
||||
}
|
||||
|
||||
@ -345,11 +327,11 @@ private:
|
||||
|
||||
template <size_t ArgN, size_t TupN, typename... Ts>
|
||||
void extract_multiple(no_void_validator_tup_t<Ts...>& tup,
|
||||
const split_input& elems) {
|
||||
const split_data& elems) {
|
||||
using elem_t = std::tuple_element_t<ArgN, std::tuple<Ts...>>;
|
||||
|
||||
constexpr bool not_void = !std::is_void_v<elem_t>;
|
||||
constexpr bool one_element = count_not<std::is_void, Ts...>::size == 1;
|
||||
constexpr bool one_element = count_not_v<std::is_void, Ts...> == 1;
|
||||
|
||||
if constexpr (not_void) {
|
||||
if constexpr (one_element) {
|
||||
@ -367,8 +349,8 @@ private:
|
||||
}
|
||||
|
||||
template <typename... Ts>
|
||||
no_void_validator_tup_t<Ts...> extract_tuple(const split_input& elems) {
|
||||
static_assert(!all_of<std::is_void, Ts...>::value,
|
||||
no_void_validator_tup_t<Ts...> extract_tuple(const split_data& elems) {
|
||||
static_assert(!all_of_v<std::is_void, Ts...>,
|
||||
"at least one parameter must be non void");
|
||||
no_void_validator_tup_t<Ts...> ret{};
|
||||
extract_multiple<0, 0, Ts...>(ret, elems);
|
||||
@ -379,7 +361,7 @@ private:
|
||||
// members
|
||||
////////////////
|
||||
|
||||
error_type error_;
|
||||
error_type error_{};
|
||||
splitter<Matchers...> splitter_;
|
||||
|
||||
template <typename...>
|
||||
|
@ -225,8 +225,8 @@ struct unsupported_type {
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<!std::is_integral_v<T> && !std::is_floating_point_v<T> &&
|
||||
!is_instance_of<T, std::optional>::value &&
|
||||
!is_instance_of<T, std::variant>::value,
|
||||
!is_instance_of_v<std::optional, T> &&
|
||||
!is_instance_of_v<std::variant, T>,
|
||||
bool>
|
||||
extract(const char*, const char*, T&) {
|
||||
static_assert(error::unsupported_type<T>::value,
|
||||
@ -246,7 +246,7 @@ extract(const char* begin, const char* end, T& value) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<is_instance_of<T, std::optional>::value, bool> extract(
|
||||
std::enable_if_t<is_instance_of_v<std::optional, T>, bool> extract(
|
||||
const char* begin, const char* end, T& value) {
|
||||
typename T::value_type raw_value;
|
||||
if (extract(begin, end, raw_value)) {
|
||||
@ -271,7 +271,7 @@ bool extract_variant(const char* begin, const char* end, T& value) {
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::enable_if_t<is_instance_of<T, std::variant>::value, bool> extract(
|
||||
std::enable_if_t<is_instance_of_v<std::variant, T>, bool> extract(
|
||||
const char* begin, const char* end, T& value) {
|
||||
return extract_variant<T, 0>(begin, end, value);
|
||||
}
|
||||
|
@ -13,8 +13,6 @@ namespace ss {
|
||||
|
||||
template <typename... Matchers>
|
||||
class parser {
|
||||
struct none {};
|
||||
|
||||
constexpr static auto string_error = setup<Matchers...>::string_error;
|
||||
constexpr static auto multiline = setup<Matchers...>::multiline;
|
||||
|
||||
@ -48,8 +46,7 @@ public:
|
||||
}
|
||||
|
||||
const std::string& error_msg() const {
|
||||
static_assert(string_error,
|
||||
"'string_error' needs to be enabled to use 'error_msg'");
|
||||
assert_string_error_defined<string_error>();
|
||||
return error_;
|
||||
}
|
||||
|
||||
@ -157,11 +154,13 @@ public:
|
||||
if (!parser_.valid()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if constexpr (!std::is_same_v<U, decltype(tuple_output)>) {
|
||||
value = to_object<U>(std::move(tuple_output));
|
||||
} else {
|
||||
value = std::move(tuple_output);
|
||||
}
|
||||
|
||||
parser_.try_invoke(*value, std::forward<Fun>(fun));
|
||||
}
|
||||
}
|
||||
@ -177,6 +176,10 @@ public:
|
||||
return value;
|
||||
}
|
||||
|
||||
////////////////
|
||||
// members
|
||||
////////////////
|
||||
|
||||
std::tuple<Ts...> values_;
|
||||
parser& parser_;
|
||||
};
|
||||
@ -488,7 +491,7 @@ private:
|
||||
////////////////
|
||||
|
||||
std::string file_name_;
|
||||
error_type error_;
|
||||
error_type error_{};
|
||||
reader reader_;
|
||||
size_t line_number_{0};
|
||||
bool eof_{false};
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
namespace ss {
|
||||
|
||||
////////////////
|
||||
// matcher
|
||||
////////////////
|
||||
|
||||
template <char... Cs>
|
||||
struct matcher {
|
||||
private:
|
||||
@ -36,7 +40,7 @@ public:
|
||||
};
|
||||
|
||||
template <typename FirstMatcher, typename SecondMatcher>
|
||||
constexpr bool matches_intersect() {
|
||||
inline constexpr bool matches_intersect() {
|
||||
for (const auto& first_match : FirstMatcher::matches) {
|
||||
for (const auto& second_match : SecondMatcher::matches) {
|
||||
if (first_match != '\0' && first_match == second_match) {
|
||||
@ -47,6 +51,13 @@ constexpr bool matches_intersect() {
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename FirstMatcher, typename SecondMatcher1,
|
||||
typename SecondMatcher2>
|
||||
inline constexpr bool matches_intersect_union() {
|
||||
return matches_intersect<FirstMatcher, SecondMatcher1>() ||
|
||||
matches_intersect<FirstMatcher, SecondMatcher2>();
|
||||
}
|
||||
|
||||
template <>
|
||||
class matcher<'\0'> {
|
||||
public:
|
||||
@ -55,31 +66,57 @@ public:
|
||||
static bool match(char c) = delete;
|
||||
};
|
||||
|
||||
////////////////
|
||||
// setup parameters
|
||||
////////////////
|
||||
|
||||
template <char C>
|
||||
struct quote : matcher<C> {};
|
||||
|
||||
template <char... Cs>
|
||||
struct trim : matcher<Cs...> {};
|
||||
|
||||
template <char... Cs>
|
||||
struct trim_left : matcher<Cs...> {};
|
||||
|
||||
template <char... Cs>
|
||||
struct trim_right : matcher<Cs...> {};
|
||||
|
||||
template <char... Cs>
|
||||
struct escape : matcher<Cs...> {};
|
||||
|
||||
// TODO add limit
|
||||
class multiline;
|
||||
|
||||
class string_error;
|
||||
|
||||
////////////////
|
||||
// setup implementation
|
||||
////////////////
|
||||
|
||||
template <typename T, template <char...> class Template>
|
||||
struct is_instance_of_matcher {
|
||||
constexpr static bool value = false;
|
||||
};
|
||||
struct is_instance_of_matcher : std::false_type {};
|
||||
|
||||
template <char... Ts, template <char...> class Template>
|
||||
struct is_instance_of_matcher<Template<Ts...>, Template> {
|
||||
constexpr static bool value = true;
|
||||
};
|
||||
struct is_instance_of_matcher<Template<Ts...>, Template> : std::true_type {};
|
||||
|
||||
template <typename T, template <char...> class Template>
|
||||
using is_instance_of_matcher_t =
|
||||
typename is_instance_of_matcher<T, Template>::type;
|
||||
|
||||
template <template <char...> class Matcher, typename... Ts>
|
||||
struct get_matcher;
|
||||
|
||||
template <template <char...> class Matcher, typename T, typename... Ts>
|
||||
struct get_matcher<Matcher, T, Ts...> {
|
||||
using type = ternary_t<is_instance_of_matcher<T, Matcher>::value, T,
|
||||
|
||||
template <typename U>
|
||||
struct is_matcher : is_instance_of_matcher<U, Matcher> {};
|
||||
|
||||
static_assert(count_v<is_matcher, T, Ts...> <= 1,
|
||||
"the same matcher is cannot"
|
||||
"be defined multiple times");
|
||||
using type = ternary_t<is_matcher<T>::value, T,
|
||||
typename get_matcher<Matcher, Ts...>::type>;
|
||||
};
|
||||
|
||||
@ -91,39 +128,72 @@ struct get_matcher<Matcher> {
|
||||
template <template <char...> class Matcher, typename... Ts>
|
||||
using get_matcher_t = typename get_matcher<Matcher, Ts...>::type;
|
||||
|
||||
class multiline;
|
||||
class string_error;
|
||||
|
||||
template <typename... Ts>
|
||||
struct setup {
|
||||
private:
|
||||
template <typename T>
|
||||
struct is_multiline : std::is_same<T, multiline> {};
|
||||
struct is_matcher
|
||||
: std::disjunction<is_instance_of_matcher_t<T, quote>,
|
||||
is_instance_of_matcher_t<T, escape>,
|
||||
is_instance_of_matcher_t<T, trim>,
|
||||
is_instance_of_matcher_t<T, trim_left>,
|
||||
is_instance_of_matcher_t<T, trim_right>> {};
|
||||
|
||||
constexpr static auto count_multiline = count<is_multiline, Ts...>::size;
|
||||
template <typename T>
|
||||
struct is_multiline : std::is_same<T, multiline> {};
|
||||
|
||||
template <typename T>
|
||||
struct is_string_error : std::is_same<T, string_error> {};
|
||||
|
||||
constexpr static auto count_string_error =
|
||||
count<is_string_error, Ts...>::size;
|
||||
constexpr static auto count_matcher = count_v<is_matcher, Ts...>;
|
||||
constexpr static auto count_multiline = count_v<is_multiline, Ts...>;
|
||||
constexpr static auto count_string_error = count_v<is_string_error, Ts...>;
|
||||
|
||||
constexpr static auto number_of_valid_setup_types =
|
||||
count_matcher + count_multiline + count_string_error;
|
||||
|
||||
using trim_left_only = get_matcher_t<trim_left, Ts...>;
|
||||
using trim_right_only = get_matcher_t<trim_right, Ts...>;
|
||||
using trim_all = get_matcher_t<trim, Ts...>;
|
||||
|
||||
public:
|
||||
using quote = get_matcher_t<quote, Ts...>;
|
||||
using trim = get_matcher_t<trim, Ts...>;
|
||||
using escape = get_matcher_t<escape, Ts...>;
|
||||
|
||||
using trim_left = ternary_t<trim_all::enabled, trim_all, trim_left_only>;
|
||||
using trim_right = ternary_t<trim_all::enabled, trim_all, trim_right_only>;
|
||||
|
||||
constexpr static bool multiline = (count_multiline == 1);
|
||||
constexpr static bool string_error = (count_string_error == 1);
|
||||
|
||||
private:
|
||||
#define ASSERT_MSG "cannot have the same match character in multiple matchers"
|
||||
static_assert(!matches_intersect<escape, quote>(), ASSERT_MSG);
|
||||
|
||||
constexpr static auto quote_trim_intersect =
|
||||
matches_intersect_union<quote, trim_left, trim_right>();
|
||||
static_assert(!quote_trim_intersect, ASSERT_MSG);
|
||||
|
||||
constexpr static auto escape_trim_intersect =
|
||||
matches_intersect_union<escape, trim_left, trim_right>();
|
||||
static_assert(!escape_trim_intersect, ASSERT_MSG);
|
||||
|
||||
#undef ASSERT_MSG
|
||||
|
||||
static_assert(
|
||||
!multiline || (multiline && (quote::enabled || escape::enabled)),
|
||||
"to enable multiline either quote or escape need to be enabled");
|
||||
|
||||
#define ASSERT_MSG "cannot have the same match character in multiple matchers"
|
||||
static_assert(!matches_intersect<quote, trim>(), ASSERT_MSG);
|
||||
static_assert(!matches_intersect<trim, escape>(), ASSERT_MSG);
|
||||
static_assert(!matches_intersect<escape, quote>(), ASSERT_MSG);
|
||||
#undef ASSERT_MSG
|
||||
static_assert(!(trim_all::enabled && trim_left_only::enabled) &&
|
||||
!(trim_all::enabled && trim_right_only::enabled),
|
||||
"ambiguous trim setup");
|
||||
|
||||
static_assert(count_multiline <= 1, "mutliline defined multiple times");
|
||||
static_assert(count_string_error <= 1,
|
||||
"string_error defined multiple times");
|
||||
|
||||
static_assert(number_of_valid_setup_types == sizeof...(Ts),
|
||||
"one or multiple invalid setup parameters defined");
|
||||
};
|
||||
|
||||
template <typename... Ts>
|
||||
|
@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include "common.hpp"
|
||||
#include "setup.hpp"
|
||||
#include "type_traits.hpp"
|
||||
#include <algorithm>
|
||||
@ -10,16 +11,12 @@
|
||||
|
||||
namespace ss {
|
||||
|
||||
// TODO move to common or something
|
||||
using string_range = std::pair<const char*, const char*>;
|
||||
using split_input = std::vector<string_range>;
|
||||
constexpr static auto default_delimiter = ",";
|
||||
|
||||
template <typename... Ts>
|
||||
class splitter {
|
||||
private:
|
||||
using quote = typename setup<Ts...>::quote;
|
||||
using trim = typename setup<Ts...>::trim;
|
||||
using trim_left = typename setup<Ts...>::trim_left;
|
||||
using trim_right = typename setup<Ts...>::trim_right;
|
||||
using escape = typename setup<Ts...>::escape;
|
||||
|
||||
constexpr static auto string_error = setup<Ts...>::string_error;
|
||||
@ -39,8 +36,7 @@ public:
|
||||
}
|
||||
|
||||
const std::string& error_msg() const {
|
||||
static_assert(string_error,
|
||||
"'string_error' needs to be enabled to use 'error_msg'");
|
||||
assert_string_error_defined<string_error>();
|
||||
return error_;
|
||||
}
|
||||
|
||||
@ -48,9 +44,9 @@ public:
|
||||
return unterminated_quote_;
|
||||
}
|
||||
|
||||
const split_input& split(line_ptr_type new_line,
|
||||
const split_data& split(line_ptr_type new_line,
|
||||
const std::string& delimiter = default_delimiter) {
|
||||
split_input_.clear();
|
||||
split_data_.clear();
|
||||
return resplit(new_line, -1, delimiter);
|
||||
}
|
||||
|
||||
@ -60,33 +56,35 @@ private:
|
||||
////////////////
|
||||
|
||||
void adjust_ranges(const char* old_line) {
|
||||
for (auto& [begin, end] : split_input_) {
|
||||
for (auto& [begin, end] : split_data_) {
|
||||
begin = begin - old_line + line_;
|
||||
end = end - old_line + line_;
|
||||
}
|
||||
}
|
||||
|
||||
const split_input& resplit(
|
||||
const split_data& resplit(
|
||||
line_ptr_type new_line, ssize_t new_size,
|
||||
const std::string& delimiter = default_delimiter) {
|
||||
line_ = new_line;
|
||||
|
||||
// resplitting, continue from last slice
|
||||
if (!split_input_.empty() && unterminated_quote()) {
|
||||
const auto& last = std::prev(split_input_.end());
|
||||
if constexpr (quote::enabled) {
|
||||
if (!split_data_.empty() && unterminated_quote()) {
|
||||
const auto& last = std::prev(split_data_.end());
|
||||
const auto [old_line, old_begin] = *last;
|
||||
size_t begin = old_begin - old_line - 1;
|
||||
split_input_.pop_back();
|
||||
split_data_.pop_back();
|
||||
adjust_ranges(old_line);
|
||||
|
||||
// safety measure
|
||||
if (new_size != -1 && static_cast<size_t>(new_size) < begin) {
|
||||
set_error_invalid_resplit();
|
||||
return split_input_;
|
||||
return split_data_;
|
||||
}
|
||||
|
||||
begin_ = line_ + begin;
|
||||
}
|
||||
}
|
||||
|
||||
return split_impl_select_delim(delimiter);
|
||||
}
|
||||
@ -163,9 +161,17 @@ private:
|
||||
return delim.size();
|
||||
}
|
||||
|
||||
void trim_if_enabled(line_ptr_type& curr) {
|
||||
if constexpr (trim::enabled) {
|
||||
while (trim::match(*curr)) {
|
||||
void trim_left_if_enabled(line_ptr_type& curr) {
|
||||
if constexpr (trim_left::enabled) {
|
||||
while (trim_left::match(*curr)) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void trim_right_if_enabled(line_ptr_type& curr) {
|
||||
if constexpr (trim_right::enabled) {
|
||||
while (trim_right::match(*curr)) {
|
||||
++curr;
|
||||
}
|
||||
}
|
||||
@ -176,7 +182,7 @@ private:
|
||||
const Delim& delim) {
|
||||
line_ptr_type end = begin;
|
||||
|
||||
trim_if_enabled(end);
|
||||
trim_right_if_enabled(end);
|
||||
|
||||
// just spacing
|
||||
if (*end == '\0') {
|
||||
@ -190,7 +196,7 @@ private:
|
||||
}
|
||||
|
||||
end += delimiter_size(delim);
|
||||
trim_if_enabled(end);
|
||||
trim_left_if_enabled(end);
|
||||
|
||||
// delimiter
|
||||
return {end - begin, true};
|
||||
@ -213,7 +219,7 @@ private:
|
||||
|
||||
void shift_and_push() {
|
||||
shift_and_set_current();
|
||||
split_input_.emplace_back(begin_, curr_);
|
||||
split_data_.emplace_back(begin_, curr_);
|
||||
}
|
||||
|
||||
void shift_if_escaped(line_ptr_type& curr) {
|
||||
@ -241,13 +247,13 @@ private:
|
||||
// split impl
|
||||
////////////////
|
||||
|
||||
const split_input& split_impl_select_delim(
|
||||
const split_data& split_impl_select_delim(
|
||||
const std::string& delimiter = default_delimiter) {
|
||||
clear_error();
|
||||
switch (delimiter.size()) {
|
||||
case 0:
|
||||
set_error_empty_delimiter();
|
||||
return split_input_;
|
||||
return split_data_;
|
||||
case 1:
|
||||
return split_impl(delimiter[0]);
|
||||
default:
|
||||
@ -256,18 +262,18 @@ private:
|
||||
}
|
||||
|
||||
template <typename Delim>
|
||||
const split_input& split_impl(const Delim& delim) {
|
||||
const split_data& split_impl(const Delim& delim) {
|
||||
|
||||
if (split_input_.empty()) {
|
||||
if (split_data_.empty()) {
|
||||
begin_ = line_;
|
||||
}
|
||||
|
||||
trim_if_enabled(begin_);
|
||||
trim_left_if_enabled(begin_);
|
||||
|
||||
for (done_ = false; !done_; read(delim))
|
||||
;
|
||||
|
||||
return split_input_;
|
||||
return split_data_;
|
||||
}
|
||||
|
||||
////////////////
|
||||
@ -329,7 +335,7 @@ private:
|
||||
// eg: ..."hell\0 -> quote not terminated
|
||||
if (*end_ == '\0') {
|
||||
set_error_unterminated_quote();
|
||||
split_input_.emplace_back(line_, begin_);
|
||||
split_data_.emplace_back(line_, begin_);
|
||||
done_ = true;
|
||||
break;
|
||||
}
|
||||
@ -363,7 +369,7 @@ private:
|
||||
// mismatched quote
|
||||
// eg: ...,"hel"lo,... -> error
|
||||
set_error_mismatched_quote(end_ - line_);
|
||||
split_input_.emplace_back(line_, begin_);
|
||||
split_data_.emplace_back(line_, begin_);
|
||||
}
|
||||
done_ = true;
|
||||
break;
|
||||
@ -375,17 +381,16 @@ private:
|
||||
// members
|
||||
////////////////
|
||||
|
||||
static_assert(std::is_same_v<error_type, bool> ||
|
||||
std::is_same_v<error_type, std::string>);
|
||||
error_type error_;
|
||||
error_type error_{};
|
||||
bool unterminated_quote_{false};
|
||||
bool done_;
|
||||
size_t escaped_{0};
|
||||
split_data split_data_;
|
||||
|
||||
line_ptr_type begin_;
|
||||
line_ptr_type curr_;
|
||||
line_ptr_type end_;
|
||||
line_ptr_type line_;
|
||||
bool done_;
|
||||
size_t escaped_{0};
|
||||
split_input split_input_;
|
||||
|
||||
template <typename...>
|
||||
friend class converter;
|
||||
|
@ -103,6 +103,9 @@ struct apply_trait<Trait, std::tuple<T>> {
|
||||
using type = std::tuple<typename Trait<T>::type>;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
using apply_trait_t = typename apply_trait<Trait, Ts...>::type;
|
||||
|
||||
////////////////
|
||||
// apply optional trait
|
||||
////////////////
|
||||
@ -143,6 +146,9 @@ struct apply_optional_trait<Trait, std::tuple<T>> {
|
||||
std::tuple<typename optional_trait<typename Trait<T>::type, T>::type>;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
using apply_trait_optional_t = apply_optional_trait<Trait, Ts...>;
|
||||
|
||||
////////////////
|
||||
// filter false_type
|
||||
////////////////
|
||||
@ -192,6 +198,9 @@ struct negate_impl {
|
||||
using type = std::integral_constant<bool, !Trait<Ts...>::value>;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait>
|
||||
using negate_impl_t = typename negate_impl<Trait>::type;
|
||||
|
||||
////////////////
|
||||
// filter by trait
|
||||
////////////////
|
||||
@ -235,20 +244,23 @@ struct count;
|
||||
|
||||
template <template <typename...> class Trait, typename T, typename... Ts>
|
||||
struct count<Trait, T, Ts...> {
|
||||
static constexpr size_t size =
|
||||
static constexpr size_t value =
|
||||
std::tuple_size<filter_if_t<Trait, T, Ts...>>::value;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename T>
|
||||
struct count<Trait, T> {
|
||||
static constexpr size_t size = Trait<T>::value;
|
||||
static constexpr size_t value = Trait<T>::value;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait>
|
||||
struct count<Trait> {
|
||||
static constexpr size_t size = 0;
|
||||
static constexpr size_t value = 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
constexpr size_t count_v = count<Trait, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// count not
|
||||
////////////////
|
||||
@ -258,34 +270,40 @@ struct count_not;
|
||||
|
||||
template <template <typename...> class Trait, typename T, typename... Ts>
|
||||
struct count_not<Trait, T, Ts...> {
|
||||
static constexpr size_t size =
|
||||
static constexpr size_t value =
|
||||
std::tuple_size<filter_not_t<Trait, T, Ts...>>::value;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename T>
|
||||
struct count_not<Trait, T> {
|
||||
static constexpr size_t size = !Trait<T>::value;
|
||||
static constexpr size_t value = !Trait<T>::value;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait>
|
||||
struct count_not<Trait> {
|
||||
static constexpr size_t size = 0;
|
||||
static constexpr size_t value = 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
constexpr size_t count_not_v = count_not<Trait, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// all of
|
||||
////////////////
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct all_of {
|
||||
static constexpr bool value = count<Trait, Ts...>::size == sizeof...(Ts);
|
||||
static constexpr bool value = count_v<Trait, Ts...> == sizeof...(Ts);
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct all_of<Trait, std::tuple<Ts...>> {
|
||||
static constexpr bool value = count<Trait, Ts...>::size == sizeof...(Ts);
|
||||
static constexpr bool value = count_v<Trait, Ts...> == sizeof...(Ts);
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
constexpr bool all_of_v = all_of<Trait, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// any of
|
||||
////////////////
|
||||
@ -293,43 +311,52 @@ struct all_of<Trait, std::tuple<Ts...>> {
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct any_of {
|
||||
static_assert(sizeof...(Ts) > 0);
|
||||
static constexpr bool value = count<Trait, Ts...>::size > 0;
|
||||
static constexpr bool value = count_v<Trait, Ts...> > 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct any_of<Trait, std::tuple<Ts...>> {
|
||||
static_assert(sizeof...(Ts) > 0);
|
||||
static constexpr bool value = count<Trait, Ts...>::size > 0;
|
||||
static constexpr bool value = count_v<Trait, Ts...> > 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
constexpr bool any_of_v = any_of<Trait, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// none of
|
||||
////////////////
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct none_of {
|
||||
static constexpr bool value = count<Trait, Ts...>::size == 0;
|
||||
static constexpr bool value = count_v<Trait, Ts...> == 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
struct none_of<Trait, std::tuple<Ts...>> {
|
||||
static constexpr bool value = count<Trait, Ts...>::size == 0;
|
||||
static constexpr bool value = count_v<Trait, Ts...> == 0;
|
||||
};
|
||||
|
||||
template <template <typename...> class Trait, typename... Ts>
|
||||
constexpr bool none_of_v = none_of<Trait, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// is instance of
|
||||
////////////////
|
||||
|
||||
template <typename T, template <typename...> class Template>
|
||||
template <template <typename...> class Template, typename T>
|
||||
struct is_instance_of {
|
||||
constexpr static bool value = false;
|
||||
};
|
||||
|
||||
template <typename... Ts, template <typename...> class Template>
|
||||
struct is_instance_of<Template<Ts...>, Template> {
|
||||
template <template <typename...> class Template, typename... Ts>
|
||||
struct is_instance_of<Template, Template<Ts...>> {
|
||||
constexpr static bool value = true;
|
||||
};
|
||||
|
||||
template <template <typename...> class Template, typename... Ts>
|
||||
constexpr bool is_instance_of_v = is_instance_of<Template, Ts...>::value;
|
||||
|
||||
////////////////
|
||||
// ternary
|
||||
////////////////
|
||||
@ -354,17 +381,21 @@ using ternary_t = typename ternary<B, T, U>::type;
|
||||
// tuple to struct
|
||||
////////////////
|
||||
|
||||
template <class S, std::size_t... Is, class Tup>
|
||||
S to_object(std::index_sequence<Is...>, Tup&& tup) {
|
||||
return {std::get<Is>(std::forward<Tup>(tup))...};
|
||||
template <class T, std::size_t... Is, class U>
|
||||
T to_object_impl(std::index_sequence<Is...>, U&& data) {
|
||||
return {std::get<Is>(std::forward<U>(data))...};
|
||||
}
|
||||
|
||||
// TODO Tup may not be a tuple ...
|
||||
template <class S, class Tup>
|
||||
S to_object(Tup&& tup) {
|
||||
using T = std::remove_reference_t<Tup>;
|
||||
return to_object<S>(std::make_index_sequence<std::tuple_size<T>{}>{},
|
||||
std::forward<Tup>(tup));
|
||||
template <class T, class U>
|
||||
T to_object(U&& data) {
|
||||
using NoRefU = std::remove_reference_t<U>;
|
||||
if constexpr (is_instance_of_v<std::tuple, NoRefU>) {
|
||||
return to_object_impl<
|
||||
T>(std::make_index_sequence<std::tuple_size<NoRefU>{}>{},
|
||||
std::forward<U>(data));
|
||||
} else {
|
||||
return T{std::forward<U>(data)};
|
||||
}
|
||||
}
|
||||
|
||||
} /* trait */
|
||||
|
@ -17,7 +17,7 @@ struct set_combinations_size {
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::string> words(const ss::split_input& input) {
|
||||
std::vector<std::string> words(const ss::split_data& input) {
|
||||
std::vector<std::string> ret;
|
||||
for (const auto& [begin, end] : input) {
|
||||
ret.emplace_back(begin, end);
|
||||
@ -88,6 +88,30 @@ auto spaced(const case_type& input, const std::string& s1,
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto spaced_left(const case_type& input, const std::string& s) {
|
||||
case_type ret = input;
|
||||
for (const auto& i : input) {
|
||||
ret.push_back(concat(i));
|
||||
ret.push_back(concat(s, i));
|
||||
ret.push_back(concat(s, s, i));
|
||||
ret.push_back(concat(s, s, s, i));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
auto spaced_right(const case_type& input, const std::string& s) {
|
||||
case_type ret = input;
|
||||
for (const auto& i : input) {
|
||||
ret.push_back(concat(i));
|
||||
ret.push_back(concat(i, s));
|
||||
ret.push_back(concat(i, s, s));
|
||||
ret.push_back(concat(i, s, s, s));
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::vector<std::string> combinations(const std::vector<std::string>& v,
|
||||
const std::string& delim, size_t n) {
|
||||
if (n <= 1) {
|
||||
@ -709,3 +733,104 @@ TEST_CASE("splitter test invalid splits") {
|
||||
CHECK(!s.unterminated_quote());
|
||||
CHECK(!s.error_msg().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("splitter test with trim_left") {
|
||||
auto guard = set_combinations_size(3);
|
||||
case_type case1 = spaced_left({R"(x )"}, " ");
|
||||
case_type case2 = spaced_left({R"(yy )"}, " ");
|
||||
case_type case3 = spaced_left({R"(y y )"}, " ");
|
||||
case_type case4 = spaced_left({R"()"}, " ");
|
||||
|
||||
std::vector<std::string> delims = {",", "::", "\t", "\n"};
|
||||
|
||||
{
|
||||
matches_type p{{case1, "x "},
|
||||
{case2, "yy "},
|
||||
{case3, "y y "},
|
||||
{case4, ""}};
|
||||
test_combinations<ss::trim_left<' '>>(p, delims);
|
||||
}
|
||||
|
||||
case_type case5 = spaced_left({"z "}, "\t");
|
||||
case_type case6 = spaced_left({"ab\t "}, " \t");
|
||||
case_type case7 = spaced_left({"a\tb "}, " \t");
|
||||
case_type case8 = spaced_left({"a \t b "}, " \t");
|
||||
|
||||
{
|
||||
matches_type p{{case1, "x "}, {case2, "yy "}, {case3, "y y "},
|
||||
{case4, ""}, {case5, "z "}, {case6, "ab\t "},
|
||||
{case7, "a\tb "}, {case8, "a \t b "}};
|
||||
test_combinations<ss::trim_left<' ', '\t'>>(p, {",", "::", "\n"});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("splitter test with trim_right") {
|
||||
auto guard = set_combinations_size(3);
|
||||
case_type case1 = spaced_right({R"( x)"}, " ");
|
||||
case_type case2 = spaced_right({R"( yy)"}, " ");
|
||||
case_type case3 = spaced_right({R"( y y)"}, " ");
|
||||
case_type case4 = spaced_right({R"()"}, " ");
|
||||
|
||||
std::vector<std::string> delims = {",", "::", "\t", "\n"};
|
||||
|
||||
{
|
||||
matches_type p{{case1, " x"},
|
||||
{case2, " yy"},
|
||||
{case3, " y y"},
|
||||
{case4, ""}};
|
||||
test_combinations<ss::trim_right<' '>>(p, delims);
|
||||
}
|
||||
|
||||
case_type case5 = spaced_right({" z"}, "\t");
|
||||
case_type case6 = spaced_right({"\t ab"}, " \t");
|
||||
case_type case7 = spaced_right({"\ta\tb"}, " \t");
|
||||
case_type case8 = spaced_right({" \t a \t b"}, " \t");
|
||||
|
||||
{
|
||||
matches_type p{{case1, " x"}, {case2, " yy"},
|
||||
{case3, " y y"}, {case4, ""},
|
||||
{case5, " z"}, {case6, "\t ab"},
|
||||
{case7, "\ta\tb"}, {case8, " \t a \t b"}};
|
||||
test_combinations<ss::trim_right<' ', '\t'>>(p, {",", "::", "\n"});
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("splitter test with trim_right and trim_left") {
|
||||
auto guard = set_combinations_size(3);
|
||||
case_type case1 = spaced_right({R"(-x)"}, "-");
|
||||
case_type case2 = spaced_left({R"(yy_)"}, "_");
|
||||
case_type case3 = spaced_right({R"(-y y)"}, "-");
|
||||
case_type case4 = spaced_left({R"()"}, "-");
|
||||
case_type case5 = spaced_left({R"()"}, "_");
|
||||
case_type case6 = {"___---", "_-", "______-"};
|
||||
|
||||
std::vector<std::string> delims = {",", "::", "\t", "\n"};
|
||||
|
||||
{
|
||||
matches_type p{{case1, "-x"}, {case2, "yy_"}, {case3, "-y y"},
|
||||
{case4, ""}, {case5, ""}, {case6, ""}};
|
||||
test_combinations<ss::trim_left<'_'>, ss::trim_right<'-'>>(p, delims);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_CASE("splitter test with quote and escape, trim_left and trim_right") {
|
||||
auto guard = set_combinations_size(3);
|
||||
case_type case1 = spaced_left({R"("\"")", R"(\")", R"("""")"}, "_");
|
||||
case_type case2 =
|
||||
spaced_left({R"("x\"x")", R"(x\"x)", R"(x"x)", R"("x""x")"}, "_");
|
||||
case_type case3 = spaced_left({R"("")", R"()"}, "_");
|
||||
case_type case4 = spaced_left({R"("x")", R"(x)"}, "_");
|
||||
case_type case5 =
|
||||
spaced_right({R"("\"\"")", R"("""""")", R"("\"""")", R"("""\"")"}, "-");
|
||||
case_type case6 = spaced_right({R"("\\")", R"(\\)"}, "-");
|
||||
case_type case7 = spaced_right({R"("xxxxxxxxxx")", R"(xxxxxxxxxx)"}, "-");
|
||||
|
||||
std::vector<std::string> delims = {"::", "\n"};
|
||||
|
||||
{
|
||||
matches_type p{{case1, "\""}, {case2, "x\"x"}, {case3, ""},
|
||||
{case5, "\"\""}, {case6, "\\"}, {case7, "xxxxxxxxxx"}};
|
||||
test_combinations<ss::quote<'"'>, ss::escape<'\\'>, ss::trim_left<'_'>,
|
||||
ss::trim_right<'-'>>(p, delims);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user