| 392 static_cast<int>(ymd.year()), |
392 static_cast<int>(ymd.year()), |
| 393 static_cast<unsigned>(ymd.month()), |
393 static_cast<unsigned>(ymd.month()), |
| 394 static_cast<unsigned>(ymd.day())); |
394 static_cast<unsigned>(ymd.day())); |
| 395 |
395 |
| 396 // Utility function to escape strings in JSON |
396 // Utility function to escape strings in JSON |
| 397 auto escape_json = [](std::string str) static { |
397 auto escape_json = [](std::string raw) static { |
| 398 size_t pos = str.find('\"'); |
398 using std::string_view_literals::operator ""sv; |
| 399 if (pos == std::string::npos) return str; |
399 auto replace_all = [](std::string str, char chr, std::string_view repl) static { |
| 400 std::string escaped = std::move(str); |
400 size_t pos = str.find(chr); |
| 401 do { |
401 if (pos == std::string::npos) return str; |
| 402 escaped.replace(pos, 1, "\\\""); |
402 std::string result = std::move(str); |
| 403 pos += 2; |
403 do { |
| 404 } while ((pos = escaped.find('\"', pos)) != std::string::npos); |
404 result.replace(pos, 1, repl); |
| 405 return escaped; |
405 pos += repl.length(); |
| |
406 } while ((pos = result.find(chr, pos)) != std::string::npos); |
| |
407 return result; |
| |
408 }; |
| |
409 return replace_all(replace_all(std::move(raw), '\\', "\\\\"), '\"', "\\\""sv); |
| 406 }; |
410 }; |
| 407 |
411 |
| 408 // Build a JSON object of commit summaries |
412 // Build a JSON object of commit summaries |
| 409 auto add_summaries = [escape_json](std::string &json, const std::vector<std::string> &summaries) { |
413 auto add_summaries = [escape_json](std::string &json, const std::vector<std::string> &summaries) { |
| 410 // We have to iterate in reverse order to sort the summaries chronologically |
414 // We have to iterate in reverse order to sort the summaries chronologically |