#pragma once #include "exception.hpp" #include "extract.hpp" #include "function_traits.hpp" #include "restrictions.hpp" #include "splitter.hpp" #include "type_traits.hpp" #include #include #include namespace ss { INIT_HAS_METHOD(tied) INIT_HAS_METHOD(ss_valid) INIT_HAS_METHOD(error) //////////////// // replace validator //////////////// // replace 'validator' types with elements they operate on // eg. no_validator_tup_t> <=> std::tuple // where ss::nx is a validator '(n)one e(x)cept' which // checks if the returned character is either 'A' or 'B', returns error if not // additionally if one element is left in the pack, it will be unwrapped from // the tuple eg. no_void_validator_tup_t <=> int instead of std::tuple template struct no_validator; template struct no_validator>> { using type = typename member_wrapper::arg_type; }; template struct no_validator { using type = T; }; template using no_validator_t = typename no_validator::type; template struct no_validator_tup : apply_trait> {}; template struct no_validator_tup> : no_validator_tup {}; template struct no_validator_tup> : no_validator {}; template using no_validator_tup_t = typename no_validator_tup::type; //////////////// // no void tuple //////////////// template struct no_void_tup : filter_not> {}; template using no_void_tup_t = filter_not_t; //////////////// // no void or validator //////////////// // replace 'validators' and remove void from tuple template struct no_void_validator_tup : no_validator_tup> {}; template struct no_void_validator_tup> : no_validator_tup> {}; template using no_void_validator_tup_t = typename no_void_validator_tup::type; //////////////// // tied class //////////////// // check if the parameter pack is only one element which is a class and has // the 'tied' method which is to be used for type deduction when converting template struct tied_class { constexpr static bool value = (sizeof...(Ts) == 0 && std::is_class_v && has_m_tied::value); }; template constexpr bool tied_class_v = tied_class::value; //////////////// // converter //////////////// template class converter { using line_ptr_type = typename splitter::line_ptr_type; constexpr static auto string_error = setup::string_error; constexpr static auto throw_on_error = setup::throw_on_error; constexpr static auto default_delimiter = ","; using error_type = std::conditional_t; public: // parses line with given delimiter, returns a 'T' object created with // extracted values of type 'Ts' template T convert_object(line_ptr_type line, const std::string& delim = default_delimiter) { return to_object(convert(line, delim)); } // parses line with given delimiter, returns tuple of objects with // extracted values of type 'Ts' template no_void_validator_tup_t convert( line_ptr_type line, const std::string& delim = default_delimiter) { split(line, delim); if (splitter_.valid()) { return convert(splitter_.split_data_); } else { handle_error_bad_split(); return {}; } } // parses already split line, returns 'T' object with extracted values template T convert_object(const split_data& elems) { return to_object(convert(elems)); } // same as above, but uses cached split line template T convert_object() { return to_object(convert()); } // parses already split line, returns either a tuple of objects with // parsed values (returns raw element (no tuple) if Ts is empty), or if // one argument is given which is a class which has a tied // method which returns a tuple, returns that type template no_void_validator_tup_t convert(const split_data& elems) { if constexpr (sizeof...(Ts) == 0 && is_instance_of_v) { return convert_impl(elems, static_cast(nullptr)); } else if constexpr (tied_class_v) { using arg_ref_tuple = std::invoke_result_t; using arg_tuple = apply_trait_t; return to_object( convert_impl(elems, static_cast(nullptr))); } else { return convert_impl(elems); } } // same as above, but uses cached split line template no_void_validator_tup_t convert() { return convert(splitter_.split_data_); } bool valid() const { if constexpr (string_error) { return error_.empty(); } else if constexpr (throw_on_error) { return true; } else { return !error_; } } const std::string& error_msg() const { assert_string_error_defined(); return error_; } bool unterminated_quote() const { return splitter_.unterminated_quote(); } // 'splits' string by given delimiter, returns vector of pairs which // contain the beginnings and the ends of each column of the string const split_data& split(line_ptr_type line, const std::string& delim = default_delimiter) { splitter_.split_data_.clear(); if (line[0] == '\0') { return splitter_.split_data_; } return splitter_.split(line, delim); } private: //////////////// // resplit //////////////// const split_data& resplit(line_ptr_type new_line, ssize_t new_size, const std::string& delim) { return splitter_.resplit(new_line, new_size, delim); } size_t size_shifted() { return splitter_.size_shifted(); } //////////////// // error //////////////// void clear_error() { if constexpr (string_error) { error_.clear(); } else { error_ = false; } } std::string error_sufix(const string_range msg, size_t pos) const { std::string error; error.reserve(32); error.append("at column ") .append(std::to_string(pos + 1)) .append(": \'") .append(msg.first, msg.second) .append("\'"); return error; } void handle_error_bad_split() { if constexpr (string_error) { error_.clear(); error_.append(splitter_.error_msg()); } else if constexpr (!throw_on_error) { error_ = true; } } void handle_error_unterminated_escape() { if constexpr (string_error) { error_.clear(); splitter_.handle_error_unterminated_escape(); error_.append(splitter_.error_msg()); } else if constexpr (throw_on_error) { splitter_.handle_error_unterminated_escape(); } else { error_ = true; } } void handle_error_unterminated_quote() { if constexpr (string_error) { error_.clear(); splitter_.handle_error_unterminated_quote(); error_.append(splitter_.error_msg()); } else if constexpr (throw_on_error) { splitter_.handle_error_unterminated_quote(); } else { error_ = true; } } void handle_error_multiline_limit_reached() { constexpr static auto error_msg = "multiline limit reached"; splitter_.unterminated_quote_ = false; if constexpr (string_error) { error_.clear(); error_.append(error_msg); } else if constexpr (throw_on_error) { throw ss::exception{error_msg}; } else { error_ = true; } } void handle_error_invalid_conversion(const string_range msg, size_t pos) { constexpr static auto error_msg = "invalid conversion for parameter "; if constexpr (string_error) { error_.clear(); error_.append(error_msg).append(error_sufix(msg, pos)); } else if constexpr (throw_on_error) { throw ss::exception{error_msg + error_sufix(msg, pos)}; } else { error_ = true; } } void handle_error_validation_failed(const char* const error, const string_range msg, size_t pos) { if constexpr (string_error) { error_.clear(); error_.append(error).append(" ").append(error_sufix(msg, pos)); } else if constexpr (throw_on_error) { throw ss::exception{error + (" " + error_sufix(msg, pos))}; } else { error_ = true; } } void handle_error_number_of_columns(size_t expected_pos, size_t pos) { constexpr static auto error_msg1 = "invalid number of columns, expected: "; constexpr static auto error_msg2 = ", got: "; if constexpr (string_error) { error_.clear(); error_.append(error_msg1) .append(std::to_string(expected_pos)) .append(error_msg2) .append(std::to_string(pos)); } else if constexpr (throw_on_error) { throw ss::exception{error_msg1 + std::to_string(expected_pos) + error_msg2 + std::to_string(pos)}; } else { error_ = true; } } void handle_error_incompatible_mapping(size_t argument_size, size_t mapping_size) { constexpr static auto error_msg1 = "number of arguments does not match mapping, expected: "; constexpr static auto error_msg2 = ", got: "; if constexpr (string_error) { error_.clear(); error_.append(error_msg1) .append(std::to_string(mapping_size)) .append(error_msg2) .append(std::to_string(argument_size)); } else if constexpr (throw_on_error) { throw ss::exception{error_msg1 + std::to_string(mapping_size) + error_msg2 + std::to_string(argument_size)}; } else { error_ = true; } } //////////////// // convert implementation //////////////// template no_void_validator_tup_t convert_impl(const split_data& elems) { clear_error(); if (!splitter_.valid()) { handle_error_bad_split(); return {}; } if (!columns_mapped()) { if (sizeof...(Ts) != elems.size()) { handle_error_number_of_columns(sizeof...(Ts), elems.size()); return {}; } } else { if (sizeof...(Ts) != column_mappings_.size()) { handle_error_incompatible_mapping(sizeof...(Ts), column_mappings_.size()); return {}; } if (elems.size() != number_of_columns_) { handle_error_number_of_columns(number_of_columns_, elems.size()); return {}; } } return extract_tuple(elems); } template no_void_validator_tup_t> convert_impl( const split_data& elems, const std::tuple*) { return convert_impl(elems); } //////////////// // column mapping //////////////// bool columns_mapped() const { return column_mappings_.size() != 0; } size_t column_position(size_t tuple_position) const { if (!columns_mapped()) { return tuple_position; } return column_mappings_[tuple_position]; } // assumes positions are valid and the vector is not empty void set_column_mapping(std::vector positions, size_t number_of_columns) { column_mappings_ = positions; number_of_columns_ = number_of_columns; } void clear_column_positions() { column_mappings_.clear(); number_of_columns_ = 0; } //////////////// // conversion //////////////// template void extract_one(no_validator_t& dst, const string_range msg, size_t pos) { if (!valid()) { return; } if constexpr (std::is_same_v) { extract(msg.first, msg.second, dst); return; } if (!extract(msg.first, msg.second, dst)) { handle_error_invalid_conversion(msg, pos); return; } if constexpr (has_m_ss_valid_t) { if (T validator; !validator.ss_valid(dst)) { if constexpr (has_m_error_t) { handle_error_validation_failed(validator.error(), msg, pos); } else { handle_error_validation_failed("validation error", msg, pos); } return; } } } template void extract_multiple(no_void_validator_tup_t& tup, const split_data& elems) { using elem_t = std::tuple_element_t>; constexpr bool not_void = !std::is_void_v; constexpr bool one_element = count_not_v == 1; if constexpr (not_void) { if constexpr (one_element) { extract_one(tup, elems[column_position(ArgN)], ArgN); } else { auto& el = std::get(tup); extract_one(el, elems[column_position(ArgN)], ArgN); } } if constexpr (sizeof...(Ts) > ArgN + 1) { constexpr size_t NewTupN = (not_void) ? TupN + 1 : TupN; extract_multiple(tup, elems); } } template no_void_validator_tup_t extract_tuple(const split_data& elems) { static_assert(!all_of_v, "at least one parameter must be non void"); no_void_validator_tup_t ret{}; extract_multiple<0, 0, Ts...>(ret, elems); return ret; } //////////////// // members //////////////// error_type error_{}; splitter splitter_; template friend class parser; std::vector column_mappings_; size_t number_of_columns_; }; } /* ss */