add trim_left and trim_right, add setup static asserts, update type_traits, refactor some code, add unit tests

This commit is contained in:
ado
2021-02-14 01:59:06 +01:00
parent ea42948c42
commit 2985027505
8 changed files with 381 additions and 146 deletions

View File

@@ -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 std::string& delimiter = default_delimiter) {
split_input_.clear();
const split_data& split(line_ptr_type new_line,
const std::string& delimiter = default_delimiter) {
split_data_.clear();
return resplit(new_line, -1, delimiter);
}
@@ -60,32 +56,34 @@ 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());
const auto [old_line, old_begin] = *last;
size_t begin = old_begin - old_line - 1;
split_input_.pop_back();
adjust_ranges(old_line);
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_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_;
// safety measure
if (new_size != -1 && static_cast<size_t>(new_size) < begin) {
set_error_invalid_resplit();
return split_data_;
}
begin_ = line_ + begin;
}
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;