|
1 | 1 | export module CppUtils.Language.CSV.Mapping; |
2 | | -/* |
| 2 | + |
3 | 3 | import std; |
4 | 4 |
|
5 | 5 | import CppUtils.String; |
| 6 | +import CppUtils.Type.Mapping; |
| 7 | +import CppUtils.FileSystem; |
6 | 8 |
|
7 | | -export namespace CppUtils::Language::CSV::Mapping |
| 9 | +export namespace CppUtils::Language::CSV |
8 | 10 | { |
9 | | - struct Column final |
10 | | - { |
11 | | - String::Token token; |
12 | | - std::size_t position; |
13 | | - }; |
14 | | - |
15 | | - template<class...> |
16 | | - struct CSVSchema; |
| 11 | + using Line = std::vector<std::string_view>; |
17 | 12 |
|
18 | | - template<class... Columns> |
19 | | - struct CSVSchema final |
20 | | - { |
21 | | - explicit CSVSchema(const Columns&... columns) |
22 | | - { |
23 | | - ([&](const Column& column) -> void { |
24 | | - columnsPositions[column.token] = column.position; |
25 | | - }(columns), ...); |
26 | | - } |
27 | | - |
28 | | - std::unordered_map<String::Hash, std::size_t> columnsPositions; |
29 | | - }; |
30 | | - |
31 | | - template<class> |
32 | | - struct Mapping; |
33 | | - |
34 | | - template<class Object> |
35 | | - [[nodiscard]] inline auto toStruct(const Language::CSV::Line& csvLine) -> decltype(auto) |
| 13 | + template<class Object, class Mapping> |
| 14 | + [[nodiscard]] inline auto toStruct(const auto& csvLine) -> Object |
36 | 15 | { |
| 16 | + using CSVMapping = typename Mapping::CSVMapping; |
| 17 | + using StructMapping = typename Mapping::StructMapping; |
| 18 | + using FunctionMapping = typename Mapping::FunctionMapping; |
37 | 19 | static_assert(std::is_default_constructible_v<Object>); |
38 | | - const auto& structSchema = Language::Mapping::Mapping<Object>::schema; |
39 | | - const auto& csvSchema = Mapping<Object>::schema; |
40 | 20 | auto object = Object{}; |
41 | | - std::apply([&](auto&&... attributes) -> void { |
42 | | - ([&](auto&& attribute) -> void { |
43 | | - auto& objectAttribute = object.*attribute.address; |
44 | | - using AttributeType = std::remove_cvref_t<decltype(objectAttribute)>; |
45 | | - auto position = csvSchema.columnsPositions.at(attribute.token); |
46 | | - if (position < std::size(csvLine)) |
| 21 | + std::apply([&](auto&&... csvPairs) -> void { |
| 22 | + ([&](auto&& csvPair) -> void { |
| 23 | + using CSVPair = std::remove_cvref_t<decltype(csvPair)>; |
| 24 | + constexpr auto token = CSVPair::lhs; |
| 25 | + constexpr auto csvColumnPosition = std::get<0>(CSVPair::rhs); |
| 26 | + if (csvColumnPosition < std::size(csvLine)) |
47 | 27 | { |
48 | | - if (attribute.parsingFunction) |
49 | | - objectAttribute = attribute.parsingFunction(csvLine[position]); |
50 | | - else if constexpr (std::same_as<AttributeType, std::string>) |
51 | | - objectAttribute = csvLine[position]; |
| 28 | + constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>()); |
| 29 | + auto& member = object.*memberPointer; |
| 30 | + using MemberType = std::remove_cvref_t<decltype(member)>; |
| 31 | + auto value = std::string_view{csvLine[csvColumnPosition]}; |
| 32 | + if constexpr (FunctionMapping::template containsLhs<token>()) |
| 33 | + FunctionMapping::template toSingleRhs<token>()(member, value); |
| 34 | + else if constexpr (std::same_as<MemberType, std::string_view>) |
| 35 | + member = value; |
| 36 | + else if constexpr (std::same_as<MemberType, std::string>) |
| 37 | + member = value; |
| 38 | + else if constexpr (std::integral<MemberType> or std::floating_point<MemberType>) |
| 39 | + std::from_chars(std::data(value), std::data(value) + std::size(value), member); |
52 | 40 | } |
53 | | - }(attributes, ...); |
54 | | - }, structSchema.attributes); |
| 41 | + }(csvPairs), ...); |
| 42 | + }, typename CSVMapping::Values{}); |
55 | 43 | return object; |
56 | 44 | } |
57 | 45 |
|
58 | | - using namespace String::Literals; |
| 46 | + template<class Object, class Mapping> |
| 47 | + [[nodiscard]] inline auto toStruct(std::string_view csvLine, std::string_view separator = ";") -> Object |
| 48 | + { |
| 49 | + const auto tokens = csvLine |
| 50 | + | std::views::split(separator) |
| 51 | + | std::views::transform([](auto&& range) { return std::string_view{range}; }) |
| 52 | + | std::ranges::to<std::vector<std::string_view>>(); |
| 53 | + return toStruct<Object, Mapping>(tokens); |
| 54 | + } |
| 55 | + |
| 56 | + template<class Object, class Mapping> |
| 57 | + [[nodiscard]] inline auto toStructs( |
| 58 | + std::string_view csv, |
| 59 | + std::string_view columnSeparator = ";", |
| 60 | + std::string_view lineSeparator = "\n", |
| 61 | + std::size_t dropNFirstLines = 0, |
| 62 | + std::vector<Object>&& objects = {}) -> std::vector<Object> |
| 63 | + { |
| 64 | + const auto lines = csv |
| 65 | + | std::views::split(lineSeparator) |
| 66 | + | std::views::transform([](auto&& range) { return std::string_view{range}; }) |
| 67 | + | std::ranges::to<std::vector<std::string_view>>(); |
| 68 | + for (auto i = dropNFirstLines; i < std::size(lines); ++i) |
| 69 | + if (const auto trimmedLine = String::trimString(lines[i]); not std::empty(trimmedLine)) |
| 70 | + objects.push_back(toStruct<Object, Mapping>(trimmedLine, columnSeparator)); |
| 71 | + return std::move(objects); |
| 72 | + } |
| 73 | + |
| 74 | + template<class Object, class Mapping> |
| 75 | + [[nodiscard]] inline auto toCSV(const Object& object, std::string_view separator = ";") -> std::string |
| 76 | + { |
| 77 | + using CSVMapping = typename Mapping::CSVMapping; |
| 78 | + using StructMapping = typename Mapping::StructMapping; |
| 79 | + constexpr auto maxPosition = []() -> std::size_t { |
| 80 | + auto max = 0uz; |
| 81 | + std::apply([&](auto&&... pairs) { |
| 82 | + ((max = std::max(max, static_cast<std::size_t>(std::get<0>(std::remove_cvref_t<decltype(pairs)>::rhs)))), ...); |
| 83 | + }, typename CSVMapping::Values{}); |
| 84 | + return max; |
| 85 | + }(); |
| 86 | + auto columns = std::vector<std::string>(maxPosition + 1); |
| 87 | + std::apply([&](auto&&... csvPairs) -> void { |
| 88 | + ([&](auto&& csvPair) -> void { |
| 89 | + using CSVPair = std::remove_cvref_t<decltype(csvPair)>; |
| 90 | + constexpr auto token = CSVPair::lhs; |
| 91 | + constexpr auto csvColumnPosition = std::get<0>(CSVPair::rhs); |
| 92 | + constexpr auto memberPointer = std::get<0>(StructMapping::template toRhs<token>()); |
| 93 | + columns[csvColumnPosition] = String::formatValue(object.*memberPointer); |
| 94 | + }(csvPairs), ...); |
| 95 | + }, typename CSVMapping::Values{}); |
| 96 | + return String::concatenateStringsWithSeparator(columns, separator); |
| 97 | + } |
| 98 | + |
| 99 | + template<class Object, class Mapping> |
| 100 | + [[nodiscard]] inline auto toCSV( |
| 101 | + const std::vector<Object>& objects, |
| 102 | + std::string_view columnSeparator = ";", |
| 103 | + std::string_view lineSeparator = "\n") -> std::string |
| 104 | + { |
| 105 | + auto csv = std::string{}; |
| 106 | + for (const auto& object : objects) |
| 107 | + { |
| 108 | + if (not std::empty(csv)) |
| 109 | + csv += lineSeparator; |
| 110 | + csv += toCSV<Object, Mapping>(object, columnSeparator); |
| 111 | + } |
| 112 | + return csv; |
| 113 | + } |
59 | 114 |
|
60 | | - template<class T> |
61 | | - inline auto loadFile(const std::filesystem::path& filePath, std::vector<T>&& elements = {}, std::size dropNFirstLines = 0) -> std::vector<T> |
| 115 | + template<class Object, class Mapping> |
| 116 | + [[nodiscard]] inline auto loadFile( |
| 117 | + const std::filesystem::path& filePath, |
| 118 | + std::string_view columnSeparator = ";", |
| 119 | + std::string_view lineSeparator = "\n", |
| 120 | + std::size_t dropNFirstLines = 0, |
| 121 | + std::vector<Object>&& elements = {}) -> std::vector<Object> |
62 | 122 | { |
63 | 123 | auto fileContent = FileSystem::String::read(filePath); |
64 | | - auto csvTable = parse(fileContent, dropNFirstLines); |
65 | | - for (const auto& csvLine : csvTable) |
66 | | - elements.emplace_back(toStruct<T>(csvLine)); |
67 | | - return elements; |
| 124 | + return toStructs<Object, Mapping>(fileContent, columnSeparator, lineSeparator, dropNFirstLines, std::move(elements)); |
68 | 125 | } |
69 | 126 |
|
70 | | - template<class T> |
71 | | - inline auto loadDirectory(const std::filesystem::path& directory, std::vector<T>&& elements = {}, std::size_t dropNFirstLines = 0) -> std::vector<T> |
| 127 | + template<class Object, class Mapping> |
| 128 | + [[nodiscard]] inline auto loadDirectory(const std::filesystem::path& directory, |
| 129 | + std::string_view columnSeparator = ";", |
| 130 | + std::string_view lineSeparator = "\n", |
| 131 | + std::size_t dropNFirstLines = 0, |
| 132 | + std::vector<Object>&& elements = {}) -> std::vector<Object> |
72 | 133 | { |
73 | | - FileSystem::forFilesWithExtension(directory, ".csv", [&elements, dropNFirstLines](const auto& filePath) { |
74 | | - loadFile(filePath, std::move(elements), dropNFirstLines); |
| 134 | + FileSystem::forFilesWithExtension(directory, ".csv", [&](const auto& filePath) { |
| 135 | + elements = loadFile<Object, Mapping>(filePath, columnSeparator, lineSeparator, dropNFirstLines, std::move(elements)); |
75 | 136 | }); |
76 | | - return elements; |
| 137 | + return std::move(elements); |
77 | 138 | } |
78 | 139 | } |
79 | | -*/ |
|
0 commit comments