mirror of
				https://github.com/red0124/ssp.git
				synced 2025-10-30 21:06:44 +01:00 
			
		
		
		
	Merge pull request #10 from red0124/improvement/error_mode_and_multiline_setup
add string_error and multiline within the setup, remove set_error_mod…
This commit is contained in:
		
						commit
						dfc428c923
					
				
							
								
								
									
										15
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										15
									
								
								README.md
									
									
									
									
									
								
							| @ -22,7 +22,6 @@ Bill (Heath) Gates,65,3.3 | |||||||
| int main() { | int main() { | ||||||
|     ss::parser p{"students.csv", ","}; |     ss::parser p{"students.csv", ","}; | ||||||
|     if (!p.valid()) { |     if (!p.valid()) { | ||||||
|         std::cout << p.error_msg() << std::endl; |  | ||||||
|         exit(EXIT_FAILURE); |         exit(EXIT_FAILURE); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -232,14 +231,16 @@ Not yet documented. | |||||||
| 
 | 
 | ||||||
| ## Error handling | ## Error handling | ||||||
| 
 | 
 | ||||||
| Detailed error messages can be accessed via the **error_msg** method, and to enable them the error mode has to be changed to **error_mode::error_string** using the **set_error_mode** method: | Detailed error messages can be accessed via the **error_msg** method, and to enable them ss::string_error needs to be included in the setup. | ||||||
| ```cpp | ```cpp | ||||||
| void parser::set_error_mode(ss::error_mode); |  | ||||||
| const std::string& parser::error_msg(); | const std::string& parser::error_msg(); | ||||||
| bool parser::valid(); | bool parser::valid(); | ||||||
| bool parser::eof(); | bool parser::eof(); | ||||||
|  | 
 | ||||||
|  | // ... | ||||||
|  | ss::parser<ss::string_error> parser; | ||||||
| ``` | ``` | ||||||
| Error messages can always be disabled by setting the error mode to **error_mode::error_bool**. An error can be detected using the **valid** method which would return **false** if the file could not be opened, or if the conversion could not be made (invalid types, invalid number of columns, ...). The **eof** method can be used to detect if the end of the file was reached. | An error can be detected using the **valid** method which would return **false** if the file could not be opened, or if the conversion could not be made (invalid types, invalid number of columns, ...). The **eof** method can be used to detect if the end of the file was reached. | ||||||
| 
 | 
 | ||||||
| ## Substitute conversions | ## Substitute conversions | ||||||
| 
 | 
 | ||||||
| @ -340,7 +341,7 @@ p.try_next<ss::nx<shape, shape::circle, shape::square>, udbl>( | |||||||
|             } |             } | ||||||
|         }); |         }); | ||||||
| ``` | ``` | ||||||
| It is a bit less readable, but it removes the need to check which conversion was invoked. The **composite** also has an **on_error** method which accepts a lambda which will be invoked if no previous conversions were successful. The lambda can take no arguments or just one argument, an **std::string**, in which the error message is stored if **error_mode** is set to **error_mode::error_string**:  | It is a bit less readable, but it removes the need to check which conversion was invoked. The **composite** also has an **on_error** method which accepts a lambda which will be invoked if no previous conversions were successful. The lambda can take no arguments or just one argument, an **std::string**, in which the error message is stored if **string_error** is enabled:  | ||||||
| ```cpp | ```cpp | ||||||
| p.try_next<int>() | p.try_next<int>() | ||||||
|     .on_error([](const std::string& e) { /* int conversion failed */ }) |     .on_error([](const std::string& e) { /* int conversion failed */ }) | ||||||
| @ -371,8 +372,8 @@ if (c.valid()) { | |||||||
|     // do something with s |     // do something with s | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| All special types and restrictions work on the converter too. Error handling is | All setup parameters, special types and restrictions work on the converter too.   | ||||||
| also identical to error handling of the parser. | Error handling is also identical to error handling of the parser. | ||||||
| 
 | 
 | ||||||
| The converter has also the ability to just split the line, tho it does not change it (kinda statically), hence the name of the library. It returns an **std::vector** of pairs of pointers, begin and end, each pair representing a split segment (column) of the whole string. The vector can then be used in a overloaded **convert** method. This allows the reuse of the same line without splitting it on every conversion.  | The converter has also the ability to just split the line, tho it does not change it (kinda statically), hence the name of the library. It returns an **std::vector** of pairs of pointers, begin and end, each pair representing a split segment (column) of the whole string. The vector can then be used in a overloaded **convert** method. This allows the reuse of the same line without splitting it on every conversion.  | ||||||
| ```cpp | ```cpp | ||||||
|  | |||||||
| @ -109,9 +109,13 @@ constexpr bool tied_class_v = tied_class<Ts...>::value; | |||||||
| 
 | 
 | ||||||
| template <typename... Matchers> | template <typename... Matchers> | ||||||
| class converter { | class converter { | ||||||
|     constexpr static auto default_delimiter = ","; |  | ||||||
|     using line_ptr_type = typename splitter<Matchers...>::line_ptr_type; |     using line_ptr_type = typename splitter<Matchers...>::line_ptr_type; | ||||||
| 
 | 
 | ||||||
|  |     constexpr static auto string_error = setup<Matchers...>::string_error; | ||||||
|  |     constexpr static auto default_delimiter = ","; | ||||||
|  | 
 | ||||||
|  |     using error_type = ss::ternary_t<string_error, std::string, bool>; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     // parses line with given delimiter, returns a 'T' object created with
 |     // parses line with given delimiter, returns a 'T' object created with
 | ||||||
|     // extracted values of type 'Ts'
 |     // extracted values of type 'Ts'
 | ||||||
| @ -173,23 +177,23 @@ public: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool valid() const { |     bool valid() const { | ||||||
|         return (error_mode_ == error_mode::error_string) ? string_error_.empty() |         if constexpr (string_error) { | ||||||
|                                                          : bool_error_ == false; |             return error_.empty(); | ||||||
|  |         } else { | ||||||
|  |             return !error_; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const std::string& error_msg() const { | ||||||
|  |         static_assert(string_error, | ||||||
|  |                       "'string_error' needs to be enabled to use 'error_msg'"); | ||||||
|  |         return error_; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool unterminated_quote() const { |     bool unterminated_quote() const { | ||||||
|         return splitter_.unterminated_quote(); |         return splitter_.unterminated_quote(); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const std::string& error_msg() const { |  | ||||||
|         return string_error_; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void set_error_mode(error_mode mode) { |  | ||||||
|         splitter_.set_error_mode(mode); |  | ||||||
|         error_mode_ = mode; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     // 'splits' string by given delimiter, returns vector of pairs which
 |     // 'splits' string by given delimiter, returns vector of pairs which
 | ||||||
|     // contain the beginnings and the ends of each column of the string
 |     // contain the beginnings and the ends of each column of the string
 | ||||||
|     const split_input& split(line_ptr_type line, |     const split_input& split(line_ptr_type line, | ||||||
| @ -203,7 +207,6 @@ public: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| 
 |  | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
|     // resplit
 |     // resplit
 | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
| @ -218,8 +221,11 @@ private: | |||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     void clear_error() { |     void clear_error() { | ||||||
|         string_error_.clear(); |         if constexpr (string_error) { | ||||||
|         bool_error_ = false; |             error_.clear(); | ||||||
|  |         } else { | ||||||
|  |             error_ = false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     std::string error_sufix(const string_range msg, size_t pos) const { |     std::string error_sufix(const string_range msg, size_t pos) const { | ||||||
| @ -234,44 +240,43 @@ private: | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_unterminated_quote() { |     void set_error_unterminated_quote() { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append(splitter_.error_msg()); |             error_.append(splitter_.error_msg()); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_invalid_conversion(const string_range msg, size_t pos) { |     void set_error_invalid_conversion(const string_range msg, size_t pos) { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("invalid conversion for parameter ") |             error_.append("invalid conversion for parameter ") | ||||||
|                 .append(error_sufix(msg, pos)); |                 .append(error_sufix(msg, pos)); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_validate(const char* const error, const string_range msg, |     void set_error_validate(const char* const error, const string_range msg, | ||||||
|                             size_t pos) { |                             size_t pos) { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append(error).append(" ").append( |             error_.append(error).append(" ").append(error_sufix(msg, pos)); | ||||||
|                 error_sufix(msg, pos)); |  | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_number_of_colums(size_t expected_pos, size_t pos) { |     void set_error_number_of_colums(size_t expected_pos, size_t pos) { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("invalid number of columns, expected: ") |             error_.append("invalid number of columns, expected: ") | ||||||
|                 .append(std::to_string(expected_pos)) |                 .append(std::to_string(expected_pos)) | ||||||
|                 .append(", got: ") |                 .append(", got: ") | ||||||
|                 .append(std::to_string(pos)); |                 .append(std::to_string(pos)); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -374,12 +379,10 @@ private: | |||||||
|     // members
 |     // members
 | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     std::string string_error_; |     error_type error_; | ||||||
|     bool bool_error_; |  | ||||||
|     enum error_mode error_mode_ { error_mode::error_bool }; |  | ||||||
|     splitter<Matchers...> splitter_; |     splitter<Matchers...> splitter_; | ||||||
| 
 | 
 | ||||||
|     template <typename ...> |     template <typename...> | ||||||
|     friend class parser; |     friend class parser; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -15,6 +15,11 @@ template <typename... Matchers> | |||||||
| class parser { | class parser { | ||||||
|     struct none {}; |     struct none {}; | ||||||
| 
 | 
 | ||||||
|  |     constexpr static auto string_error = setup<Matchers...>::string_error; | ||||||
|  |     constexpr static auto multiline = setup<Matchers...>::multiline; | ||||||
|  | 
 | ||||||
|  |     using error_type = ss::ternary_t<string_error, std::string, bool>; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     parser(const std::string& file_name, |     parser(const std::string& file_name, | ||||||
|            const std::string& delim = ss::default_delimiter) |            const std::string& delim = ss::default_delimiter) | ||||||
| @ -35,17 +40,17 @@ public: | |||||||
|     parser& operator=(const parser& other) = delete; |     parser& operator=(const parser& other) = delete; | ||||||
| 
 | 
 | ||||||
|     bool valid() const { |     bool valid() const { | ||||||
|         return (error_mode_ == error_mode::error_string) ? string_error_.empty() |         if constexpr (string_error) { | ||||||
|                                                          : bool_error_ == false; |             return error_.empty(); | ||||||
|     } |         } else { | ||||||
| 
 |             return !error_; | ||||||
|     void set_error_mode(error_mode mode) { |         } | ||||||
|         error_mode_ = mode; |  | ||||||
|         reader_.set_error_mode(mode); |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const std::string& error_msg() const { |     const std::string& error_msg() const { | ||||||
|         return string_error_; |         static_assert(string_error, | ||||||
|  |                       "'string_error' needs to be enabled to use 'error_msg'"); | ||||||
|  |         return error_; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool eof() const { |     bool eof() const { | ||||||
| @ -124,6 +129,10 @@ public: | |||||||
|                 if constexpr (std::is_invocable_v<Fun>) { |                 if constexpr (std::is_invocable_v<Fun>) { | ||||||
|                     fun(); |                     fun(); | ||||||
|                 } else { |                 } else { | ||||||
|  |                     static_assert(string_error, | ||||||
|  |                                   "to enable error messages within the " | ||||||
|  |                                   "on_error method " | ||||||
|  |                                   "callback string_error needs to be enabled"); | ||||||
|                     std::invoke(std::forward<Fun>(fun), parser_.error_msg()); |                     std::invoke(std::forward<Fun>(fun), parser_.error_msg()); | ||||||
|                 } |                 } | ||||||
|             } |             } | ||||||
| @ -246,34 +255,40 @@ private: | |||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     void clear_error() { |     void clear_error() { | ||||||
|         string_error_.clear(); |         if constexpr (string_error) { | ||||||
|         bool_error_ = false; |             error_.clear(); | ||||||
|  |         } else { | ||||||
|  |             error_ = false; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_failed_check() { |     void set_error_failed_check() { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.append(file_name_).append(" failed check."); |             error_.append(file_name_).append(" failed check."); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_file_not_open() { |     void set_error_file_not_open() { | ||||||
|         string_error_.append(file_name_).append(" could not be opened."); |         if constexpr (string_error) { | ||||||
|         bool_error_ = true; |             error_.append(file_name_).append(" could not be opened."); | ||||||
|  |         } else { | ||||||
|  |             error_ = true; | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_eof_reached() { |     void set_error_eof_reached() { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.append(file_name_).append(" reached end of file."); |             error_.append(file_name_).append(" reached end of file."); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_invalid_conversion() { |     void set_error_invalid_conversion() { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.append(file_name_) |             error_.append(file_name_) | ||||||
|                 .append(" ") |                 .append(" ") | ||||||
|                 .append(std::to_string(line_number_)) |                 .append(std::to_string(line_number_)) | ||||||
|                 .append(": ") |                 .append(": ") | ||||||
| @ -282,7 +297,7 @@ private: | |||||||
|                 .append(reader_.buffer_) |                 .append(reader_.buffer_) | ||||||
|                 .append("\""); |                 .append("\""); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -360,7 +375,7 @@ private: | |||||||
| 
 | 
 | ||||||
|             size_t size = remove_eol(next_line_buffer_, ssize); |             size_t size = remove_eol(next_line_buffer_, ssize); | ||||||
| 
 | 
 | ||||||
|             if constexpr (setup<Matchers...>::escape::enabled) { |             if constexpr (multiline && setup<Matchers...>::escape::enabled) { | ||||||
|                 while (escaped_eol(size)) { |                 while (escaped_eol(size)) { | ||||||
|                     if (!append_line(next_line_buffer_, size)) { |                     if (!append_line(next_line_buffer_, size)) { | ||||||
|                         return false; |                         return false; | ||||||
| @ -370,7 +385,7 @@ private: | |||||||
| 
 | 
 | ||||||
|             next_line_converter_.split(next_line_buffer_, delim_); |             next_line_converter_.split(next_line_buffer_, delim_); | ||||||
| 
 | 
 | ||||||
|             if constexpr (setup<Matchers...>::quote::enabled) { |             if constexpr (multiline && setup<Matchers...>::quote::enabled) { | ||||||
|                 while (unterminated_quote()) { |                 while (unterminated_quote()) { | ||||||
|                     if (!append_line(next_line_buffer_, size)) { |                     if (!append_line(next_line_buffer_, size)) { | ||||||
|                         return false; |                         return false; | ||||||
| @ -382,11 +397,6 @@ private: | |||||||
|             return true; |             return true; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         void set_error_mode(error_mode mode) { |  | ||||||
|             converter_.set_error_mode(mode); |  | ||||||
|             next_line_converter_.set_error_mode(mode); |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         void update() { |         void update() { | ||||||
|             std::swap(buffer_, next_line_buffer_); |             std::swap(buffer_, next_line_buffer_); | ||||||
|             std::swap(converter_, next_line_converter_); |             std::swap(converter_, next_line_converter_); | ||||||
| @ -478,9 +488,7 @@ private: | |||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     std::string file_name_; |     std::string file_name_; | ||||||
|     std::string string_error_; |     error_type error_; | ||||||
|     bool bool_error_{false}; |  | ||||||
|     error_mode error_mode_{error_mode::error_bool}; |  | ||||||
|     reader reader_; |     reader reader_; | ||||||
|     size_t line_number_{0}; |     size_t line_number_{0}; | ||||||
|     bool eof_{false}; |     bool eof_{false}; | ||||||
|  | |||||||
| @ -79,9 +79,8 @@ struct get_matcher; | |||||||
| 
 | 
 | ||||||
| template <template <char...> class Matcher, typename T, typename... Ts> | template <template <char...> class Matcher, typename T, typename... Ts> | ||||||
| struct get_matcher<Matcher, T, Ts...> { | struct get_matcher<Matcher, T, Ts...> { | ||||||
|     using type = |     using type = ternary_t<is_instance_of_matcher<T, Matcher>::value, T, | ||||||
|         typename ternary<is_instance_of_matcher<T, Matcher>::value, T, |                            typename get_matcher<Matcher, Ts...>::type>; | ||||||
|                          typename get_matcher<Matcher, Ts...>::type>::type; |  | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <template <char...> class Matcher> | template <template <char...> class Matcher> | ||||||
| @ -92,11 +91,33 @@ struct get_matcher<Matcher> { | |||||||
| template <template <char...> class Matcher, typename... Ts> | template <template <char...> class Matcher, typename... Ts> | ||||||
| using get_matcher_t = typename get_matcher<Matcher, Ts...>::type; | using get_matcher_t = typename get_matcher<Matcher, Ts...>::type; | ||||||
| 
 | 
 | ||||||
|  | class multiline; | ||||||
|  | class string_error; | ||||||
|  | 
 | ||||||
| template <typename... Ts> | template <typename... Ts> | ||||||
| struct setup { | struct setup { | ||||||
|  | private: | ||||||
|  |     template <typename T> | ||||||
|  |     struct is_multiline : std::is_same<T, multiline> {}; | ||||||
|  | 
 | ||||||
|  |     constexpr static auto count_multiline = count<is_multiline, Ts...>::size; | ||||||
|  | 
 | ||||||
|  |     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; | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|     using quote = get_matcher_t<quote, Ts...>; |     using quote = get_matcher_t<quote, Ts...>; | ||||||
|     using trim = get_matcher_t<trim, Ts...>; |     using trim = get_matcher_t<trim, Ts...>; | ||||||
|     using escape = get_matcher_t<escape, Ts...>; |     using escape = get_matcher_t<escape, Ts...>; | ||||||
|  |     constexpr static bool multiline = (count_multiline == 1); | ||||||
|  |     constexpr static bool string_error = (count_string_error == 1); | ||||||
|  | 
 | ||||||
|  |     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" | #define ASSERT_MSG "cannot have the same match character in multiple matchers" | ||||||
|     static_assert(!matches_intersect<quote, trim>(), ASSERT_MSG); |     static_assert(!matches_intersect<quote, trim>(), ASSERT_MSG); | ||||||
|  | |||||||
| @ -10,14 +10,11 @@ | |||||||
| 
 | 
 | ||||||
| namespace ss { | namespace ss { | ||||||
| 
 | 
 | ||||||
| // TODO move to common
 | // TODO move to common or something
 | ||||||
| using string_range = std::pair<const char*, const char*>; | using string_range = std::pair<const char*, const char*>; | ||||||
| using split_input = std::vector<string_range>; | using split_input = std::vector<string_range>; | ||||||
| constexpr static auto default_delimiter = ","; | constexpr static auto default_delimiter = ","; | ||||||
| 
 | 
 | ||||||
| // the error can be set inside a string, or a bool
 |  | ||||||
| enum class error_mode { error_string, error_bool }; |  | ||||||
| 
 |  | ||||||
| template <typename... Ts> | template <typename... Ts> | ||||||
| class splitter { | class splitter { | ||||||
| private: | private: | ||||||
| @ -25,35 +22,43 @@ private: | |||||||
|     using trim = typename setup<Ts...>::trim; |     using trim = typename setup<Ts...>::trim; | ||||||
|     using escape = typename setup<Ts...>::escape; |     using escape = typename setup<Ts...>::escape; | ||||||
| 
 | 
 | ||||||
|  |     constexpr static auto string_error = setup<Ts...>::string_error; | ||||||
|     constexpr static auto is_const_line = !quote::enabled && !escape::enabled; |     constexpr static auto is_const_line = !quote::enabled && !escape::enabled; | ||||||
| 
 | 
 | ||||||
|  |     using error_type = ss::ternary_t<string_error, std::string, bool>; | ||||||
|  | 
 | ||||||
| public: | public: | ||||||
|     using line_ptr_type = |     using line_ptr_type = ternary_t<is_const_line, const char*, char*>; | ||||||
|         typename ternary<is_const_line, const char*, char*>::type; |  | ||||||
| 
 | 
 | ||||||
|     bool valid() const { |     bool valid() const { | ||||||
|         return (error_mode_ == error_mode::error_string) ? string_error_.empty() |         if constexpr (string_error) { | ||||||
|                                                          : bool_error_ == false; |             return error_.empty(); | ||||||
|  |         } else { | ||||||
|  |             return !error_; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     const std::string& error_msg() const { | ||||||
|  |         static_assert(string_error, | ||||||
|  |                       "'string_error' needs to be enabled to use 'error_msg'"); | ||||||
|  |         return error_; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     bool unterminated_quote() const { |     bool unterminated_quote() const { | ||||||
|         return unterminated_quote_; |         return unterminated_quote_; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const std::string& error_msg() const { |  | ||||||
|         return string_error_; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     void set_error_mode(error_mode mode) { |  | ||||||
|         error_mode_ = mode; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     const split_input& split(line_ptr_type new_line, |     const split_input& split(line_ptr_type new_line, | ||||||
|                              const std::string& delimiter = default_delimiter) { |                              const std::string& delimiter = default_delimiter) { | ||||||
|         split_input_.clear(); |         split_input_.clear(); | ||||||
|         return resplit(new_line, -1, delimiter); |         return resplit(new_line, -1, delimiter); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  | private: | ||||||
|  |     ////////////////
 | ||||||
|  |     // resplit
 | ||||||
|  |     ////////////////
 | ||||||
|  | 
 | ||||||
|     void adjust_ranges(const char* old_line) { |     void adjust_ranges(const char* old_line) { | ||||||
|         for (auto& [begin, end] : split_input_) { |         for (auto& [begin, end] : split_input_) { | ||||||
|             begin = begin - old_line + line_; |             begin = begin - old_line + line_; | ||||||
| @ -61,11 +66,6 @@ public: | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: |  | ||||||
|     ////////////////
 |  | ||||||
|     // resplit
 |  | ||||||
|     ////////////////
 |  | ||||||
| 
 |  | ||||||
|     const split_input& resplit( |     const split_input& resplit( | ||||||
|         line_ptr_type new_line, ssize_t new_size, |         line_ptr_type new_line, ssize_t new_size, | ||||||
|         const std::string& delimiter = default_delimiter) { |         const std::string& delimiter = default_delimiter) { | ||||||
| @ -96,48 +96,50 @@ private: | |||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     void clear_error() { |     void clear_error() { | ||||||
|         string_error_.clear(); |         if constexpr (string_error) { | ||||||
|         bool_error_ = false; |             error_.clear(); | ||||||
|  |         } else { | ||||||
|  |             error_ = false; | ||||||
|  |         } | ||||||
|         unterminated_quote_ = false; |         unterminated_quote_ = false; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_empty_delimiter() { |     void set_error_empty_delimiter() { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("empty delimiter"); |             error_.append("empt  delimiter"); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_mismatched_quote(size_t n) { |     void set_error_mismatched_quote(size_t n) { | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("mismatched quote at position: " + |             error_.append("mismatched quote at position: " + std::to_string(n)); | ||||||
|                                  std::to_string(n)); |  | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_unterminated_quote() { |     void set_error_unterminated_quote() { | ||||||
|         unterminated_quote_ = true; |         unterminated_quote_ = true; | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("unterminated quote"); |             error_.append("unterminated quote"); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     void set_error_invalid_resplit() { |     void set_error_invalid_resplit() { | ||||||
|         unterminated_quote_ = false; |         unterminated_quote_ = false; | ||||||
|         if (error_mode_ == error_mode::error_string) { |         if constexpr (string_error) { | ||||||
|             string_error_.clear(); |             error_.clear(); | ||||||
|             string_error_.append("invalid resplit, new line must be longer" |             error_.append("invalid resplit, new line must be longer" | ||||||
|                                  "than the end of the last slice"); |                           "than the end of the last slice"); | ||||||
|         } else { |         } else { | ||||||
|             bool_error_ = true; |             error_ = true; | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -373,10 +375,10 @@ private: | |||||||
|     // members
 |     // members
 | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
| 
 | 
 | ||||||
|     std::string string_error_; |     static_assert(std::is_same_v<error_type, bool> || | ||||||
|     bool bool_error_{false}; |                   std::is_same_v<error_type, std::string>); | ||||||
|  |     error_type error_; | ||||||
|     bool unterminated_quote_{false}; |     bool unterminated_quote_{false}; | ||||||
|     enum error_mode error_mode_ { error_mode::error_bool }; |  | ||||||
|     line_ptr_type begin_; |     line_ptr_type begin_; | ||||||
|     line_ptr_type curr_; |     line_ptr_type curr_; | ||||||
|     line_ptr_type end_; |     line_ptr_type end_; | ||||||
|  | |||||||
| @ -230,8 +230,11 @@ using filter_not_t = typename filter_not<Trait, Ts...>::type; | |||||||
| // count
 | // count
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| 
 | 
 | ||||||
|  | template <template <typename...> class Trait, typename... Ts> | ||||||
|  | struct count; | ||||||
|  | 
 | ||||||
| template <template <typename...> class Trait, typename T, typename... Ts> | template <template <typename...> class Trait, typename T, typename... Ts> | ||||||
| struct count { | struct count<Trait, T, Ts...> { | ||||||
|     static constexpr size_t size = |     static constexpr size_t size = | ||||||
|         std::tuple_size<filter_if_t<Trait, T, Ts...>>::value; |         std::tuple_size<filter_if_t<Trait, T, Ts...>>::value; | ||||||
| }; | }; | ||||||
| @ -241,12 +244,20 @@ struct count<Trait, T> { | |||||||
|     static constexpr size_t size = Trait<T>::value; |     static constexpr size_t size = Trait<T>::value; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | template <template <typename...> class Trait> | ||||||
|  | struct count<Trait> { | ||||||
|  |     static constexpr size_t size = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| // count not
 | // count not
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| 
 | 
 | ||||||
|  | template <template <typename...> class Trait, typename... Ts> | ||||||
|  | struct count_not; | ||||||
|  | 
 | ||||||
| template <template <typename...> class Trait, typename T, typename... Ts> | template <template <typename...> class Trait, typename T, typename... Ts> | ||||||
| struct count_not { | struct count_not<Trait, T, Ts...> { | ||||||
|     static constexpr size_t size = |     static constexpr size_t size = | ||||||
|         std::tuple_size<filter_not_t<Trait, T, Ts...>>::value; |         std::tuple_size<filter_not_t<Trait, T, Ts...>>::value; | ||||||
| }; | }; | ||||||
| @ -256,6 +267,11 @@ struct count_not<Trait, T> { | |||||||
|     static constexpr size_t size = !Trait<T>::value; |     static constexpr size_t size = !Trait<T>::value; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | template <template <typename...> class Trait> | ||||||
|  | struct count_not<Trait> { | ||||||
|  |     static constexpr size_t size = 0; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| // all of
 | // all of
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| @ -331,6 +347,9 @@ struct ternary<false, T, U> { | |||||||
|     using type = U; |     using type = U; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
|  | template <bool B, typename T, typename U> | ||||||
|  | using ternary_t = typename ternary<B, T, U>::type; | ||||||
|  | 
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| // tuple to struct
 | // tuple to struct
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
|  | |||||||
| @ -384,13 +384,7 @@ TEST_CASE( | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST_CASE("converter test error mode") { | TEST_CASE("converter test error mode") { | ||||||
|     ss::converter c; |     ss::converter<ss::string_error> c; | ||||||
| 
 |  | ||||||
|     c.convert<int>("junk"); |  | ||||||
|     CHECK(!c.valid()); |  | ||||||
|     CHECK(c.error_msg().empty()); |  | ||||||
| 
 |  | ||||||
|     c.set_error_mode(ss::error_mode::error_string); |  | ||||||
|     c.convert<int>("junk"); |     c.convert<int>("junk"); | ||||||
|     CHECK(!c.valid()); |     CHECK(!c.valid()); | ||||||
|     CHECK(!c.error_msg().empty()); |     CHECK(!c.error_msg().empty()); | ||||||
| @ -444,8 +438,9 @@ TEST_CASE("converter test converter with quotes spacing and escaping") { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST_CASE("converter test invalid split conversions") { | TEST_CASE("converter test invalid split conversions") { | ||||||
|     ss::converter<ss::escape<'\\'>, ss::trim<' '>, ss::quote<'"'>> c; |     ss::converter<ss::string_error, ss::escape<'\\'>, ss::trim<' '>, | ||||||
|     c.set_error_mode(ss::error_mode::error_string); |                   ss::quote<'"'>> | ||||||
|  |         c; | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         // mismatched quote
 |         // mismatched quote
 | ||||||
| @ -453,6 +448,7 @@ TEST_CASE("converter test invalid split conversions") { | |||||||
|             buff(R"(  "just  , some ,   "12.3","a"  )")); |             buff(R"(  "just  , some ,   "12.3","a"  )")); | ||||||
|         CHECK(!c.valid()); |         CHECK(!c.valid()); | ||||||
|         CHECK(!c.unterminated_quote()); |         CHECK(!c.unterminated_quote()); | ||||||
|  |         CHECK(!c.error_msg().empty()); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
| @ -461,5 +457,6 @@ TEST_CASE("converter test invalid split conversions") { | |||||||
|             buff(R"(  ju\,st  ,  "so,me"  ,   12.34     ,   "str""ings)")); |             buff(R"(  ju\,st  ,  "so,me"  ,   12.34     ,   "str""ings)")); | ||||||
|         CHECK(!c.valid()); |         CHECK(!c.valid()); | ||||||
|         CHECK(c.unterminated_quote()); |         CHECK(c.unterminated_quote()); | ||||||
|  |         CHECK(!c.error_msg().empty()); | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -59,7 +59,6 @@ TEST_CASE("parser test various cases") { | |||||||
|         ss::parser p0{std::move(p)}; |         ss::parser p0{std::move(p)}; | ||||||
|         p = std::move(p0); |         p = std::move(p0); | ||||||
| 
 | 
 | ||||||
|         p.set_error_mode(ss::error_mode::error_string); |  | ||||||
|         std::vector<X> i; |         std::vector<X> i; | ||||||
| 
 | 
 | ||||||
|         while (!p.eof()) { |         while (!p.eof()) { | ||||||
| @ -190,8 +189,7 @@ TEST_CASE("parser test composite conversion") { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ss::parser p{f.name, ","}; |     ss::parser<ss::string_error> p{f.name, ","}; | ||||||
|     p.set_error_mode(ss::error_mode::error_string); |  | ||||||
|     auto fail = [] { FAIL(""); }; |     auto fail = [] { FAIL(""); }; | ||||||
|     auto expect_error = [](auto error) { CHECK(!error.empty()); }; |     auto expect_error = [](auto error) { CHECK(!error.empty()); }; | ||||||
| 
 | 
 | ||||||
| @ -503,14 +501,7 @@ TEST_CASE("parser test error mode") { | |||||||
|         out << "junk" << std::endl; |         out << "junk" << std::endl; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ss::parser p(f.name, ","); |     ss::parser<ss::string_error> p(f.name, ","); | ||||||
| 
 |  | ||||||
|     REQUIRE(!p.eof()); |  | ||||||
|     p.get_next<int>(); |  | ||||||
|     CHECK(!p.valid()); |  | ||||||
|     CHECK(p.error_msg().empty()); |  | ||||||
| 
 |  | ||||||
|     p.set_error_mode(ss::error_mode::error_string); |  | ||||||
| 
 | 
 | ||||||
|     REQUIRE(!p.eof()); |     REQUIRE(!p.eof()); | ||||||
|     p.get_next<int>(); |     p.get_next<int>(); | ||||||
| @ -538,8 +529,7 @@ TEST_CASE("parser test csv on multiple lines with quotes") { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ss::parser<ss::quote<'"'>> p{f.name, ","}; |     ss::parser<ss::multiline, ss::quote<'"'>> p{f.name, ","}; | ||||||
|     p.set_error_mode(ss::error_mode::error_string); |  | ||||||
|     std::vector<X> i; |     std::vector<X> i; | ||||||
| 
 | 
 | ||||||
|     while (!p.eof()) { |     while (!p.eof()) { | ||||||
| @ -548,6 +538,12 @@ TEST_CASE("parser test csv on multiple lines with quotes") { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     CHECK(std::equal(i.begin(), i.end(), data.begin())); |     CHECK(std::equal(i.begin(), i.end(), data.begin())); | ||||||
|  | 
 | ||||||
|  |     ss::parser<ss::quote<'"'>> p_no_multiline{f.name, ","}; | ||||||
|  |     while (!p.eof()) { | ||||||
|  |         auto a = p_no_multiline.get_next<int, double, std::string>(); | ||||||
|  |         CHECK(!p.valid()); | ||||||
|  |     } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| std::string no_escape(std::string& s) { | std::string no_escape(std::string& s) { | ||||||
| @ -569,8 +565,7 @@ TEST_CASE("parser test csv on multiple lines with escapes") { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ss::parser<ss::escape<'\\'>> p{f.name, ","}; |     ss::parser<ss::multiline, ss::escape<'\\'>> p{f.name, ","}; | ||||||
|     p.set_error_mode(ss::error_mode::error_string); |  | ||||||
|     std::vector<X> i; |     std::vector<X> i; | ||||||
| 
 | 
 | ||||||
|     while (!p.eof()) { |     while (!p.eof()) { | ||||||
| @ -579,4 +574,10 @@ TEST_CASE("parser test csv on multiple lines with escapes") { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     CHECK(std::equal(i.begin(), i.end(), data.begin())); |     CHECK(std::equal(i.begin(), i.end(), data.begin())); | ||||||
|  | 
 | ||||||
|  |     ss::parser<ss::escape<'\\'>> p_no_multiline{f.name, ","}; | ||||||
|  |     while (!p.eof()) { | ||||||
|  |         auto a = p_no_multiline.get_next<int, double, std::string>(); | ||||||
|  |         CHECK(!p.valid()); | ||||||
|  |     } | ||||||
| } | } | ||||||
|  | |||||||
| @ -480,13 +480,7 @@ TEST_CASE("splitter test error mode") { | |||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         // empty delimiter
 |         // empty delimiter
 | ||||||
|         ss::splitter s; |         ss::splitter<ss::string_error> s; | ||||||
|         s.split(buff("just,some,strings"), ""); |  | ||||||
|         CHECK(!s.valid()); |  | ||||||
|         CHECK(!s.unterminated_quote()); |  | ||||||
|         CHECK(s.error_msg().empty()); |  | ||||||
| 
 |  | ||||||
|         s.set_error_mode(ss::error_mode::error_string); |  | ||||||
|         s.split(buff("just,some,strings"), ""); |         s.split(buff("just,some,strings"), ""); | ||||||
|         CHECK(!s.valid()); |         CHECK(!s.valid()); | ||||||
|         CHECK(!s.unterminated_quote()); |         CHECK(!s.unterminated_quote()); | ||||||
| @ -495,13 +489,7 @@ TEST_CASE("splitter test error mode") { | |||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         // unterminated quote
 |         // unterminated quote
 | ||||||
|         ss::splitter<ss::quote<'"'>> s; |         ss::splitter<ss::string_error, ss::quote<'"'>> s; | ||||||
|         s.split(buff("\"just")); |  | ||||||
|         CHECK(!s.valid()); |  | ||||||
|         CHECK(s.unterminated_quote()); |  | ||||||
|         CHECK(s.error_msg().empty()); |  | ||||||
| 
 |  | ||||||
|         s.set_error_mode(ss::error_mode::error_string); |  | ||||||
|         s.split(buff("\"just")); |         s.split(buff("\"just")); | ||||||
|         CHECK(!s.valid()); |         CHECK(!s.valid()); | ||||||
|         CHECK(s.unterminated_quote()); |         CHECK(s.unterminated_quote()); | ||||||
| @ -691,27 +679,33 @@ TEST_CASE("splitter test unterminated quote") { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST_CASE("splitter test invalid splits") { | TEST_CASE("splitter test invalid splits") { | ||||||
|     ss::converter<ss::quote<'"'>, ss::trim<' '>, ss::escape<'\\'>> c; |     ss::converter<ss::string_error, ss::quote<'"'>, ss::trim<' '>, | ||||||
|  |                   ss::escape<'\\'>> | ||||||
|  |         c; | ||||||
|     auto& s = c.splitter; |     auto& s = c.splitter; | ||||||
| 
 | 
 | ||||||
|     // empty delimiter
 |     // empty delimiter
 | ||||||
|     s.split(buff("some,random,strings"), ""); |     s.split(buff("some,random,strings"), ""); | ||||||
|     CHECK(!s.valid()); |     CHECK(!s.valid()); | ||||||
|     CHECK(!s.unterminated_quote()); |     CHECK(!s.unterminated_quote()); | ||||||
|  |     CHECK(!s.error_msg().empty()); | ||||||
| 
 | 
 | ||||||
|     // mismatched delimiter
 |     // mismatched delimiter
 | ||||||
|     s.split(buff(R"(some,"random,"strings")")); |     s.split(buff(R"(some,"random,"strings")")); | ||||||
|     CHECK(!s.valid()); |     CHECK(!s.valid()); | ||||||
|     CHECK(!s.unterminated_quote()); |     CHECK(!s.unterminated_quote()); | ||||||
|  |     CHECK(!s.error_msg().empty()); | ||||||
| 
 | 
 | ||||||
|     // unterminated quote
 |     // unterminated quote
 | ||||||
|     s.split(buff("some,random,\"strings")); |     s.split(buff("some,random,\"strings")); | ||||||
|     CHECK(!s.valid()); |     CHECK(!s.valid()); | ||||||
|     CHECK(s.unterminated_quote()); |     CHECK(s.unterminated_quote()); | ||||||
|  |     CHECK(!s.error_msg().empty()); | ||||||
| 
 | 
 | ||||||
|     // invalid resplit
 |     // invalid resplit
 | ||||||
|     char new_line[] = "some"; |     char new_line[] = "some"; | ||||||
|     auto a = c.resplit(new_line, strlen(new_line)); |     auto a = c.resplit(new_line, strlen(new_line)); | ||||||
|     CHECK(!s.valid()); |     CHECK(!s.valid()); | ||||||
|     CHECK(!s.unterminated_quote()); |     CHECK(!s.unterminated_quote()); | ||||||
|  |     CHECK(!s.error_msg().empty()); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 red0124
						red0124