diff --git a/src/adoc.cpp b/src/adoc.cpp index 813cfcf..f59ca80 100644 --- a/src/adoc.cpp +++ b/src/adoc.cpp @@ -29,183 +29,186 @@ using std::regex; using std::regex_replace; using tagpair = std::pair>; -void export_adoc(const vector &entries, ostream &out) +namespace Export { - try + void AsciiDoc::print() const { - out << "= Visited things\n" - << ":Author: remwharead " << global::version << endl - << ":Date: " - << timepoint_to_string(system_clock::now()) << endl - << ":TOC: right" << endl - << ":TOCLevels: 2" << endl - << endl; - - std::map> alltags; - string day; - for (const Database::entry &entry : entries) + try { - const string datetime = timepoint_to_string(entry.datetime); - const string newday = datetime.substr(0, datetime.find('T')); - const string time = datetime.substr(datetime.find('T') + 1); - if (newday != day) - { - day = newday; - out << "== " << day << endl << endl; - } + _out << "= Visited things\n" + << ":Author: remwharead " << global::version << endl + << ":Date: " + << timepoint_to_string(system_clock::now()) << endl + << ":TOC: right" << endl + << ":TOCLevels: 2" << endl + << endl; - out << "[[dt_" << datetime << "]]\n"; - out << "* link:" << entry.uri; - if (!entry.title.empty()) + std::map> alltags; + string day; + for (const Database::entry &entry : _entries) { - out << '[' << entry.title << ']'; - } - else - { - out << "[]"; - } - out << " +" << endl; - - out << '_' << time.substr(0, 5) << '_'; - if (!entry.archive_uri.empty()) - { - out << " (link:" << entry.archive_uri << "[archived version])"; - } - - bool separator = false; - for (const string &tag : entry.tags) - { - if (tag.empty()) + const string datetime = timepoint_to_string(entry.datetime); + const string newday = datetime.substr(0, datetime.find('T')); + const string time = datetime.substr(datetime.find('T') + 1); + if (newday != day) { - continue; - } - if (!separator) - { - out << "\n| "; - separator = true; + day = newday; + _out << "== " << day << endl << endl; } - auto globaltag = alltags.find(tag); - if (globaltag != alltags.end()) + _out << "[[dt_" << datetime << "]]\n"; + _out << "* link:" << entry.uri; + if (!entry.title.empty()) { - globaltag->second.push_back(entry); + _out << '[' << entry.title << ']'; } else { - alltags.insert({ tag, { entry } }); + _out << "[]"; } + _out << " +" << endl; - out << "xref:t_" << replace_in_tags(tag) << "[" << tag << ']'; - if (tag != *(entry.tags.rbegin())) + _out << '_' << time.substr(0, 5) << '_'; + if (!entry.archive_uri.empty()) { - out << ", "; + _out << " (link:" << entry.archive_uri << "[archived version])"; } + + bool separator = false; + for (const string &tag : entry.tags) + { + if (tag.empty()) + { + continue; + } + if (!separator) + { + _out << "\n| "; + separator = true; + } + + auto globaltag = alltags.find(tag); + if (globaltag != alltags.end()) + { + globaltag->second.push_back(entry); + } + else + { + alltags.insert({ tag, { entry } }); + } + + _out << "xref:t_" << replace_in_tags(tag) << "[" << tag << ']'; + if (tag != *(entry.tags.rbegin())) + { + _out << ", "; + } + } + + if (!entry.description.empty()) + { + _out << " +" << endl << entry.description; + } + _out << endl << endl; } - if (!entry.description.empty()) + if (!alltags.empty()) { - out << " +" << endl << entry.description; + print_tags(alltags); } - out << endl << endl; } - - if (!alltags.empty()) + catch (std::exception &e) { - adoc_print_tags(alltags, out); + cerr << "Error in " << __func__ << ": " << e.what() << endl; } } - catch (std::exception &e) + + const string AsciiDoc::replace_in_tags(string text) const { - cerr << "Error in " << __func__ << ": " << e.what() << endl; + // TODO: Find a better solution. + const std::map searchreplace = + { + { " ", "-" }, { "§", "-" }, + { "$", "-" }, { "%", "-" }, + { "&", "-" }, { "/", "-" }, + { "=", "-" }, { "^", "-" }, + { "!", "-" }, { "?", "-" }, + { "₀", "0" }, { "⁰", "0" }, + { "₁", "1" }, { "¹", "1" }, + { "₂", "2" }, { "²", "2" }, + { "₃", "3" }, { "³", "3" }, + { "₄", "4" }, { "⁴", "4" }, + { "₅", "5" }, { "⁵", "5" }, + { "₆", "6" }, { "⁶", "6" }, + { "₇", "7" }, { "⁷", "7" }, + { "₈", "8" }, { "⁸", "8" }, + { "₉", "9" }, { "⁹", "9" } + }; + for (const std::pair &sr : searchreplace) + { + size_t pos = 0; + while ((pos = text.find(sr.first, pos)) != std::string::npos) + { + text.replace(pos, sr.first.length(), sr.second); + } + } + return text; + } + + void AsciiDoc::print_tags( + const std::map> &tags) const + { + _out << "== Tags\n\n"; + vector sortedtags(tags.size()); + std::move(tags.begin(), tags.end(), sortedtags.begin()); + std::sort(sortedtags.begin(), sortedtags.end(), + [](const tagpair &a, tagpair &b) + { + if (a.second.size() != b.second.size()) + { // Sort by number of occurrences if they are different. + return a.second.size() > b.second.size(); + } + else + { // Sort by tag names otherwise. + std::locale loc; + const std::collate &coll = + std::use_facet>(loc); + return (coll.compare( + a.first.data(), a.first.data() + + a.first.length(), + b.first.data(), b.first.data() + + b.first.length()) == -1); + } + }); + + bool othertags = false; + for (const auto &tag : sortedtags) + { + if (tag.second.size() == 1) + { + if (!othertags) + { + _out << "=== Less used tags\n\n"; + othertags = true; + } + _out << "="; + } + + _out << "=== [[t_" << replace_in_tags(tag.first) << "]]" + << tag.first << endl; + for (const Database::entry &entry : tag.second) + { + const string datetime = timepoint_to_string(entry.datetime); + const string date = datetime.substr(0, datetime.find('T')); + string title = entry.title; + if (title.empty()) + { + title = "++" + entry.uri + "++"; + } + _out << endl << "* xref:dt_" << datetime + << '[' << title << "] _(" << date << ")_" << endl; + } + _out << endl; + } + _out << endl; } } - -const string replace_in_tags(string text) -{ - // TODO: Find a better solution. - const std::map searchreplace = - { - { " ", "-" }, { "§", "-" }, - { "$", "-" }, { "%", "-" }, - { "&", "-" }, { "/", "-" }, - { "=", "-" }, { "^", "-" }, - { "!", "-" }, { "?", "-" }, - { "₀", "0" }, { "⁰", "0" }, - { "₁", "1" }, { "¹", "1" }, - { "₂", "2" }, { "²", "2" }, - { "₃", "3" }, { "³", "3" }, - { "₄", "4" }, { "⁴", "4" }, - { "₅", "5" }, { "⁵", "5" }, - { "₆", "6" }, { "⁶", "6" }, - { "₇", "7" }, { "⁷", "7" }, - { "₈", "8" }, { "⁸", "8" }, - { "₉", "9" }, { "⁹", "9" } - }; - for (const std::pair &sr : searchreplace) - { - size_t pos = 0; - while ((pos = text.find(sr.first, pos)) != std::string::npos) - { - text.replace(pos, sr.first.length(), sr.second); - } - } - return text; -} - -void adoc_print_tags(const std::map> &tags, - ostream &out) -{ - out << "== Tags\n\n"; - vector sortedtags(tags.size()); - std::move(tags.begin(), tags.end(), sortedtags.begin()); - std::sort(sortedtags.begin(), sortedtags.end(), - [](const tagpair &a, tagpair &b) - { - if (a.second.size() != b.second.size()) - { // Sort by number of occurrences if they are different. - return a.second.size() > b.second.size(); - } - else - { // Sort by tag names otherwise. - std::locale loc; - const std::collate &coll = - std::use_facet>(loc); - return (coll.compare( - a.first.data(), a.first.data() - + a.first.length(), - b.first.data(), b.first.data() - + b.first.length()) == -1); - } - }); - - bool othertags = false; - for (const auto &tag : sortedtags) - { - if (tag.second.size() == 1) - { - if (!othertags) - { - out << "=== Less used tags\n\n"; - othertags = true; - } - out << "="; - } - - out << "=== [[t_" << replace_in_tags(tag.first) << "]]" - << tag.first << endl; - for (const Database::entry &entry : tag.second) - { - const string datetime = timepoint_to_string(entry.datetime); - const string date = datetime.substr(0, datetime.find('T')); - string title = entry.title; - if (title.empty()) - { - title = "++" + entry.uri + "++"; - } - out << endl << "* xref:dt_" << datetime - << '[' << title << "] _(" << date << ")_" << endl; - } - out << endl; - } - out << endl; -} diff --git a/src/export.hpp b/src/export.hpp index d50a947..6d4dd2e 100644 --- a/src/export.hpp +++ b/src/export.hpp @@ -45,18 +45,27 @@ namespace Export using ExportBase::ExportBase; void print() const; + + private: //! replaces " with "". const string quote(string field) const; }; + + class AsciiDoc : protected ExportBase + { + public: + using ExportBase::ExportBase; + + void print() const; + + private: + //! Replaces characters in tags that asciidoctor doesn't like. + const string replace_in_tags(string text) const; + void print_tags(const std::map> &tags) const; + }; } -void export_adoc(const vector &entries, ostream &out = cout); -//! Replaces characters in tags that asciidoctor doesn't like. -const string replace_in_tags(string text); -void adoc_print_tags(const std::map> &tags, - ostream &out = cout); - //! Export as Netscape bookmark file. void export_bookmarks(const vector &entries, diff --git a/src/main.cpp b/src/main.cpp index 23fee28..ddd9a46 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -111,12 +111,12 @@ int main(const int argc, const char *argv[]) { if (file.is_open()) { - export_adoc(entries, file); + Export::AsciiDoc(entries, file).print(); file.close(); } else { - export_adoc(entries); + Export::AsciiDoc(entries).print(); } break; } diff --git a/tests/test_adoc.cpp b/tests/test_adoc.cpp index 436c6de..0e25ba9 100644 --- a/tests/test_adoc.cpp +++ b/tests/test_adoc.cpp @@ -47,7 +47,7 @@ SCENARIO ("The AsciiDoc export works correctly") try { std::ostringstream output; - export_adoc({ entry }, output); + Export::AsciiDoc({ entry }, output).print(); const string adoc = output.str(); const regex re_header