mirror of
				https://github.com/red0124/ssp.git
				synced 2025-10-31 05:06:46 +01:00 
			
		
		
		
	Merge pull request #25 from red0124/feature/win_msvc_ci
Feature/win msvc ci
This commit is contained in:
		
						commit
						78c75abec7
					
				
							
								
								
									
										63
									
								
								.github/workflows/win-msvc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								.github/workflows/win-msvc.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @ -0,0 +1,63 @@ | ||||
| name: win-msvc-ci | ||||
| 
 | ||||
| on: | ||||
|   push: | ||||
|     branches: | ||||
|       - master | ||||
|       - feature/** | ||||
|       - improvement/** | ||||
|       - bugfix/** | ||||
| 
 | ||||
|   pull_request: | ||||
|     branches: | ||||
|       - master | ||||
|       - feature/** | ||||
|       - improvement/** | ||||
|       - bugfix/** | ||||
| 
 | ||||
| jobs: | ||||
|   ci: | ||||
|     if: >- | ||||
|       ! contains(toJSON(github.event.commits.*.message), '[skip ci]') && | ||||
|       ! contains(toJSON(github.event.commits.*.message), '[skip github]') | ||||
| 
 | ||||
|     defaults: | ||||
|       run: | ||||
|         shell: bash | ||||
| 
 | ||||
|     runs-on: ${{ matrix.config.os }} | ||||
| 
 | ||||
|     strategy: | ||||
|       fail-fast: false | ||||
|       matrix: | ||||
|         config: | ||||
|           - os: windows-2019 | ||||
|             vs: "Visual Studio 16 2019" | ||||
| 
 | ||||
|           - os: windows-latest | ||||
|             vs: "Visual Studio 17 2022" | ||||
| 
 | ||||
|         build: [Debug, Release] | ||||
|         platform: [Win32, x64] | ||||
| 
 | ||||
|     name: "${{matrix.config.vs}}:${{matrix.platform}}:${{matrix.build}}" | ||||
| 
 | ||||
|     steps: | ||||
|       - name: checkout | ||||
|         uses: actions/checkout@v2 | ||||
| 
 | ||||
|       - name: Install dependencies | ||||
|         run: script/ci_install_deps.sh | ||||
| 
 | ||||
|       - name: Configure | ||||
|         run: >- | ||||
|           cmake -S test -B build -D CMAKE_BUILD_TYPE=${{matrix.build}} | ||||
|           -G "${{matrix.config.vs}}" -A ${{matrix.platform}} | ||||
| 
 | ||||
|       - name: Build | ||||
|         run: cmake --build build -j ${{steps.cores.outputs.count}} | ||||
| 
 | ||||
|       - name: Run | ||||
|         working-directory: build | ||||
|         run: >- | ||||
|           ctest -C Debug --output-on-failure -j ${{steps.cores.outputs.count}} | ||||
| @ -13,6 +13,7 @@ | ||||
| [](https://github.com/red0124/ssp/actions/workflows/ubuntu-latest-icc.yml) | ||||
| [](https://github.com/red0124/ssp/actions/workflows/win-msys2-gcc.yml) | ||||
| [](https://github.com/red0124/ssp/actions/workflows/win-msys2-clang.yml) | ||||
| [](https://github.com/red0124/ssp/actions/workflows/win-msvc.yml) | ||||
| 
 | ||||
| A header only "csv" parser which is fast and versatile with modern C++ api. Requires compiler with C++17 support. [Can also be used to convert strings to specific types.](#The-converter) | ||||
| 
 | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| #pragma once | ||||
| #include <cstdlib> | ||||
| #include <cstring> | ||||
| 
 | ||||
| #include <string> | ||||
| 
 | ||||
| #ifdef CMAKE_GITHUB_CI | ||||
| #include <doctest/doctest.h> | ||||
| @ -9,40 +9,22 @@ | ||||
| #endif | ||||
| 
 | ||||
| struct buffer { | ||||
|     char* data_{nullptr}; | ||||
|   std::string data_; | ||||
| 
 | ||||
|     char* operator()(const char* data) { | ||||
|         if (data_) { | ||||
|             delete[] data_; | ||||
|         } | ||||
|         data_ = new char[strlen(data) + 1]; | ||||
|         strcpy(data_, data); | ||||
|         return data_; | ||||
|   char *operator()(const std::string &data) { | ||||
|     data_ = data; | ||||
|     return data_.data(); | ||||
|   } | ||||
| 
 | ||||
|     char* append(const char* data) { | ||||
|         if (data_) { | ||||
|             char* new_data_ = new char[strlen(data_) + strlen(data) + 1]; | ||||
|             strcpy(new_data_, data_); | ||||
|             strcat(new_data_, data); | ||||
|             delete[] data_; | ||||
|             data_ = new_data_; | ||||
|             return data_; | ||||
|         } else { | ||||
|             return operator()(data); | ||||
|         } | ||||
|   char *append(const std::string &data) { | ||||
|     data_ += data; | ||||
|     return data_.data(); | ||||
|   } | ||||
| 
 | ||||
|     char* append_overwrite_last(const char* data, size_t size) { | ||||
|         data_[strlen(data_) - size] = '\0'; | ||||
|   char *append_overwrite_last(const std::string &data, size_t size) { | ||||
|     data_.resize(data_.size() - size); | ||||
|     return append(data); | ||||
|   } | ||||
| 
 | ||||
|     ~buffer() { | ||||
|         if (data_) { | ||||
|             delete[] data_; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| [[maybe_unused]] inline buffer buff; | ||||
|  | ||||
| @ -20,18 +20,15 @@ struct unique_file_name { | ||||
|   const std::string name; | ||||
| 
 | ||||
|   unique_file_name() | ||||
|         : name{"random_" + std::to_string(i++) + time_now_rand() + | ||||
|                "_file.csv"} { | ||||
|     } | ||||
|       : name{"random_" + std::to_string(i++) + time_now_rand() + "_file.csv"} {} | ||||
| 
 | ||||
|     ~unique_file_name() { | ||||
|         std::filesystem::remove(name); | ||||
|     } | ||||
|   ~unique_file_name() { std::filesystem::remove(name); } | ||||
| }; | ||||
| 
 | ||||
| void replace_all(std::string& s, const std::string& from, | ||||
|                  const std::string& to) { | ||||
|     if (from.empty()) return; | ||||
| void replace_all(std::string &s, const std::string &from, | ||||
|                  const std::string &to) { | ||||
|   if (from.empty()) | ||||
|     return; | ||||
|   size_t start_pos = 0; | ||||
|   while ((start_pos = s.find(from, start_pos)) != std::string::npos) { | ||||
|     s.replace(start_pos, from.length(), to); | ||||
| @ -39,7 +36,7 @@ void replace_all(std::string& s, const std::string& from, | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| void update_if_crlf(std::string& s) { | ||||
| void update_if_crlf(std::string &s) { | ||||
| #ifdef _WIN32 | ||||
|   replace_all(s, "\r\n", "\n"); | ||||
| #else | ||||
| @ -65,30 +62,28 @@ struct X { | ||||
|         .append(delim) | ||||
|         .append(s); | ||||
|   } | ||||
|     auto tied() const { | ||||
|         return std::tie(i, d, s); | ||||
|     } | ||||
|   auto tied() const { return std::tie(i, d, s); } | ||||
| }; | ||||
| 
 | ||||
| template <typename T> | ||||
| std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(const T& lhs, | ||||
|                                                        const T& rhs) { | ||||
| std::enable_if_t<ss::has_m_tied_t<T>, bool> operator==(const T &lhs, | ||||
|                                                        const T &rhs) { | ||||
|   return lhs.tied() == rhs.tied(); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| static void make_and_write(const std::string& file_name, | ||||
|                            const std::vector<T>& data, | ||||
|                            const std::vector<std::string>& header = {}) { | ||||
| static void make_and_write(const std::string &file_name, | ||||
|                            const std::vector<T> &data, | ||||
|                            const std::vector<std::string> &header = {}) { | ||||
|   std::ofstream out{file_name}; | ||||
| 
 | ||||
| #ifdef _WIN32 | ||||
|     std::vector<const char*> new_lines = {"\n"}; | ||||
|   std::vector<const char *> new_lines = {"\n"}; | ||||
| #else | ||||
|     std::vector<const char*> new_lines = {"\n", "\r\n"}; | ||||
|   std::vector<const char *> new_lines = {"\n", "\r\n"}; | ||||
| #endif | ||||
| 
 | ||||
|     for (const auto& i : header) { | ||||
|   for (const auto &i : header) { | ||||
|     if (&i != &header.front()) { | ||||
|       out << T::delim; | ||||
|     } | ||||
| @ -125,7 +120,7 @@ TEST_CASE("parser test various cases") { | ||||
|       i.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
|         for (const auto& a : p2.iterate<int, double, std::string>()) { | ||||
|     for (const auto &a : p2.iterate<int, double, std::string>()) { | ||||
|       i2.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -153,7 +148,7 @@ TEST_CASE("parser test various cases") { | ||||
|     } | ||||
| 
 | ||||
|     p2.ignore_next(); | ||||
|         for (const auto& a : p2.iterate<tup>()) { | ||||
|     for (const auto &a : p2.iterate<tup>()) { | ||||
|       i2.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -178,7 +173,7 @@ TEST_CASE("parser test various cases") { | ||||
|       i.push_back(p.get_object<X, int, double, std::string>()); | ||||
|     } | ||||
| 
 | ||||
|         for (auto&& a : p2.iterate_object<X, int, double, std::string>()) { | ||||
|     for (auto &&a : p2.iterate_object<X, int, double, std::string>()) { | ||||
|       i2.push_back(std::move(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -190,7 +185,7 @@ TEST_CASE("parser test various cases") { | ||||
|     ss::parser p{f.name, ","}; | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|         for (auto&& a : p.iterate_object<X, int, double, std::string>()) { | ||||
|     for (auto &&a : p.iterate_object<X, int, double, std::string>()) { | ||||
|       i.push_back(std::move(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -223,7 +218,7 @@ TEST_CASE("parser test various cases") { | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|     using tup = std::tuple<int, double, std::string>; | ||||
|         for (auto&& a : p.iterate_object<X, tup>()) { | ||||
|     for (auto &&a : p.iterate_object<X, tup>()) { | ||||
|       i.push_back(std::move(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -245,7 +240,7 @@ TEST_CASE("parser test various cases") { | ||||
|     ss::parser p{f.name, ","}; | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|         for (auto&& a : p.iterate<X>()) { | ||||
|     for (auto &&a : p.iterate<X>()) { | ||||
|       i.push_back(std::move(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -261,29 +256,28 @@ TEST_CASE("parser test various cases") { | ||||
|     std::vector<X> i2; | ||||
| 
 | ||||
|     while (!p.eof()) { | ||||
|             auto a = | ||||
|                 p.get_object<X, ss::ax<int, excluded>, double, std::string>(); | ||||
|       auto a = p.get_object<X, ss::ax<int, excluded>, double, std::string>(); | ||||
|       if (p.valid()) { | ||||
|         i.push_back(a); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|         for (auto&& a : p2.iterate_object<X, ss::ax<int, excluded>, double, | ||||
|                                           std::string>()) { | ||||
|     for (auto &&a : | ||||
|          p2.iterate_object<X, ss::ax<int, excluded>, double, std::string>()) { | ||||
|       if (p2.valid()) { | ||||
|         i2.push_back(std::move(a)); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     std::vector<X> expected; | ||||
|         for (auto& x : data) { | ||||
|     for (auto &x : data) { | ||||
|       if (x.i != excluded) { | ||||
|         expected.push_back(x); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     std::copy_if(data.begin(), data.end(), expected.begin(), | ||||
|                      [](const X& x) { return x.i != excluded; }); | ||||
|                  [&](const X &x) { return x.i != excluded; }); | ||||
|     CHECK_EQ(i, expected); | ||||
|     CHECK_EQ(i2, expected); | ||||
|   } | ||||
| @ -302,7 +296,7 @@ TEST_CASE("parser test various cases") { | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|         for (auto&& a : | ||||
|     for (auto &&a : | ||||
|          p2.iterate_object<X, ss::nx<int, 3>, double, std::string>()) { | ||||
|       if (p2.valid()) { | ||||
|         i2.push_back(std::move(a)); | ||||
| @ -330,7 +324,7 @@ TEST_CASE("parser test various cases") { | ||||
|       i.push_back(p.get_next<X>()); | ||||
|     } | ||||
| 
 | ||||
|         for (auto&& a : p2.iterate<X>()) { | ||||
|     for (auto &&a : p2.iterate<X>()) { | ||||
|       i2.push_back(std::move(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -344,20 +338,17 @@ struct test_struct { | ||||
|   int i; | ||||
|   double d; | ||||
|   char c; | ||||
|     auto tied() { | ||||
|         return std::tie(i, d, c); | ||||
|     } | ||||
|   auto tied() { return std::tie(i, d, c); } | ||||
| }; | ||||
| 
 | ||||
| void expect_test_struct(const test_struct&) { | ||||
| } | ||||
| void expect_test_struct(const test_struct &) {} | ||||
| 
 | ||||
| // various scenarios
 | ||||
| TEST_CASE("parser test composite conversion") { | ||||
|   unique_file_name f; | ||||
|   { | ||||
|     std::ofstream out{f.name}; | ||||
|         for (auto& i : | ||||
|     for (auto &i : | ||||
|          {"10,a,11.1", "10,20,11.1", "junk", "10,11.1", "1,11.1,a", "junk", | ||||
|           "10,junk", "11,junk", "10,11.1,c", "10,20", "10,22.2,f"}) { | ||||
|       out << i << std::endl; | ||||
| @ -374,11 +365,11 @@ TEST_CASE("parser test composite conversion") { | ||||
|   { | ||||
|     constexpr static auto expectedData = std::tuple{10, 'a', 11.1}; | ||||
| 
 | ||||
|         auto [d1, d2, d3, d4] = | ||||
|             p.try_next<int, int, double>(fail) | ||||
|     auto [d1, d2, d3, d4] = p.try_next<int, int, double>(fail) | ||||
|                                 .or_else<test_struct>(fail) | ||||
|                 .or_else<int, char, double>( | ||||
|                     [](auto&& data) { CHECK_EQ(data, expectedData); }) | ||||
|                                 .or_else<int, char, double>([&](auto &&data) { | ||||
|                                   CHECK_EQ(data, expectedData); | ||||
|                                 }) | ||||
|                                 .on_error(fail) | ||||
|                                 .or_else<test_tuple>(fail) | ||||
|                                 .values(); | ||||
| @ -396,7 +387,7 @@ TEST_CASE("parser test composite conversion") { | ||||
|     constexpr static auto expectedData = std::tuple{10, 20, 11.1}; | ||||
| 
 | ||||
|     auto [d1, d2, d3, d4] = | ||||
|             p.try_next<int, int, double>([](auto& i1, auto i2, double d) { | ||||
|         p.try_next<int, int, double>([&](auto &i1, auto i2, double d) { | ||||
|            CHECK_EQ(std::tie(i1, i2, d), expectedData); | ||||
|          }) | ||||
|             .on_error(fail) | ||||
| @ -438,11 +429,10 @@ TEST_CASE("parser test composite conversion") { | ||||
|   { | ||||
|     REQUIRE(!p.eof()); | ||||
| 
 | ||||
|         auto [d1, d2] = | ||||
|             p.try_next<int, double>([](auto& i, auto& d) { | ||||
|     auto [d1, d2] = p.try_next<int, double>([](auto &i, auto &d) { | ||||
|                        REQUIRE_EQ(std::tie(i, d), std::tuple{10, 11.1}); | ||||
|                      }) | ||||
|                 .or_else<int, double>([](auto&, auto&) { FAIL(""); }) | ||||
|                         .or_else<int, double>([](auto &, auto &) { FAIL(""); }) | ||||
|                         .values(); | ||||
| 
 | ||||
|     REQUIRE(p.valid()); | ||||
| @ -453,7 +443,7 @@ TEST_CASE("parser test composite conversion") { | ||||
|   { | ||||
|     REQUIRE(!p.eof()); | ||||
| 
 | ||||
|         auto [d1, d2] = p.try_next<int, double>([](auto&, auto&) { FAIL(""); }) | ||||
|     auto [d1, d2] = p.try_next<int, double>([](auto &, auto &) { FAIL(""); }) | ||||
|                         .or_else<test_struct>(expect_test_struct) | ||||
|                         .values(); | ||||
| 
 | ||||
| @ -466,8 +456,7 @@ TEST_CASE("parser test composite conversion") { | ||||
|   { | ||||
|     REQUIRE(!p.eof()); | ||||
| 
 | ||||
|         auto [d1, d2, d3, d4, d5] = | ||||
|             p.try_next<int, int, double>(fail) | ||||
|     auto [d1, d2, d3, d4, d5] = p.try_next<int, int, double>(fail) | ||||
|                                     .or_object<test_struct, int, double, char>() | ||||
|                                     .or_else<test_struct>(expect_test_struct) | ||||
|                                     .or_else<test_tuple>(fail) | ||||
| @ -530,7 +519,7 @@ TEST_CASE("parser test composite conversion") { | ||||
| 
 | ||||
|     auto [d1, d2, d3, d4] = | ||||
|         p.try_next<int, int>([] { return false; }) | ||||
|                 .or_else<int, double>([](auto&) { return false; }) | ||||
|             .or_else<int, double>([](auto &) { return false; }) | ||||
|             .or_else<int, int>() | ||||
|             .or_else<int, int>(fail) | ||||
|             .values(); | ||||
| @ -548,7 +537,7 @@ TEST_CASE("parser test composite conversion") { | ||||
| 
 | ||||
|     auto [d1, d2, d3, d4] = | ||||
|         p.try_object<test_struct, int, double, char>([] { return false; }) | ||||
|                 .or_else<int, double>([](auto&) { return false; }) | ||||
|             .or_else<int, double>([](auto &) { return false; }) | ||||
|             .or_object<test_struct, int, double, char>() | ||||
|             .or_else<int, int>(fail) | ||||
|             .values(); | ||||
| @ -564,35 +553,29 @@ TEST_CASE("parser test composite conversion") { | ||||
|   CHECK(p.eof()); | ||||
| } | ||||
| 
 | ||||
| size_t move_called = 0; | ||||
| 
 | ||||
| struct my_string { | ||||
|     char* data{nullptr}; | ||||
|   char *data{nullptr}; | ||||
| 
 | ||||
|   my_string() = default; | ||||
| 
 | ||||
|     ~my_string() { | ||||
|         delete[] data; | ||||
|     } | ||||
|   ~my_string() { delete[] data; } | ||||
| 
 | ||||
|   // make sure no object is copied
 | ||||
|     my_string(const my_string&) = delete; | ||||
|     my_string& operator=(const my_string&) = delete; | ||||
|   my_string(const my_string &) = delete; | ||||
|   my_string &operator=(const my_string &) = delete; | ||||
| 
 | ||||
|     my_string(my_string&& other) : data{other.data} { | ||||
|         move_called++; | ||||
|   my_string(my_string &&other) : data{other.data} { | ||||
|     other.data = nullptr; | ||||
|   } | ||||
| 
 | ||||
|     my_string& operator=(my_string&& other) { | ||||
|         move_called++; | ||||
|   my_string &operator=(my_string &&other) { | ||||
|     data = other.data; | ||||
|     return *this; | ||||
|   } | ||||
| }; | ||||
| 
 | ||||
| template <> | ||||
| inline bool ss::extract(const char* begin, const char* end, my_string& s) { | ||||
| inline bool ss::extract(const char *begin, const char *end, my_string &s) { | ||||
|   size_t size = end - begin; | ||||
|   s.data = new char[size + 1]; | ||||
|   strncpy(s.data, begin, size); | ||||
| @ -604,65 +587,20 @@ struct xyz { | ||||
|   my_string x; | ||||
|   my_string y; | ||||
|   my_string z; | ||||
|     auto tied() { | ||||
|         return std::tie(x, y, z); | ||||
|     } | ||||
|   auto tied() { return std::tie(x, y, z); } | ||||
| }; | ||||
| 
 | ||||
| TEST_CASE("parser test the moving of parsed values") { | ||||
|     { | ||||
|         unique_file_name f; | ||||
|         { | ||||
|             std::ofstream out{f.name}; | ||||
|             out << "x" << std::endl; | ||||
|         } | ||||
| 
 | ||||
|         ss::parser p{f.name, ","}; | ||||
|         auto x = p.get_next<my_string>(); | ||||
|         CHECK_LE(move_called, 1); | ||||
|         move_called = 0; | ||||
|     } | ||||
| 
 | ||||
|     unique_file_name f; | ||||
|     { | ||||
|         std::ofstream out{f.name}; | ||||
|         out << "a,b,c" << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
| 
 | ||||
|         ss::parser p{f.name, ","}; | ||||
|         auto x = p.get_next<my_string, my_string, my_string>(); | ||||
|         CHECK_LE(move_called, 3); | ||||
|         move_called = 0; | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         ss::parser p{f.name, ","}; | ||||
|         auto x = p.get_object<xyz, my_string, my_string, my_string>(); | ||||
|         CHECK_LE(move_called, 6); | ||||
|         move_called = 0; | ||||
|     } | ||||
| 
 | ||||
|     { | ||||
|         ss::parser p{f.name, ","}; | ||||
|         auto x = p.get_next<xyz>(); | ||||
|         CHECK_LE(move_called, 6); | ||||
|         move_called = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("parser test the moving of parsed composite values") { | ||||
|   // to compile is enough
 | ||||
|   return; | ||||
|   ss::parser p{"", ""}; | ||||
|   p.try_next<my_string, my_string, my_string>() | ||||
|         .or_else<my_string, my_string, my_string, my_string>([](auto&&) {}) | ||||
|         .or_else<my_string>([](auto&) {}) | ||||
|         .or_else<xyz>([](auto&&) {}) | ||||
|         .or_object<xyz, my_string, my_string, my_string>([](auto&&) {}) | ||||
|       .or_else<my_string, my_string, my_string, my_string>([](auto &&) {}) | ||||
|       .or_else<my_string>([](auto &) {}) | ||||
|       .or_else<xyz>([](auto &&) {}) | ||||
|       .or_object<xyz, my_string, my_string, my_string>([](auto &&) {}) | ||||
|       .or_else<std::tuple<my_string, my_string, my_string>>( | ||||
|             [](auto&, auto&, auto&) {}); | ||||
|           [](auto &, auto &, auto &) {}); | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("parser test error mode") { | ||||
| @ -681,7 +619,7 @@ TEST_CASE("parser test error mode") { | ||||
|   CHECK_FALSE(p.error_msg().empty()); | ||||
| } | ||||
| 
 | ||||
| std::string no_quote(const std::string& s) { | ||||
| std::string no_quote(const std::string &s) { | ||||
|   if (!s.empty() && s[0] == '"') { | ||||
|     return {std::next(begin(s)), std::prev(end(s))}; | ||||
|   } | ||||
| @ -696,12 +634,12 @@ TEST_CASE("parser test csv on multiple lines with quotes") { | ||||
|                          {7, 8, "\"u\"\"\""}, | ||||
|                          {9, 10, "v"}, | ||||
|                          {11, 12, "\"w\n\""}}; | ||||
|     for (auto& [_, __, s] : data) { | ||||
|   for (auto &[_, __, s] : data) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
| 
 | ||||
|   make_and_write(f.name, data); | ||||
|     for (auto& [_, __, s] : data) { | ||||
|   for (auto &[_, __, s] : data) { | ||||
|     s = no_quote(s); | ||||
|     if (s[0] == 'u') { | ||||
|       s = "u\""; | ||||
| @ -716,7 +654,7 @@ TEST_CASE("parser test csv on multiple lines with quotes") { | ||||
|     i.emplace_back(ss::to_object<X>(a)); | ||||
|   } | ||||
| 
 | ||||
|     for (auto& [_, __, s] : i) { | ||||
|   for (auto &[_, __, s] : i) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
|   CHECK_EQ(i, data); | ||||
| @ -728,25 +666,22 @@ TEST_CASE("parser test csv on multiple lines with quotes") { | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| std::string no_escape(std::string& s) { | ||||
| std::string no_escape(std::string &s) { | ||||
|   s.erase(std::remove(begin(s), end(s), '\\'), end(s)); | ||||
|   return s; | ||||
| } | ||||
| 
 | ||||
| TEST_CASE("parser test csv on multiple lines with escapes") { | ||||
|   unique_file_name f; | ||||
|     std::vector<X> data = {{1, 2, "x\\\nx\\\r\nx"}, | ||||
|                            {5, 6, "z\\\nz\\\nz"}, | ||||
|                            {7, 8, "u"}, | ||||
|                            {3, 4, "y\\\ny\\\ny"}, | ||||
|                            {9, 10, "v\\\\"}, | ||||
|                            {11, 12, "w\\\n"}}; | ||||
|     for (auto& [_, __, s] : data) { | ||||
|   std::vector<X> data = { | ||||
|       {1, 2, "x\\\nx\\\r\nx"}, {5, 6, "z\\\nz\\\nz"}, {7, 8, "u"}, | ||||
|       {3, 4, "y\\\ny\\\ny"},   {9, 10, "v\\\\"},      {11, 12, "w\\\n"}}; | ||||
|   for (auto &[_, __, s] : data) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
| 
 | ||||
|   make_and_write(f.name, data); | ||||
|     for (auto& [_, __, s] : data) { | ||||
|   for (auto &[_, __, s] : data) { | ||||
|     s = no_escape(s); | ||||
|     if (s == "v") { | ||||
|       s = "v\\"; | ||||
| @ -761,7 +696,7 @@ TEST_CASE("parser test csv on multiple lines with escapes") { | ||||
|     i.emplace_back(ss::to_object<X>(a)); | ||||
|   } | ||||
| 
 | ||||
|     for (auto& [_, __, s] : i) { | ||||
|   for (auto &[_, __, s] : i) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
|   CHECK_EQ(i, data); | ||||
| @ -807,7 +742,7 @@ TEST_CASE("parser test csv on multiple lines with quotes and escapes") { | ||||
| #endif | ||||
|                          {9, 10, "just strings"}}; | ||||
| 
 | ||||
|     for (auto& [_, __, s] : i) { | ||||
|   for (auto &[_, __, s] : i) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
|   CHECK_EQ(i, data); | ||||
| @ -833,8 +768,8 @@ TEST_CASE("parser test multiline restricted") { | ||||
|     out << "19,20,just strings" << std::endl; | ||||
|   } | ||||
| 
 | ||||
|     ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>> | ||||
|         p{f.name, ","}; | ||||
|   ss::parser<ss::multiline_restricted<2>, ss::quote<'"'>, ss::escape<'\\'>> p{ | ||||
|       f.name, ","}; | ||||
|   std::vector<X> i; | ||||
| 
 | ||||
|   while (!p.eof()) { | ||||
| @ -853,26 +788,23 @@ TEST_CASE("parser test multiline restricted") { | ||||
|                          {9, 10, "just\n\nstrings"}, | ||||
|                          {19, 20, "just strings"}}; | ||||
| 
 | ||||
|     for (auto& [_, __, s] : i) { | ||||
|   for (auto &[_, __, s] : i) { | ||||
|     update_if_crlf(s); | ||||
|   } | ||||
|   CHECK_EQ(i, data); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename Tuple> | ||||
| struct has_type; | ||||
| template <typename T, typename Tuple> struct has_type; | ||||
| 
 | ||||
| template <typename T, typename... Us> | ||||
| struct has_type<T, std::tuple<Us...>> | ||||
|     : std::disjunction<std::is_same<T, Us>...> {}; | ||||
| 
 | ||||
| void checkSize(size_t size1, size_t size2) { | ||||
|     CHECK_EQ(size1, size2); | ||||
| } | ||||
| void checkSize(size_t size1, size_t size2) { CHECK_EQ(size1, size2); } | ||||
| 
 | ||||
| template <typename... Ts> | ||||
| void testFields(const std::string file_name, const std::vector<X>& data, | ||||
|                 const std::vector<std::string>& fields) { | ||||
| void testFields(const std::string file_name, const std::vector<X> &data, | ||||
|                 const std::vector<std::string> &fields) { | ||||
|   using CaseType = std::tuple<Ts...>; | ||||
| 
 | ||||
|   ss::parser p{file_name, ","}; | ||||
| @ -880,7 +812,7 @@ void testFields(const std::string file_name, const std::vector<X>& data, | ||||
|   p.use_fields(fields); | ||||
|   std::vector<CaseType> i; | ||||
| 
 | ||||
|     for (const auto& a : p.iterate<CaseType>()) { | ||||
|   for (const auto &a : p.iterate<CaseType>()) { | ||||
|     i.push_back(a); | ||||
|   } | ||||
| 
 | ||||
| @ -911,14 +843,14 @@ TEST_CASE("parser test various cases with header") { | ||||
|                          {7, 8, "u"}, {9, 10, "v"}, {11, 12, "w"}}; | ||||
| 
 | ||||
|   make_and_write(f.name, data, header); | ||||
|     const auto& o = f.name; | ||||
|     const auto& d = data; | ||||
|   const auto &o = f.name; | ||||
|   const auto &d = data; | ||||
| 
 | ||||
|   { | ||||
|     ss::parser<ss::string_error> p{f.name, ","}; | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|         for (const auto& a : p.iterate<int, double, std::string>()) { | ||||
|     for (const auto &a : p.iterate<int, double, std::string>()) { | ||||
|       i.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -930,7 +862,7 @@ TEST_CASE("parser test various cases with header") { | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|     p.ignore_next(); | ||||
|         for (const auto& a : p.iterate<int, double, std::string>()) { | ||||
|     for (const auto &a : p.iterate<int, double, std::string>()) { | ||||
|       i.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -941,7 +873,7 @@ TEST_CASE("parser test various cases with header") { | ||||
|     ss::parser<ss::ignore_header> p{f.name, ","}; | ||||
|     std::vector<X> i; | ||||
| 
 | ||||
|         for (const auto& a : p.iterate<int, double, std::string>()) { | ||||
|     for (const auto &a : p.iterate<int, double, std::string>()) { | ||||
|       i.emplace_back(ss::to_object<X>(a)); | ||||
|     } | ||||
| 
 | ||||
| @ -996,8 +928,7 @@ TEST_CASE("parser test various cases with header") { | ||||
|     p.use_fields(Str, Int, Dbl); | ||||
| 
 | ||||
|     { | ||||
|             auto [string_, int_, double_] = | ||||
|                 p.get_next<std::string, int, double>(); | ||||
|       auto [string_, int_, double_] = p.get_next<std::string, int, double>(); | ||||
|       CHECK_EQ(double_, data[3].d); | ||||
|       CHECK_EQ(int_, data[3].i); | ||||
|       CHECK_EQ(string_, data[3].s); | ||||
| @ -1044,12 +975,12 @@ TEST_CASE("parser test various cases with header") { | ||||
|   testFields<double, int, str>(o, d, {Dbl, Int, Str}); | ||||
| } | ||||
| 
 | ||||
| void testIgnoreEmpty(const std::vector<X>& data) { | ||||
| void testIgnoreEmpty(const std::vector<X> &data) { | ||||
|   unique_file_name f; | ||||
|   make_and_write(f.name, data); | ||||
| 
 | ||||
|   std::vector<X> expected; | ||||
|     for (const auto& d : data) { | ||||
|   for (const auto &d : data) { | ||||
|     if (d.s != X::make_empty) { | ||||
|       expected.push_back(d); | ||||
|     } | ||||
| @ -1059,7 +990,7 @@ void testIgnoreEmpty(const std::vector<X>& data) { | ||||
|     ss::parser<ss::string_error, ss::ignore_empty> p{f.name, ","}; | ||||
| 
 | ||||
|     std::vector<X> i; | ||||
|         for (const auto& a : p.iterate<X>()) { | ||||
|     for (const auto &a : p.iterate<X>()) { | ||||
|       i.push_back(a); | ||||
|     } | ||||
| 
 | ||||
| @ -1070,7 +1001,7 @@ void testIgnoreEmpty(const std::vector<X>& data) { | ||||
|     ss::parser<ss::string_error> p{f.name, ","}; | ||||
|     std::vector<X> i; | ||||
|     size_t n = 0; | ||||
|         for (const auto& a : p.iterate<X>()) { | ||||
|     for (const auto &a : p.iterate<X>()) { | ||||
|       if (data.at(n).s == X::make_empty) { | ||||
|         CHECK_FALSE(p.valid()); | ||||
|       } | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 red0124
						red0124