Add header and raw_header methods, update header usage methods error handling, write new and update existing unit tests

This commit is contained in:
ado
2024-03-13 17:15:31 +01:00
parent 69875c238e
commit b9f4afdd5f
7 changed files with 429 additions and 109 deletions

View File

@@ -31,6 +31,9 @@ class parser {
constexpr static bool ignore_empty = setup<Options...>::ignore_empty;
using header_splitter = ss::splitter<
ss::filter_not_t<ss::is_instance_of_multiline, Options...>>;
public:
parser(std::string file_name, std::string delim = ss::default_delimiter)
: file_name_{std::move(file_name)}, reader_{file_name_, delim} {
@@ -161,20 +164,49 @@ public:
return value;
}
std::string raw_header() const {
assert_ignore_header_not_defined();
return raw_header_;
}
std::vector<std::string> header() {
assert_ignore_header_not_defined();
clear_error();
header_splitter splitter;
std::string raw_header_copy = raw_header_;
if (!strict_split(splitter, raw_header_copy)) {
return {};
}
std::vector<std::string> split_header;
for (const auto& [begin, end] : splitter.split_data_) {
split_header.emplace_back(begin, end);
}
return split_header;
}
bool field_exists(const std::string& field) {
assert_ignore_header_not_defined();
clear_error();
if (header_.empty()) {
split_header_data();
}
if (!valid()) {
return false;
}
return header_index(field).has_value();
}
template <typename... Ts>
void use_fields(const Ts&... fields_args) {
if constexpr (ignore_header) {
handle_error_header_ignored();
return;
}
assert_ignore_header_not_defined();
clear_error();
if (header_.empty() && !eof()) {
split_header_data();
@@ -491,15 +523,51 @@ private:
// header
////////////////
void assert_ignore_header_not_defined() const {
static_assert(!ignore_header,
"cannot use this method when 'ignore_header' is defined");
}
bool strict_split(header_splitter& splitter, std::string& header) {
if (header.empty()) {
return false;
}
if constexpr (throw_on_error) {
try {
splitter.split(header.data(), reader_.delim_);
} catch (const ss::exception& e) {
decorate_rethrow_no_line(e);
}
} else {
splitter.split(header.data(), reader_.delim_);
if (!splitter.valid()) {
handle_error_invalid_header_split(splitter);
return false;
}
}
return true;
}
void split_header_data() {
ss::splitter<Options...> splitter;
header_splitter splitter;
std::string raw_header_copy = raw_header_;
splitter.split(raw_header_copy.data(), reader_.delim_);
if (!strict_split(splitter, raw_header_copy)) {
return;
}
for (const auto& [begin, end] : splitter.split_data_) {
std::string field{begin, end};
if (field.empty()) {
handle_error_duplicate_header_field(field);
header_.clear();
return;
}
if (std::find(header_.begin(), header_.end(), field) !=
header_.end()) {
handle_error_invalid_header(field);
handle_error_duplicate_header_field(field);
header_.clear();
return;
}
@@ -594,20 +662,6 @@ private:
}
}
void handle_error_header_ignored() {
constexpr static auto error_msg =
": the header row is ignored within the setup it cannot be used";
if constexpr (string_error) {
error_.clear();
error_.append(file_name_).append(error_msg);
} else if constexpr (throw_on_error) {
throw ss::exception{file_name_ + error_msg};
} else {
error_ = true;
}
}
void handle_error_invalid_field(const std::string& field) {
constexpr static auto error_msg =
": header does not contain given field: ";
@@ -648,14 +702,40 @@ private:
}
}
void handle_error_invalid_header(const std::string& field) {
constexpr static auto error_msg = "header contains duplicates: ";
void handle_error_invalid_header_field() {
constexpr static auto error_msg = " header contains empty field";
if constexpr (string_error) {
error_.clear();
error_.append(error_msg).append(error_msg);
error_.append(file_name_).append(error_msg);
} else if constexpr (throw_on_error) {
throw ss::exception{error_msg + field};
throw ss::exception{error_msg};
} else {
error_ = true;
}
}
void handle_error_duplicate_header_field(const std::string& field) {
constexpr static auto error_msg = " header contains duplicate: ";
if constexpr (string_error) {
error_.clear();
error_.append(file_name_).append(error_msg).append(field);
} else if constexpr (throw_on_error) {
throw ss::exception{file_name_ + error_msg + field};
} else {
error_ = true;
}
}
void handle_error_invalid_header_split(const header_splitter& splitter) {
constexpr static auto error_msg = " failed header split: ";
if constexpr (string_error) {
error_.clear();
error_.append(file_name_)
.append(error_msg)
.append(splitter.error_msg());
} else {
error_ = true;
}
@@ -671,6 +751,14 @@ private:
.append(e.what())};
}
void decorate_rethrow_no_line(const ss::exception& e) const {
static_assert(throw_on_error,
"throw_on_error needs to be enabled to use this method");
throw ss::exception{std::string{file_name_}
.append(": ")
.append(e.what())};
}
////////////////
// line reading
////////////////
@@ -935,7 +1023,7 @@ private:
}
std::string get_buffer() {
return std::string{next_line_buffer_, next_line_buffer_size_};
return std::string{next_line_buffer_, next_line_size_};
}
////////////////

View File

@@ -293,4 +293,7 @@ private:
template <typename... Options>
struct setup<setup<Options...>> : setup<Options...> {};
template <typename... Options>
struct setup<std::tuple<Options...>> : setup<Options...> {};
} /* namespace ss */