mirror of
				https://github.com/red0124/ssp.git
				synced 2025-10-31 13:16:45 +01:00 
			
		
		
		
	add splitter, update converter and parser, update unit tests
This commit is contained in:
		
							parent
							
								
									cd264faa70
								
							
						
					
					
						commit
						69d6df12be
					
				| @ -1,17 +1,4 @@ | |||||||
| #pragma once | #pragma once | ||||||
| 
 |  | ||||||
| // TODO remove
 |  | ||||||
| #include <iostream> |  | ||||||
| #ifndef DBG |  | ||||||
| void log(const std::string& log) { |  | ||||||
|     std::cout << log << std::endl; |  | ||||||
| } |  | ||||||
| #else |  | ||||||
| void log(const std::string&) { |  | ||||||
| } |  | ||||||
| #endif |  | ||||||
| //
 |  | ||||||
| //
 |  | ||||||
| #include "extract.hpp" | #include "extract.hpp" | ||||||
| #include "function_traits.hpp" | #include "function_traits.hpp" | ||||||
| #include "restrictions.hpp" | #include "restrictions.hpp" | ||||||
| @ -20,10 +7,6 @@ void log(const std::string&) { | |||||||
| #include <type_traits> | #include <type_traits> | ||||||
| #include <vector> | #include <vector> | ||||||
| 
 | 
 | ||||||
| constexpr auto space = '_'; |  | ||||||
| constexpr auto escaping = true; |  | ||||||
| constexpr auto quote = '"'; |  | ||||||
| 
 |  | ||||||
| namespace ss { | namespace ss { | ||||||
| INIT_HAS_METHOD(tied); | INIT_HAS_METHOD(tied); | ||||||
| INIT_HAS_METHOD(ss_valid); | INIT_HAS_METHOD(ss_valid); | ||||||
| @ -122,29 +105,368 @@ constexpr bool tied_class_v = tied_class<Ts...>::value; | |||||||
| // the error can be set inside a string, or a bool
 | // the error can be set inside a string, or a bool
 | ||||||
| enum class error_mode { error_string, error_bool }; | enum class error_mode { error_string, error_bool }; | ||||||
| 
 | 
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | ///////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | template <char... Cs> | ||||||
|  | struct matcher { | ||||||
|  | private: | ||||||
|  |     template <char X, char... Xs> | ||||||
|  |     static bool match_impl(char c) { | ||||||
|  |         if constexpr (sizeof...(Xs) != 0) { | ||||||
|  |             return (c == X) || match_impl<Xs...>(c); | ||||||
|  |         } | ||||||
|  |         return (c == X); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     static bool match(char c) { | ||||||
|  |         return match_impl<Cs...>(c); | ||||||
|  |     } | ||||||
|  |     constexpr static bool enabled = true; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <> | ||||||
|  | class matcher<'\0'> { | ||||||
|  | public: | ||||||
|  |     constexpr static bool enabled = false; | ||||||
|  |     static bool match(char c) = delete; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ////////////////
 | ||||||
|  | // is instance of
 | ||||||
|  | ////////////////
 | ||||||
|  | 
 | ||||||
|  | template <typename T, template <char...> class Template> | ||||||
|  | struct is_instance_of_char { | ||||||
|  |     constexpr static bool value = false; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <char... Ts, template <char...> class Template> | ||||||
|  | struct is_instance_of_char<Template<Ts...>, Template> { | ||||||
|  |     constexpr static bool value = true; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ///////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | template <char... Cs> | ||||||
|  | struct quote : matcher<Cs...> {}; | ||||||
|  | 
 | ||||||
|  | template <char... Cs> | ||||||
|  | struct trim : matcher<Cs...> {}; | ||||||
|  | 
 | ||||||
|  | template <char... Cs> | ||||||
|  | struct escape : matcher<Cs...> {}; | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////////
 | ||||||
|  | // -> type traits
 | ||||||
|  | template <bool B, typename T, typename U> | ||||||
|  | struct if_then_else; | ||||||
|  | 
 | ||||||
|  | template <typename T, typename U> | ||||||
|  | struct if_then_else<true, T, U> { | ||||||
|  |     using type = T; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <typename T, typename U> | ||||||
|  | struct if_then_else<false, T, U> { | ||||||
|  |     using type = U; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | //////////////////////////////////////////////
 | ||||||
|  | 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 = | ||||||
|  |         typename if_then_else<is_instance_of_char<T, Matcher>::value, T, | ||||||
|  |                               typename get_matcher<Matcher, Ts...>::type>::type; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <template <char...> class Matcher> | ||||||
|  | struct get_matcher<Matcher> { | ||||||
|  |     using type = Matcher<'\0'>; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ///////////////////////////////////////////////
 | ||||||
|  | // TODO add restriction
 | ||||||
|  | template <typename... Ts> | ||||||
|  | struct setup { | ||||||
|  |     using quote = typename get_matcher<quote, Ts...>::type; | ||||||
|  |     using trim = typename get_matcher<trim, Ts...>::type; | ||||||
|  |     using escape = typename get_matcher<escape, Ts...>::type; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | template <typename... Ts> | ||||||
|  | struct setup<setup<Ts...>> : setup<Ts...> {}; | ||||||
|  | 
 | ||||||
|  | /////////////////////////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
|  | enum class State { finished, begin, reading, quoting }; | ||||||
|  | using range = std::pair<const char*, const char*>; | ||||||
|  | 
 | ||||||
|  | using string_range = std::pair<const char*, const char*>; | ||||||
|  | using split_input = std::vector<string_range>; | ||||||
|  | 
 | ||||||
|  | template <typename... Ts> | ||||||
|  | class splitter { | ||||||
|  |     using Setup = setup<Ts...>; | ||||||
|  |     using quote = typename Setup::quote; | ||||||
|  |     using trim = typename Setup::trim; | ||||||
|  |     using escape = typename Setup::escape; | ||||||
|  | 
 | ||||||
|  |     bool match(const char* end_i, char delim) { | ||||||
|  |         return *end_i == delim; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     bool match(const char* end_i, const std::string& delim) { | ||||||
|  |         return strncmp(end_i, delim.c_str(), delim.size()) == 0; | ||||||
|  |     }; | ||||||
|  | 
 | ||||||
|  |     size_t delimiter_size(char) { | ||||||
|  |         return 1; | ||||||
|  |     } | ||||||
|  |     size_t delimiter_size(const std::string& delim) { | ||||||
|  |         return delim.size(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void trim_if_enabled(char*& curr) { | ||||||
|  |         if constexpr (trim::enabled) { | ||||||
|  |             while (trim::match(*curr)) { | ||||||
|  |                 ++curr; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void shift_if_escaped(char*& curr_i) { | ||||||
|  |         if constexpr (escape::enabled) { | ||||||
|  |             if (escape::match(*curr_i)) { | ||||||
|  |                 *curr = end[1]; | ||||||
|  |                 ++end; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void shift() { | ||||||
|  |         *curr = *end; | ||||||
|  |         ++end; | ||||||
|  |         ++curr; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void shift(size_t n) { | ||||||
|  |         memcpy(curr, end, n); | ||||||
|  |         end += n; | ||||||
|  |         curr += n; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename Delim> | ||||||
|  |     std::tuple<size_t, bool> match_delimiter(char* begin, const Delim& delim) { | ||||||
|  |         char* end_i = begin; | ||||||
|  | 
 | ||||||
|  |         trim_if_enabled(end_i); | ||||||
|  | 
 | ||||||
|  |         // just spacing
 | ||||||
|  |         if (*end_i == '\0') { | ||||||
|  |             return {0, false}; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         // not a delimiter
 | ||||||
|  |         if (!match(end_i, delim)) { | ||||||
|  |             shift_if_escaped(end_i); | ||||||
|  |             return {1 + end_i - begin, false}; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         end_i += delimiter_size(delim); | ||||||
|  |         trim_if_enabled(end_i); | ||||||
|  | 
 | ||||||
|  |         // delimiter
 | ||||||
|  |         return {end_i - begin, true}; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | public: | ||||||
|  |     bool valid() { | ||||||
|  |         return error_.empty(); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     split_input& split(char* new_line, const std::string& d = ",") { | ||||||
|  |         line = new_line; | ||||||
|  |         output_.clear(); | ||||||
|  |         switch (d.size()) { | ||||||
|  |         case 0: | ||||||
|  |             // set error
 | ||||||
|  |             return output_; | ||||||
|  |         case 1: | ||||||
|  |             return split_impl(d[0]); | ||||||
|  |         default: | ||||||
|  |             return split_impl(d); | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename Delim> | ||||||
|  |     std::vector<range>& split_impl(const Delim& delim) { | ||||||
|  |         state = State::begin; | ||||||
|  |         begin = line; | ||||||
|  | 
 | ||||||
|  |         trim_if_enabled(begin); | ||||||
|  | 
 | ||||||
|  |         while (state != State::finished) { | ||||||
|  |             curr = end = begin; | ||||||
|  |             switch (state) { | ||||||
|  |             case (State::begin): | ||||||
|  |                 state_begin(); | ||||||
|  |                 break; | ||||||
|  |             case (State::reading): | ||||||
|  |                 state_reading(delim); | ||||||
|  |                 break; | ||||||
|  |             case (State::quoting): | ||||||
|  |                 state_quoting(delim); | ||||||
|  |                 break; | ||||||
|  |             default: | ||||||
|  |                 break; | ||||||
|  |             }; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|  |         return output_; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void state_begin() { | ||||||
|  |         if constexpr (quote::enabled) { | ||||||
|  |             if (quote::match(*begin)) { | ||||||
|  |                 ++begin; | ||||||
|  |                 state = State::quoting; | ||||||
|  |                 return; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |         state = State::reading; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename Delim> | ||||||
|  |     void state_reading(const Delim& delim) { | ||||||
|  |         while (true) { | ||||||
|  |             auto [width, valid] = match_delimiter(end, delim); | ||||||
|  | 
 | ||||||
|  |             // not a delimiter
 | ||||||
|  |             if (!valid) { | ||||||
|  |                 if (width == 0) { | ||||||
|  |                     // eol
 | ||||||
|  |                     output_.emplace_back(begin, curr); | ||||||
|  |                     state = State::finished; | ||||||
|  |                     break; | ||||||
|  |                 } else { | ||||||
|  |                     shift(width); | ||||||
|  |                     continue; | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  | 
 | ||||||
|  |             // found delimiter
 | ||||||
|  |             push_and_start_next(width); | ||||||
|  |             break; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     template <typename Delim> | ||||||
|  |     void state_quoting(const Delim& delim) { | ||||||
|  |         if constexpr (quote::enabled) { | ||||||
|  |             while (true) { | ||||||
|  |                 if (quote::match(*end)) { | ||||||
|  |                     // double quote
 | ||||||
|  |                     // eg: ...,"hel""lo,... -> hel"lo
 | ||||||
|  |                     if (quote::match(end[1])) { | ||||||
|  |                         ++end; | ||||||
|  |                         shift(); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     auto [width, valid] = match_delimiter(end + 1, delim); | ||||||
|  | 
 | ||||||
|  |                     // not a delimiter
 | ||||||
|  |                     if (!valid) { | ||||||
|  |                         if (width == 0) { | ||||||
|  |                             // eol
 | ||||||
|  |                             // eg: ...,"hello"   \0 -> hello
 | ||||||
|  |                             // eg no trim: ...,"hello"\0 -> hello
 | ||||||
|  |                             output_.emplace_back(begin, curr); | ||||||
|  |                         } else { | ||||||
|  |                             // missmatched quote
 | ||||||
|  |                             // eg: ...,"hel"lo,... -> error
 | ||||||
|  |                         } | ||||||
|  |                         state = State::finished; | ||||||
|  |                         break; | ||||||
|  |                     } | ||||||
|  | 
 | ||||||
|  |                     // delimiter
 | ||||||
|  |                     push_and_start_next(width + 1); | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 if constexpr (escape::enabled) { | ||||||
|  |                     if (escape::match(*end)) { | ||||||
|  |                         ++end; | ||||||
|  |                         shift(); | ||||||
|  |                         continue; | ||||||
|  |                     } | ||||||
|  |                 } | ||||||
|  | 
 | ||||||
|  |                 // unterminated error
 | ||||||
|  |                 // eg: ..."hell\0 -> quote not terminated
 | ||||||
|  |                 if (*end == '\0') { | ||||||
|  |                     *curr = '\0'; | ||||||
|  |                     state = State::finished; | ||||||
|  |                     break; | ||||||
|  |                 } | ||||||
|  |                 shift(); | ||||||
|  |             } | ||||||
|  |         } else { | ||||||
|  |             // set error impossible scenario
 | ||||||
|  |             state = State::finished; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     void push_and_start_next(size_t n) { | ||||||
|  |         output_.emplace_back(begin, curr); | ||||||
|  |         begin = end + n; | ||||||
|  |         state = State::begin; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     std::vector<range> output_; | ||||||
|  |     std::string error_ = ""; | ||||||
|  |     State state; | ||||||
|  |     char* curr; | ||||||
|  |     char* end; | ||||||
|  |     char* begin; | ||||||
|  |     char* line; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | ////////////////////////////////////////////////////////
 | ||||||
|  | 
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| // converter
 | // converter
 | ||||||
| ////////////////
 | ////////////////
 | ||||||
| 
 | 
 | ||||||
|  | template <typename... Matchers> | ||||||
| class converter { | class converter { | ||||||
|     using string_range = std::pair<const char*, const char*>; |     constexpr static auto default_delimiter = ","; | ||||||
|     constexpr static auto default_delimiter = ','; |  | ||||||
| 
 | 
 | ||||||
| public: | public: | ||||||
|     using split_input = std::vector<string_range>; |  | ||||||
| 
 |  | ||||||
|     // 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'
 | ||||||
|     template <typename T, typename... Ts> |     template <typename T, typename... Ts> | ||||||
|     T convert_object(const char* const line, const std::string& delim = "") { |     T convert_object(char* line, const std::string& delim = default_delimiter) { | ||||||
|         return to_object<T>(convert<Ts...>(line, delim)); |         return to_object<T>(convert<Ts...>(line, delim)); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     // parses line with given delimiter, returns tuple of objects with
 |     // parses line with given delimiter, returns tuple of objects with
 | ||||||
|     // extracted values of type 'Ts'
 |     // extracted values of type 'Ts'
 | ||||||
|     template <typename... Ts> |     template <typename... Ts> | ||||||
|     no_void_validator_tup_t<Ts...> convert(const char* const line, |     no_void_validator_tup_t<Ts...> convert( | ||||||
|                                            const std::string& delim = "") { |         char* line, const std::string& delim = default_delimiter) { | ||||||
|         input_ = split(line, delim); |         input_ = split(line, delim); | ||||||
|         return convert<Ts...>(input_); |         return convert<Ts...>(input_); | ||||||
|     } |     } | ||||||
| @ -205,21 +527,15 @@ public: | |||||||
| 
 | 
 | ||||||
|     // '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(const char* const line, |     const split_input& split(char* line, | ||||||
|                              const std::string& delim = "") { |                              const std::string& delim = default_delimiter) { | ||||||
|         input_.clear(); |         input_.clear(); | ||||||
|         if (line[0] == '\0') { |         if (line[0] == '\0') { | ||||||
|             return input_; |             return input_; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         switch (delim.size()) { |         input_ = splitter_.split(line, delim); | ||||||
|         case 0: |         return input_; | ||||||
|             return split_impl(line, ','); |  | ||||||
|         case 1: |  | ||||||
|             return split_impl(line, delim[0]); |  | ||||||
|         default: |  | ||||||
|             return split_impl(line, delim, delim.size()); |  | ||||||
|         }; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| private: | private: | ||||||
| @ -316,116 +632,6 @@ private: | |||||||
|         return convert_impl<Ts...>(elems); |         return convert_impl<Ts...>(elems); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     ////////////////
 |  | ||||||
|     // substring
 |  | ||||||
|     ////////////////
 |  | ||||||
| 
 |  | ||||||
|     template <typename Delim> |  | ||||||
|     const split_input& split_impl(const char* const line, Delim delim, |  | ||||||
|                                   size_t delim_size = 1) { |  | ||||||
|         auto [range, begin] = substring(line, delim); |  | ||||||
|         input_.push_back(range); |  | ||||||
|         while (range.second[0] != '\0') { |  | ||||||
|             if constexpr (quote != '\0') { |  | ||||||
|                 if (*begin == quote) { |  | ||||||
|                     ++begin; |  | ||||||
|                 } |  | ||||||
|                 if (*begin == '\0') { |  | ||||||
|                     break; |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
| 
 |  | ||||||
|             std::tie(range, begin) = substring(begin + delim_size, delim); |  | ||||||
|             log("-> " + std::string{range.first, range.second}); |  | ||||||
|             input_.push_back(range); |  | ||||||
|         } |  | ||||||
|         return input_; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     size_t match(const char* begin, char delim) const { |  | ||||||
|         const char* p = begin; |  | ||||||
|         if constexpr (space == '\0') { |  | ||||||
|             if (*p == delim) { |  | ||||||
|                 return 1; |  | ||||||
|             } |  | ||||||
|         } else { |  | ||||||
|             while (*p == space) { |  | ||||||
|                 ++p; |  | ||||||
|             } |  | ||||||
|             if (*p == '\0') { |  | ||||||
|                 return p - begin; |  | ||||||
|             } |  | ||||||
|             if (*p != delim) { |  | ||||||
|                 return 0; |  | ||||||
|             } |  | ||||||
|             do |  | ||||||
|                 ++p; |  | ||||||
|             while (*p == space); |  | ||||||
|             return p - begin; |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     size_t match(const char* end, const std::string& delim) const { |  | ||||||
|         // TODO
 |  | ||||||
|         log("ahamm"); |  | ||||||
|         return strncmp(end, delim.c_str(), delim.size()) != 0; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     template <typename Delim> |  | ||||||
|     std::tuple<string_range, const char*> substring(const char* begin, |  | ||||||
|                                                     Delim delim) { |  | ||||||
|         const char* end; |  | ||||||
|         const char* i; |  | ||||||
|         for (i = begin; *i != '\0'; ++i) |  | ||||||
|             ; |  | ||||||
|         log(">> " + std::string{begin, i}); |  | ||||||
|         if constexpr (quote != '\0') { |  | ||||||
|             if (*begin == quote) { |  | ||||||
|                 ++begin; |  | ||||||
| 
 |  | ||||||
|                 for (end = begin; true; ++end) { |  | ||||||
| 
 |  | ||||||
|                     if (*end == '\0') { |  | ||||||
|                         log("error"); |  | ||||||
|                         set_error_unterminated_quote(); |  | ||||||
|                         return {string_range{begin, end}, end}; |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if constexpr (escaping) { |  | ||||||
|                         if (end[-1] == '\\') { |  | ||||||
|                             continue; |  | ||||||
|                         } |  | ||||||
|                     } |  | ||||||
| 
 |  | ||||||
|                     if (*end == quote) { |  | ||||||
|                         break; |  | ||||||
|                     } |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 // end is not \0
 |  | ||||||
|                 size_t to_ignore = match(end + 1, delim); |  | ||||||
|                 log(std::to_string(to_ignore)); |  | ||||||
|                 if (to_ignore != 0) { |  | ||||||
|                     return {string_range{begin, end}, end + to_ignore}; |  | ||||||
|                 } |  | ||||||
| 
 |  | ||||||
|                 log("error"); |  | ||||||
|                 set_error_invalid_quotation(); |  | ||||||
|                 return {string_range{begin, end}, end}; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         for (end = begin; *end != '\0'; ++end) { |  | ||||||
|             size_t to_ignore = match(end, delim); |  | ||||||
|             log(std::to_string(to_ignore)); |  | ||||||
|             if (to_ignore != 0) { |  | ||||||
|                 return {string_range{begin, end}, end + to_ignore}; |  | ||||||
|             } |  | ||||||
|         } |  | ||||||
| 
 |  | ||||||
|         return {string_range{begin, end}, end}; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
|     // conversion
 |     // conversion
 | ||||||
|     ////////////////
 |     ////////////////
 | ||||||
| @ -437,6 +643,11 @@ private: | |||||||
|             return; |             return; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  |         if constexpr (std::is_same_v<T, std::string>) { | ||||||
|  |             extract(msg.first, msg.second, dst); | ||||||
|  |             return; | ||||||
|  |         } | ||||||
|  | 
 | ||||||
|         if (!extract(msg.first, msg.second, dst)) { |         if (!extract(msg.first, msg.second, dst)) { | ||||||
|             set_error_invalid_conversion(msg, pos); |             set_error_invalid_conversion(msg, pos); | ||||||
|             return; |             return; | ||||||
| @ -494,17 +705,7 @@ private: | |||||||
|     std::string string_error_; |     std::string string_error_; | ||||||
|     bool bool_error_; |     bool bool_error_; | ||||||
|     enum error_mode error_mode_ { error_mode::error_bool }; |     enum error_mode error_mode_ { error_mode::error_bool }; | ||||||
|  |     splitter<Matchers...> splitter_; | ||||||
| }; | }; | ||||||
| 
 | 
 | ||||||
| template <> |  | ||||||
| inline void converter::extract_one<std::string>(std::string& dst, |  | ||||||
|                                                 const string_range msg, |  | ||||||
|                                                 size_t) { |  | ||||||
|     if (!valid()) { |  | ||||||
|         return; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     extract(msg.first, msg.second, dst); |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| } /* ss */ | } /* ss */ | ||||||
|  | |||||||
| @ -250,8 +250,8 @@ private: | |||||||
|         char* buffer_{nullptr}; |         char* buffer_{nullptr}; | ||||||
|         char* next_line_buffer_{nullptr}; |         char* next_line_buffer_{nullptr}; | ||||||
| 
 | 
 | ||||||
|         converter converter_; |         converter<> converter_; | ||||||
|         converter next_line_converter_; |         converter<> next_line_converter_; | ||||||
| 
 | 
 | ||||||
|         size_t size_{0}; |         size_t size_{0}; | ||||||
|         const std::string& delim_; |         const std::string& delim_; | ||||||
| @ -288,7 +288,7 @@ private: | |||||||
|             next_line_converter_.set_error_mode(mode); |             next_line_converter_.set_error_mode(mode); | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|         converter& get_converter() { |         converter<>& get_converter() { | ||||||
|             return converter_; |             return converter_; | ||||||
|         } |         } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| CXX=clang++-9 | CXX=clang++ | ||||||
| CXXFLAGS=-Wall -Wextra -std=c++17 -O0 -lstdc++fs | CXXFLAGS=-Wall -Wextra -std=c++17 -lstdc++fs | ||||||
| TESTS=test_converter | TESTS=test_parser test_converter test_extractions | ||||||
| 
 | 
 | ||||||
| all: $(TESTS) | all: $(TESTS) | ||||||
| 
 | 
 | ||||||
|  | |||||||
										
											Binary file not shown.
										
									
								
							| @ -4,13 +4,29 @@ | |||||||
| #include "doctest.h" | #include "doctest.h" | ||||||
| #include <algorithm> | #include <algorithm> | ||||||
| 
 | 
 | ||||||
| /* TODO
 | class buffer { | ||||||
| TEST_CASE("testing quoting with escaping") { |     constexpr static auto buff_size = 1024; | ||||||
|     std::vector<std::string> values{"10",          "he\\\"llo", "\\\"", |     char data_[buff_size]; | ||||||
|                                     "\\\"a,a\\\"", "3.33",      "a\\\""}; |  | ||||||
| 
 | 
 | ||||||
|  | public: | ||||||
|  |     char* operator()(const char* data) { | ||||||
|  |         memset(data_, '\0', buff_size); | ||||||
|  |         strcpy(data_, data); | ||||||
|  |         return data_; | ||||||
|  |     } | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | buffer buff; | ||||||
|  | 
 | ||||||
|  | TEST_CASE("testing splitter with escaping") { | ||||||
|  |     std::vector<std::string> values{"10",   "he\\\"llo", | ||||||
|  |                                     "\\\"", "\\\"a\\,a\\\"", | ||||||
|  |                                     "3.33", "a\\\""}; | ||||||
|  | 
 | ||||||
|  |     char buff[128]; | ||||||
|     // with quote
 |     // with quote
 | ||||||
|     ss::converter c; |     ss::splitter<ss::quote<'"'>, ss::escape<'\\'>> s; | ||||||
|  |     std::string delim = ","; | ||||||
|     for (size_t i = 0; i < values.size() * values.size(); ++i) { |     for (size_t i = 0; i < values.size() * values.size(); ++i) { | ||||||
|         std::string input1; |         std::string input1; | ||||||
|         std::string input2; |         std::string input2; | ||||||
| @ -22,44 +38,25 @@ TEST_CASE("testing quoting with escaping") { | |||||||
|                 input1.append("\"" + values[j] + "\""); |                 input1.append("\"" + values[j] + "\""); | ||||||
|                 input2.append("\"" + values.at(values.size() - 1 - j) + "\""); |                 input2.append("\"" + values.at(values.size() - 1 - j) + "\""); | ||||||
|             } |             } | ||||||
|             input1.push_back(','); |             input1.append(delim); | ||||||
|             input2.push_back(','); |             input2.append(delim); | ||||||
|         } |         } | ||||||
|         input1.pop_back(); |         input1.pop_back(); | ||||||
|         input2.pop_back(); |         input2.pop_back(); | ||||||
|         input1.append("\0\""); |         input1.append("\0\""); | ||||||
|         input2.append("\0\""); |         input2.append("\0\""); | ||||||
| 
 | 
 | ||||||
|         auto tup1 = c.convert<int, std::string, std::string, std::string, |         memcpy(buff, input1.c_str(), input1.size() + 1); | ||||||
|                               double, std::string>(input1.c_str(), ","); |         auto tup1 = s.split(buff, delim); | ||||||
|         if (!c.valid()) { |         CHECK(tup1.size() == 6); | ||||||
|             FAIL("invalid: " + input1); | 
 | ||||||
|         } else { |         memcpy(buff, input2.c_str(), input2.size() + 1); | ||||||
|             auto [a, b, c, d, e, f] = tup1; |         auto tup2 = s.split(buff, delim); | ||||||
|             CHECK(a == 10); |         CHECK(tup2.size() == 6); | ||||||
|             CHECK(b == "he\"llo"); |     } | ||||||
|             CHECK(c == "\""); |  | ||||||
|             CHECK(d == "\"a,a\""); |  | ||||||
|             CHECK(e == 3.33); |  | ||||||
|             CHECK(f == "a\""); |  | ||||||
|             std::cout << a << ' ' << b << ' ' << c << ' ' << d << ' ' << e |  | ||||||
|                       << ' ' << f << std::endl; |  | ||||||
|             CHECK(tup1 == |  | ||||||
|                   std::make_tuple(10, "he\"llo", "\"", "\"a,a\"", 3.33, "a\"")); |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|         auto tup2 = c.convert<std::string, double, std::string, std::string, | /*
 | ||||||
|                               std::string, int>(input2.c_str(), ","); |  | ||||||
|         if (!c.valid()) { |  | ||||||
|             FAIL("invalid: " + input2); |  | ||||||
|         } else { |  | ||||||
|             CHECK(tup2 == |  | ||||||
|                   std::make_tuple("a\"", 3.33, "\"a,a\"", "\"", "he\"llo", 10)); |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
| } |  | ||||||
| */ |  | ||||||
| 
 |  | ||||||
| TEST_CASE("testing quoting without escaping") { | TEST_CASE("testing quoting without escaping") { | ||||||
|     std::vector<std::string> values{"10", "hello", ",", "a,a", "3.33", "a"}; |     std::vector<std::string> values{"10", "hello", ",", "a,a", "3.33", "a"}; | ||||||
| 
 | 
 | ||||||
| @ -117,6 +114,7 @@ TEST_CASE("testing quoting without escaping") { | |||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|  | */ | ||||||
| 
 | 
 | ||||||
| TEST_CASE("testing split") { | TEST_CASE("testing split") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| @ -124,13 +122,12 @@ TEST_CASE("testing split") { | |||||||
|          // clang-format off
 |          // clang-format off
 | ||||||
|                 {std::tuple{"a,b,c,d", std::vector{"a", "b", "c", "d"}, ","}, |                 {std::tuple{"a,b,c,d", std::vector{"a", "b", "c", "d"}, ","}, | ||||||
|                 {"", {}, " "}, |                 {"", {}, " "}, | ||||||
|                 {"a,b,c", {"a", "b", "c"}, ""}, |  | ||||||
|                 {" x x x x | x ", {" x x x x ", " x "}, "|"}, |                 {" x x x x | x ", {" x x x x ", " x "}, "|"}, | ||||||
|                 {"a::b::c::d", {"a", "b", "c", "d"}, "::"}, |                 {"a::b::c::d", {"a", "b", "c", "d"}, "::"}, | ||||||
|                 {"x\t-\ty", {"x", "y"}, "\t-\t"}, |                 {"x\t-\ty", {"x", "y"}, "\t-\t"}, | ||||||
|                 {"x", {"x"}, ","}} // clang-format on
 |                 {"x", {"x"}, ","}} // clang-format on
 | ||||||
|     ) { |     ) { | ||||||
|         auto split = c.split(s, delim); |         auto split = c.split(buff(s), delim); | ||||||
|         CHECK(split.size() == expected.size()); |         CHECK(split.size() == expected.size()); | ||||||
|         for (size_t i = 0; i < split.size(); ++i) { |         for (size_t i = 0; i < split.size(); ++i) { | ||||||
|             auto s = std::string(split[i].first, split[i].second); |             auto s = std::string(split[i].first, split[i].second); | ||||||
| @ -143,84 +140,85 @@ TEST_CASE("testing valid conversions") { | |||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<int>("5"); |         auto tup = c.convert<int>(buff("5")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<int, void>("5,junk"); |         auto tup = c.convert<int, void>(buff("5,junk")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<void, int>("junk,5"); |         auto tup = c.convert<void, int>(buff("junk,5")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<int, void, void>("5\njunk\njunk", "\n"); |         auto tup = c.convert<int, void, void>(buff("5\njunk\njunk"), "\n"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         // TODO make \t -> ' '
 |         // TODO make \t -> ' '
 | ||||||
|         auto tup = c.convert<void, int, void>("junk\t5\tjunk", "\t"); |         auto tup = c.convert<void, int, void>(buff("junk\t5\tjunk"), "\t"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<void, void, int>("junk\tjunk\t5", "\t"); |         auto tup = c.convert<void, void, int>(buff("junk\tjunk\t5"), "\t"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = |         auto tup = | ||||||
|             c.convert<void, void, std::optional<int>>("junk\tjunk\t5", "\t"); |             c.convert<void, void, std::optional<int>>(buff("junk\tjunk\t5"), | ||||||
|  |                                                       "\t"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         REQUIRE(tup.has_value()); |         REQUIRE(tup.has_value()); | ||||||
|         CHECK(tup == 5); |         CHECK(tup == 5); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<int, double, void>("5,6.6,junk"); |         auto tup = c.convert<int, double, void>(buff("5,6.6,junk")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{5, 6.6}); |         CHECK(tup == std::tuple{5, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<int, void, double>("5,junk,6.6"); |         auto tup = c.convert<int, void, double>(buff("5,junk,6.6")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{5, 6.6}); |         CHECK(tup == std::tuple{5, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<void, int, double>("junk;5;6.6", ";"); |         auto tup = c.convert<void, int, double>(buff("junk;5;6.6"), ";"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{5, 6.6}); |         CHECK(tup == std::tuple{5, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = |         auto tup = | ||||||
|             c.convert<void, std::optional<int>, double>("junk;5;6.6", ";"); |             c.convert<void, std::optional<int>, double>(buff("junk;5;6.6"), | ||||||
|  |                                                         ";"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         REQUIRE(std::get<0>(tup).has_value()); |         REQUIRE(std::get<0>(tup).has_value()); | ||||||
|         CHECK(tup == std::tuple{5, 6.6}); |         CHECK(tup == std::tuple{5, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = |         auto tup = | ||||||
|             c.convert<void, std::optional<int>, double>("junk;5.4;6.6", ";"); |             c.convert<void, std::optional<int>, double>(buff("junk;5.4;6.6"), | ||||||
|  |                                                         ";"); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         REQUIRE(!std::get<0>(tup).has_value()); |         REQUIRE(!std::get<0>(tup).has_value()); | ||||||
|         CHECK(tup == std::tuple{std::nullopt, 6.6}); |         CHECK(tup == std::tuple{std::nullopt, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = |         auto tup = c.convert<void, std::variant<int, double>, | ||||||
|             c.convert<void, std::variant<int, double>, double>("junk;5;6.6", |                              double>(buff("junk;5;6.6"), ";"); | ||||||
|                                                                ";"); |  | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         REQUIRE(std::holds_alternative<int>(std::get<0>(tup))); |         REQUIRE(std::holds_alternative<int>(std::get<0>(tup))); | ||||||
|         CHECK(tup == std::tuple{std::variant<int, double>{5}, 6.6}); |         CHECK(tup == std::tuple{std::variant<int, double>{5}, 6.6}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = |         auto tup = c.convert<void, std::variant<int, double>, | ||||||
|             c.convert<void, std::variant<int, double>, double>("junk;5.5;6.6", |                              double>(buff("junk;5.5;6.6"), ";"); | ||||||
|                                                                ";"); |  | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         REQUIRE(std::holds_alternative<double>(std::get<0>(tup))); |         REQUIRE(std::holds_alternative<double>(std::get<0>(tup))); | ||||||
|         CHECK(tup == std::tuple{std::variant<int, double>{5.5}, 6.6}); |         CHECK(tup == std::tuple{std::variant<int, double>{5.5}, 6.6}); | ||||||
| @ -230,60 +228,63 @@ TEST_CASE("testing valid conversions") { | |||||||
| TEST_CASE("testing invalid conversions") { | TEST_CASE("testing invalid conversions") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<int>(""); |     c.convert<int>(buff("")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int, void>(""); |     c.convert<int, void>(buff("")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int, void>(",junk"); |     c.convert<int, void>(buff(",junk")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<void, int>("junk,"); |     c.convert<void, int>(buff("junk,")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int>("x"); |     c.convert<int>(buff("x")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int, void>("x"); |     c.convert<int, void>(buff("x")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int, void>("x,junk"); |     c.convert<int, void>(buff("x,junk")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<void, int>("junk,x"); |     c.convert<void, int>(buff("junk,x")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<void, std::variant<int, double>, double>("junk;.5.5;6", ";"); |     c.convert<void, std::variant<int, double>, double>(buff("junk;.5.5;6"), | ||||||
|  |                                                        ";"); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| TEST_CASE("testing ss:ax restriction (all except)") { | TEST_CASE("testing ss:ax restriction (all except)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ax<int, 0>>("0"); |     c.convert<ss::ax<int, 0>>(buff("0")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ax<int, 0, 1, 2>>("1"); |     c.convert<ss::ax<int, 0, 1, 2>>(buff("1")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<void, char, ss::ax<int, 0, 1, 2>>("junk,c,1"); |     c.convert<void, char, ss::ax<int, 0, 1, 2>>(buff("junk,c,1")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ax<int, 1>, char>("1,c"); |     c.convert<ss::ax<int, 1>, char>(buff("1,c")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
|     { |     { | ||||||
|         int tup = c.convert<ss::ax<int, 1>>("3"); |         int tup = c.convert<ss::ax<int, 1>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         std::tuple<char, int> tup = c.convert<char, ss::ax<int, 1>>("c,3"); |         std::tuple<char, int> tup = | ||||||
|  |             c.convert<char, ss::ax<int, 1>>(buff("c,3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{'c', 3}); |         CHECK(tup == std::tuple{'c', 3}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         std::tuple<int, char> tup = c.convert<ss::ax<int, 1>, char>("3,c"); |         std::tuple<int, char> tup = | ||||||
|  |             c.convert<ss::ax<int, 1>, char>(buff("3,c")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{3, 'c'}); |         CHECK(tup == std::tuple{3, 'c'}); | ||||||
|     } |     } | ||||||
| @ -292,32 +293,33 @@ TEST_CASE("testing ss:ax restriction (all except)") { | |||||||
| TEST_CASE("testing ss:nx restriction (none except)") { | TEST_CASE("testing ss:nx restriction (none except)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::nx<int, 1>>("3"); |     c.convert<ss::nx<int, 1>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<char, ss::nx<int, 1, 2, 69>>("c,3"); |     c.convert<char, ss::nx<int, 1, 2, 69>>(buff("c,3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::nx<int, 1>, char>("3,c"); |     c.convert<ss::nx<int, 1>, char>(buff("3,c")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::nx<int, 3>>("3"); |         auto tup = c.convert<ss::nx<int, 3>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::nx<int, 0, 1, 2>>("2"); |         auto tup = c.convert<ss::nx<int, 0, 1, 2>>(buff("2")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 2); |         CHECK(tup == 2); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<char, void, ss::nx<int, 0, 1, 2>>("c,junk,1"); |         auto tup = | ||||||
|  |             c.convert<char, void, ss::nx<int, 0, 1, 2>>(buff("c,junk,1")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{'c', 1}); |         CHECK(tup == std::tuple{'c', 1}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::nx<int, 1>, char>("1,c"); |         auto tup = c.convert<ss::nx<int, 1>, char>(buff("1,c")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{1, 'c'}); |         CHECK(tup == std::tuple{1, 'c'}); | ||||||
|     } |     } | ||||||
| @ -326,32 +328,32 @@ TEST_CASE("testing ss:nx restriction (none except)") { | |||||||
| TEST_CASE("testing ss:ir restriction (in range)") { | TEST_CASE("testing ss:ir restriction (in range)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ir<int, 0, 2>>("3"); |     c.convert<ss::ir<int, 0, 2>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<char, ss::ir<int, 4, 69>>("c,3"); |     c.convert<char, ss::ir<int, 4, 69>>(buff("c,3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ir<int, 1, 2>, char>("3,c"); |     c.convert<ss::ir<int, 1, 2>, char>(buff("3,c")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::ir<int, 1, 5>>("3"); |         auto tup = c.convert<ss::ir<int, 1, 5>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::ir<int, 0, 2>>("2"); |         auto tup = c.convert<ss::ir<int, 0, 2>>(buff("2")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 2); |         CHECK(tup == 2); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<char, void, ss::ir<int, 0, 1>>("c,junk,1"); |         auto tup = c.convert<char, void, ss::ir<int, 0, 1>>(buff("c,junk,1")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{'c', 1}); |         CHECK(tup == std::tuple{'c', 1}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::ir<int, 1, 20>, char>("1,c"); |         auto tup = c.convert<ss::ir<int, 1, 20>, char>(buff("1,c")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{1, 'c'}); |         CHECK(tup == std::tuple{1, 'c'}); | ||||||
|     } |     } | ||||||
| @ -360,32 +362,32 @@ TEST_CASE("testing ss:ir restriction (in range)") { | |||||||
| TEST_CASE("testing ss:oor restriction (out of range)") { | TEST_CASE("testing ss:oor restriction (out of range)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::oor<int, 1, 5>>("3"); |     c.convert<ss::oor<int, 1, 5>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::oor<int, 0, 2>>("2"); |     c.convert<ss::oor<int, 0, 2>>(buff("2")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<char, ss::oor<int, 0, 1>, void>("c,1,junk"); |     c.convert<char, ss::oor<int, 0, 1>, void>(buff("c,1,junk")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::oor<int, 1, 20>, char>("1,c"); |     c.convert<ss::oor<int, 1, 20>, char>(buff("1,c")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::oor<int, 0, 2>>("3"); |         auto tup = c.convert<ss::oor<int, 0, 2>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<char, void, ss::oor<int, 4, 69>>("c,junk,3"); |         auto tup = c.convert<char, void, ss::oor<int, 4, 69>>(buff("c,junk,3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{'c', 3}); |         CHECK(tup == std::tuple{'c', 3}); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::oor<int, 1, 2>, char>("3,c"); |         auto tup = c.convert<ss::oor<int, 1, 2>, char>(buff("3,c")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{3, 'c'}); |         CHECK(tup == std::tuple{3, 'c'}); | ||||||
|     } |     } | ||||||
| @ -407,33 +409,34 @@ inline bool ss::extract(const char* begin, const char* end, | |||||||
| TEST_CASE("testing ss:ne restriction (not empty)") { | TEST_CASE("testing ss:ne restriction (not empty)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ne<std::string>>(""); |     c.convert<ss::ne<std::string>>(buff("")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<int, ss::ne<std::string>>("3,"); |     c.convert<int, ss::ne<std::string>>(buff("3,")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ne<std::string>, int>(",3"); |     c.convert<ss::ne<std::string>, int>(buff(",3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<void, ss::ne<std::string>, int>("junk,,3"); |     c.convert<void, ss::ne<std::string>, int>(buff("junk,,3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::ne<std::vector<int>>>(""); |     c.convert<ss::ne<std::vector<int>>>(buff("")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::ne<std::string>>("s"); |         auto tup = c.convert<ss::ne<std::string>>(buff("s")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == "s"); |         CHECK(tup == "s"); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<std::optional<int>, ss::ne<std::string>>("1,s"); |         auto tup = | ||||||
|  |             c.convert<std::optional<int>, ss::ne<std::string>>(buff("1,s")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == std::tuple{1, "s"}); |         CHECK(tup == std::tuple{1, "s"}); | ||||||
|     } |     } | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::ne<std::vector<int>>>("{1 2 3}"); |         auto tup = c.convert<ss::ne<std::vector<int>>>(buff("{1 2 3}")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == extracted_vector); |         CHECK(tup == extracted_vector); | ||||||
|     } |     } | ||||||
| @ -442,56 +445,56 @@ TEST_CASE("testing ss:ne restriction (not empty)") { | |||||||
| TEST_CASE("testing ss:lt ss::lte ss::gt ss::gte restriction (in range)") { | TEST_CASE("testing ss:lt ss::lte ss::gt ss::gte restriction (in range)") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::lt<int, 3>>("3"); |     c.convert<ss::lt<int, 3>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::lt<int, 2>>("3"); |     c.convert<ss::lt<int, 2>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::gt<int, 3>>("3"); |     c.convert<ss::gt<int, 3>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::gt<int, 4>>("3"); |     c.convert<ss::gt<int, 4>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::lte<int, 2>>("3"); |     c.convert<ss::lte<int, 2>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     c.convert<ss::gte<int, 4>>("3"); |     c.convert<ss::gte<int, 4>>(buff("3")); | ||||||
|     REQUIRE(!c.valid()); |     REQUIRE(!c.valid()); | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::lt<int, 4>>("3"); |         auto tup = c.convert<ss::lt<int, 4>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::gt<int, 2>>("3"); |         auto tup = c.convert<ss::gt<int, 2>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::lte<int, 4>>("3"); |         auto tup = c.convert<ss::lte<int, 4>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::lte<int, 3>>("3"); |         auto tup = c.convert<ss::lte<int, 3>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::gte<int, 2>>("3"); |         auto tup = c.convert<ss::gte<int, 2>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     { |     { | ||||||
|         auto tup = c.convert<ss::gte<int, 3>>("3"); |         auto tup = c.convert<ss::gte<int, 3>>(buff("3")); | ||||||
|         REQUIRE(c.valid()); |         REQUIRE(c.valid()); | ||||||
|         CHECK(tup == 3); |         CHECK(tup == 3); | ||||||
|     } |     } | ||||||
| @ -500,12 +503,12 @@ TEST_CASE("testing ss:lt ss::lte ss::gt ss::gte restriction (in range)") { | |||||||
| TEST_CASE("testing error mode") { | TEST_CASE("testing error mode") { | ||||||
|     ss::converter c; |     ss::converter c; | ||||||
| 
 | 
 | ||||||
|     c.convert<int>("junk"); |     c.convert<int>(buff("junk")); | ||||||
|     CHECK(!c.valid()); |     CHECK(!c.valid()); | ||||||
|     CHECK(c.error_msg().empty()); |     CHECK(c.error_msg().empty()); | ||||||
| 
 | 
 | ||||||
|     c.set_error_mode(ss::error_mode::error_string); |     c.set_error_mode(ss::error_mode::error_string); | ||||||
|     c.convert<int>("junk"); |     c.convert<int>(buff("junk")); | ||||||
|     CHECK(!c.valid()); |     CHECK(!c.valid()); | ||||||
|     CHECK(!c.error_msg().empty()); |     CHECK(!c.error_msg().empty()); | ||||||
| } | } | ||||||
|  | |||||||
| @ -56,6 +56,7 @@ TEST_CASE("testing parser") { | |||||||
|     make_and_write(f.name, data); |     make_and_write(f.name, data); | ||||||
|     { |     { | ||||||
|         ss::parser p{f.name, ","}; |         ss::parser 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()) { | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user