Sat, 01 Feb 2025 16:01:14 +0100
implement authormap
src/heatmap.cpp | file | annotate | diff | comparison | revisions | |
src/heatmap.h | file | annotate | diff | comparison | revisions | |
src/main.cpp | file | annotate | diff | comparison | revisions | |
src/settings.cpp | file | annotate | diff | comparison | revisions | |
src/settings.h | file | annotate | diff | comparison | revisions |
--- a/src/heatmap.cpp Sat Feb 01 15:42:48 2025 +0100 +++ b/src/heatmap.cpp Sat Feb 01 16:01:14 2025 +0100 @@ -30,13 +30,13 @@ namespace chrono = std::chrono; -void fm::heatmap::add(const std::string &log) { +void fm::heatmap::add(const fm::settings &settings, const std::string &log) { using std::string_view_literals::operator ""sv; for (auto &&line: std::views::split(log, "\n"sv)) { if (line.empty()) continue; auto parts = std::views::split(line, "#"sv).begin(); - std::string author{(*parts).begin(), (*parts).end()}; + std::string author{settings.map_author({(*parts).begin(), (*parts).end()})}; int year = 0; unsigned int month = 0, day = 0;
--- a/src/heatmap.h Sat Feb 01 15:42:48 2025 +0100 +++ b/src/heatmap.h Sat Feb 01 16:01:14 2025 +0100 @@ -29,6 +29,8 @@ #include <string> #include <chrono> +#include "settings.h" + namespace fm { class heatmap { @@ -46,7 +48,7 @@ void set_repo(const std::string& repo) { m_current_repo.assign(repo); } - void add(const std::string& log); + void add(const settings &settings, const std::string& log); [[nodiscard]] const auto& data() const { return m_heatmap;
--- a/src/main.cpp Sat Feb 01 15:42:48 2025 +0100 +++ b/src/main.cpp Sat Feb 01 16:01:14 2025 +0100 @@ -70,11 +70,11 @@ "to specify a file that contains pairs of author strings, like in the following\n" "example:\n\n" " Full Name <full.name@example.org> = New Name <new.name@example.org>\n" - " just.mail@example.org = new.name@example.org\n" - " username = new.name\n\n" + " just.mail@example.org = Jus Mail <just.mail@example.org>\n" + " jane = Jane Doe <jane.doe@example.org>\n\n" "The different variants of full string, only mail address, and only local-part\n" - "can be combined as you like. When you use the \033[1m--author\033[22m option at the same\n" - "time, you only need to specify the new author names.\n\n" + "should \033[4monly\033[24m be used on the left-hand side. When you use the \033[1m--author\033[22m option at\n" + "the same time, you only need to specify the new author names.\n\n" "Finally, this tool prints an HTML page to stdout. A separate heap map is\n" "generated for each author showing commits across all repositories, unless the\n" "\033[1m--separate\033[22m option is specified in which case each repository is displayed with\n" @@ -126,6 +126,16 @@ settings.update_repos = false; } else if (chk_arg(argv[i], "-s", "--separate")) { settings.separate = true; + } else if (chk_arg(argv[i], "-A", "--authormap")) { + if (i + 1 < argc) { + if (settings.parse_authormap(argv[++i])) { + fputs("parsing authormap failed\n", stderr); + return -1; + } + } else { + fputs("missing filename for authormap\n", stderr); + return -1; + } } else if (chk_arg(argv[i], "--hg", nullptr)) { if (i + 1 < argc) { settings.hg.assign(argv[++i]); @@ -225,7 +235,7 @@ fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str()); return EXIT_FAILURE; } - heatmap.add(proc.output()); + heatmap.add(settings, proc.output()); } else { proc.setbin(settings.git); if (proc.exec_log({"log", @@ -235,7 +245,7 @@ fprintf(stderr, "Reading commit log for repo '%s' failed!\n", repo.path.c_str()); return EXIT_FAILURE; } - heatmap.add(proc.output()); + heatmap.add(settings, proc.output()); } }
--- a/src/settings.cpp Sat Feb 01 15:42:48 2025 +0100 +++ b/src/settings.cpp Sat Feb 01 16:01:14 2025 +0100 @@ -26,24 +26,75 @@ #include <format> #include <algorithm> +#include <fstream> using namespace fm; +static bool check_author(std::string_view author, std::string_view allowed) { + // check for exact match + if (author == allowed) return true; + + // check mail address matching + if (author.contains(std::format("<{}>", allowed))) return true; + + // check local-part from mail address matching + if (author.contains(std::format("<{}@", allowed))) return true; + + return false; +} + [[nodiscard]] bool settings::exclude_author(const std::string &author) const { // no allow-list means: always allowed if (authors.empty()) return false; // check all allowed authors - return std::ranges::all_of(authors, [&](const auto &allowed) { - // check for exact match - if (author == allowed) return false; - - // check mail address matching - if (author.contains(std::format("<{}>", allowed))) return false; - - // check local-part from mail address matching - if (author.contains(std::format("<{}@", allowed))) return false; - - return true; + return !std::ranges::any_of(authors, [&](const auto &allowed) { + return check_author(author, allowed); }); } + +std::string_view trim(const std::string& str) { + size_t s = str.find_first_not_of(" \t"); + size_t l = str.find_last_not_of(" \t") + 1 - s; + return std::string_view{str}.substr(s, l); +} + +int settings::parse_authormap(const std::string& path) { + std::ifstream file(path); + + if (!file.is_open()) { + return -1; + } + + std::string line; + while (std::getline(file, line)) { + line = trim(line); + + // skip empty lines and comments + if (line.empty() || line[0] == '#') { + continue; + } + + // find delimiter + size_t delimPos = line.find('='); + if (delimPos == std::string::npos) { + return -1; + } + + auto key = std::string{trim(line.substr(0, delimPos))}; + auto value = std::string{trim(line.substr(delimPos + 1))}; + authormap[std::move(key)] = std::move(value); + } + + return 0; +} + +std::string settings::map_author(std::string_view author) const { + if (authormap.empty()) return std::string{author}; + + for (const auto &[old_name, new_name] : authormap) { + if (check_author(author, old_name)) return new_name; + } + + return std::string{author}; +}
--- a/src/settings.h Sat Feb 01 15:42:48 2025 +0100 +++ b/src/settings.h Sat Feb 01 16:01:14 2025 +0100 @@ -28,6 +28,7 @@ #include <string> #include <vector> +#include <unordered_map> namespace fm { @@ -38,12 +39,17 @@ std::string git{"/usr/bin/git"}; std::vector<std::string> paths; std::vector<std::string> authors; + std::unordered_map<std::string, std::string> authormap; + unsigned char depth = 1; bool update_repos = true; bool separate = false; unsigned short year = settings_current_year; + int parse_authormap(const std::string& path); [[nodiscard]] bool exclude_author(const std::string &author) const; + + std::string map_author(std::string_view author) const; }; }