diff --git a/CMakeLists.txt b/CMakeLists.txt index 0873d71..002dc7c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,4 +9,6 @@ include_directories(${Boost_INCLUDE_DIR}) add_executable(portingtools ${sources}) +set_property(TARGET portingtools PROPERTY CXX_STANDARD 20) + target_link_libraries(portingtools LINK_PUBLIC ${Boost_LIBRARIES}) diff --git a/src/csv.cpp b/src/csv.cpp new file mode 100644 index 0000000..a13a16a --- /dev/null +++ b/src/csv.cpp @@ -0,0 +1,73 @@ +#include "csv.hpp" +#include +#include +#include + +namespace csv { +enum class CSVState { UnquotedField, QuotedField, QuotedQuote }; + +// this function has been made in collaboration with +// StackOverflow https://stackoverflow.com/a/30338543 +void readCSVRow(const std::string &row, std::vector &fields) { + fields.push_back(""); + CSVState state = CSVState::UnquotedField; + size_t i = 0; // index of the current field + for (char c : row) { + switch (state) { + case CSVState::UnquotedField: + switch (c) { + case ',': // end of field + fields.push_back(""); + i++; + break; + case '"': + state = CSVState::QuotedField; + break; + default: + fields[i].push_back(c); + break; + } + break; + case CSVState::QuotedField: + switch (c) { + case '"': + state = CSVState::QuotedQuote; + break; + default: + fields[i].push_back(c); + break; + } + break; + case CSVState::QuotedQuote: + switch (c) { + case ',': // , after closing quote + fields.push_back(""); + i++; + state = CSVState::UnquotedField; + break; + case '"': // "" -> " + fields[i].push_back('"'); + state = CSVState::QuotedField; + break; + default: // end of quote + state = CSVState::UnquotedField; + break; + } + break; + } + } +} +bool CsvReader::operator>>(std::vector &line) { + line.clear(); + if (!m_stream) { + return false; + } + + std::string row; + std::getline(m_stream, row); + + readCSVRow(row, line); + + return !!m_stream; +} +} // namespace csv diff --git a/src/csv.hpp b/src/csv.hpp new file mode 100644 index 0000000..23a9b2e --- /dev/null +++ b/src/csv.hpp @@ -0,0 +1,15 @@ +#include +#include +#include +namespace csv { +class CsvReader { +private: + std::ifstream m_stream; + +public: + CsvReader(auto stream) : m_stream(stream){}; + ~CsvReader() = default; + + bool operator>>(std::vector &line); +}; +} // namespace csv diff --git a/src/mappings.cpp b/src/mappings.cpp index 502206a..6795b23 100644 --- a/src/mappings.cpp +++ b/src/mappings.cpp @@ -1,4 +1,5 @@ #include "mappings.hpp" +#include "csv.hpp" #include #include #include @@ -6,30 +7,16 @@ #include #include #include +#include +#include #include #include namespace fs = boost::filesystem; namespace mappings { -void loadFile(std::string path, std::map *map) { - std::ifstream file(path); - std::string line; - while (std::getline(file, line)) { - std::vector splits; - boost::algorithm::split(splits, line, boost::is_any_of(",\t|")); - - if (splits.size() < 2) { - BOOST_LOG_TRIVIAL(warning) << "found invalid mapping '" << line << "'"; - continue; - } - - (*map)[splits[0]] = splits[1]; - } -} - Mappings Mappings::load() { - std::map mappings; + std::map mappings; std::map renames; // load mappings @@ -39,11 +26,31 @@ Mappings Mappings::load() { !boost::algorithm::ends_with(itr->path().string(), ".csv")) continue; - loadFile(itr->path().string(), &mappings); + csv::CsvReader csv(itr->path().string()); + + std::vector l; + while (csv >> l) { + if (l.size() < 2) { + BOOST_LOG_TRIVIAL(warning) << "found invalid mapping"; + continue; + } + mappings[l[0]] = {l[0], l[1], + l.size() >= 4 ? std::optional(l[3]) : std::nullopt}; + } } if (fs::is_regular_file("renames.csv")) { - loadFile("renames.csv", &renames); + csv::CsvReader csv("renames.csv"); + + std::vector l; + while (csv >> l) { + if (l.size() < 2) { + BOOST_LOG_TRIVIAL(warning) << "found invalid rename"; + continue; + } + + renames[l[0]] = l[1]; + } } BOOST_LOG_TRIVIAL(info) << "loaded " << mappings.size() << " mappings and " @@ -51,20 +58,36 @@ Mappings Mappings::load() { return {mappings, renames}; } +void showMapInfo(Mapping mapping, std::optional rename) { + auto doc = mapping.doc.has_value() ? "\n\n\t" + *mapping.doc + "\n" : ""; + if (rename.has_value()) { + BOOST_LOG_TRIVIAL(info) + << "\nFound mapping:\n" + << "\n\tOriginal\t" << mapping.orig << "\n\tRemapped\t" << mapping.name + << "\n\tRenamed \t" << *rename << doc; + } else { + BOOST_LOG_TRIVIAL(info) << "\nFound mapping:\n" + << "\n\tOriginal\t" << mapping.orig + << "\n\tRemapped\t" << mapping.name << doc; + } +} + std::string Mappings::map(std::string inp) { - if (!this->mappings.count(inp)) { + if (!this->m_mappings.count(inp)) { BOOST_LOG_TRIVIAL(warning) << "unknown mapping '" << inp << "'"; return ""; } - auto mapped = this->mappings[inp]; + auto mapped = this->m_mappings[inp]; - if (this->renames.count(mapped)) { + if (this->m_renames.count(mapped.name)) { BOOST_LOG_TRIVIAL(info) - << "found rename " << mapped << " -> " << renames[mapped]; - return renames[mapped]; + << "found rename " << mapped.name << " -> " << m_renames[mapped.name]; + showMapInfo(mapped, m_renames[mapped.name]); + return m_renames[mapped.name]; } - return mapped; + showMapInfo(mapped, std::nullopt); + return mapped.name; } } // namespace mappings diff --git a/src/mappings.hpp b/src/mappings.hpp index e3bebc5..cb12e2b 100644 --- a/src/mappings.hpp +++ b/src/mappings.hpp @@ -1,15 +1,23 @@ #include +#include #include + namespace mappings { +struct Mapping { + std::string orig; + std::string name; + std::optional doc; +}; + class Mappings { - std::map mappings; - std::map renames; + std::map m_mappings; + std::map m_renames; public: - Mappings(std::map mappings, + Mappings(std::map mappings, std::map renames) { - this->mappings = mappings; - this->renames = renames; + this->m_mappings = mappings; + this->m_renames = renames; } ~Mappings() = default; static Mappings load();