Tue, 18 Feb 2025 18:43:01 +0100
do not pull repos by default - fixes #587
/* Copyright 2025 Mike Becker. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "html.h" #include <cstdio> using namespace std::chrono; namespace html { static constexpr const char* weekdays[] = {"Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"}; static unsigned indentation; static const char *tabs = " "; static void indent(int change = 0) { indentation += change; fwrite(tabs, 4, indentation, stdout); } static std::string encode(const std::string &data) { std::string buffer; buffer.reserve(data.size()+16); for (const char &pos: data) { switch (pos) { case '&': buffer.append("&"); break; case '\"': buffer.append("""); break; case '\'': buffer.append("'"); break; case '<': buffer.append("<"); break; case '>': buffer.append(">"); break; default: buffer.append(&pos, 1); break; } } return buffer; } } void html::open(bool fragment) { if (fragment) { puts("<div class=\"heatmap-content\">"); indentation = 1; } else { puts(R"(<html> <head> <style> table.heatmap { table-layout: fixed; border-collapse: collapse; font-family: sans-serif; } table.heatmap td, table.heatmap th { text-align: center; border: solid 1px lightgray; height: 1.5em; } table.heatmap td { border: solid 1px lightgray; width: 1.5em; height: 1.5em; } table.heatmap td.out-of-range { background-color: gray; } table.heatmap td.zero-commits { background-color: white; } table.heatmap td.one-commit { background-color: #80E7A0; } table.heatmap td.up-to-5-commits { background-color: #30D350; } table.heatmap td.up-to-10-commits { background-color: #00BF00; } table.heatmap td.up-to-20-commits { background-color: #00A300; } table.heatmap td.commit-spam { background-color: #008000; } </style> </head> <body> <div class=\"heatmap-content\">)"); indentation = 3; } } void html::close(bool fragment) { if (fragment) { puts("</div>"); } else { puts(" </div>\n </body>\n</html>"); } } void html::h1(const std::string& heading) { indent(); printf("<h1>%s</h1>\n", encode(heading).c_str()); } void html::h2(const std::string& heading) { indent(); printf("<h2>%s</h2>\n", encode(heading).c_str()); } void html::table_begin(year y) { static constexpr const char* months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; // compute the column spans, first unsigned colspans[12] = {}; { unsigned total_cols = 0; sys_days day{year_month_day{y, January, 1d}}; for (unsigned col = 0; col < 12; ++col) { while (total_cols < html::columns && year_month_day{day}.month() <= month{col + 1}) { ++total_cols; ++colspans[col]; day += days{7}; } } } // now render the table heading indent(); puts("<table class=\"heatmap\">"); indent(1); puts("<tr>"); indent(1); puts("<th></th>"); for (unsigned i = 0 ; i < 12 ; i++) { indent(); printf("<th scope=\"col\" colspan=\"%d\">%s</th>\n", colspans[i], months[i]); } indent(-1); puts("</tr>"); } void html::table_end() { indentation--; indent(); puts("</table>"); } void html::row_begin(unsigned int row) { indent(); puts("<tr>"); indent(1); printf("<th scope=\"row\">%s</th>\n", weekdays[row]); } void html::row_end() { indent(-1); puts("</tr>"); } void html::cell_out_of_range() { indent(); puts("<td class=\"out-of-range\"></td>"); } void html::cell(year_month_day ymd, unsigned commits) { const char *color_class; if (commits == 0) { color_class = "zero-commits"; } else if (commits == 1) { color_class = "one-commit"; } else if (commits <= 5) { color_class = "up-to-5-commits"; } else if (commits <= 10) { color_class = "up-to-10-commits"; } else if (commits <= 20) { color_class = "up-to-20-commits"; } else { color_class = "commit-spam"; } indent(); printf("<td class=\"%s\" title=\"%s, %d-%02u-%02u: %u commits\"></td>\n", color_class, weekdays[weekday(ymd).iso_encoding() - 1], static_cast<int>(ymd.year()), static_cast<unsigned>(ymd.month()), static_cast<unsigned>(ymd.day()), commits); }