8#include "absl/status/status.h"
9#include "absl/strings/str_format.h"
25 const std::string& value) {
27 out <<
"- " << label <<
": " << value <<
"\n";
33 out <<
"- Room: 0x" << absl::StrFormat(
"%03X", room_id) <<
"\n";
38 for (
char& ch : value) {
39 const unsigned char uch =
static_cast<unsigned char>(ch);
40 if ((uch >=
'a' && uch <=
'z') || (uch >=
'A' && uch <=
'Z') ||
41 (uch >=
'0' && uch <=
'9')) {
42 ch =
static_cast<char>(std::tolower(uch));
48 std::string sanitized;
49 sanitized.reserve(value.size());
50 bool last_was_underscore =
false;
51 for (
char ch : value) {
53 if (!last_was_underscore) {
54 sanitized.push_back(ch);
56 last_was_underscore =
true;
59 sanitized.push_back(ch);
60 last_was_underscore =
false;
63 while (!sanitized.empty() && sanitized.front() ==
'_') {
64 sanitized.erase(sanitized.begin());
66 while (!sanitized.empty() && sanitized.back() ==
'_') {
70 if (sanitized.empty()) {
77 return room_id >= 0 ? absl::StrFormat(
"room_%03x", room_id) :
"room_unknown";
85 if (!base_dir_or.ok()) {
86 return base_dir_or.status();
90 paths.
reports_dir = *base_dir_or / kDungeonReportsSubdir;
107 int room_id,
const std::string& category_label,
108 const std::string& timestamp_slug) {
110 if (!paths_or.ok()) {
111 return paths_or.status();
114 return paths_or->screenshots_dir /
115 absl::StrFormat(
"%s_%s_%s.bmp", BuildRoomFileStem(room_id),
116 SanitizeFilenameComponent(category_label),
123 if (!paths_or.ok()) {
124 return paths_or.status();
127 const std::filesystem::path& log_path = paths_or->log_path;
128 std::ofstream out(log_path, std::ios::app);
130 return absl::InternalError(
131 absl::StrFormat(
"Failed to open issue log: %s", log_path.string()));
134 const std::string heading =
135 entry.
heading.empty() ? std::string(kDefaultIssueHeading) : entry.
heading;
137 ? std::string(kUnknownTimestampLabel)
140 out <<
"## " << timestamp <<
" - " << heading <<
"\n";
142 WriteDetailLine(out,
"Scope", entry.
scope);
143 WriteRoomLine(out, entry.
room_id);
148 out << kObservedIssueHeading;
152 out << kDiagnosticsHeading;
153 out <<
"```text\n" << entry.
diagnostics <<
"\n```\n\n";
156 return absl::InternalError(absl::StrFormat(
157 "Failed while writing issue log: %s", log_path.string()));
constexpr char kDungeonReportsSubdir[]
std::string BuildRoomFileStem(int room_id)
constexpr char kIssueReportsSubdir[]
void WriteDetailLine(std::ofstream &out, const char *label, const std::string &value)
constexpr char kDungeonScreenshotsSubdir[]
constexpr char kObservedIssueHeading[]
constexpr char kUnknownTimestampLabel[]
constexpr char kDefaultIssueHeading[]
std::string SanitizeFilenameComponent(std::string value)
void WriteRoomLine(std::ofstream &out, int room_id)
constexpr char kDiagnosticsHeading[]
constexpr char kDungeonIssueLogFilename[]
Editors are the view controllers for the application.
absl::StatusOr< std::filesystem::path > AppendDungeonIssueLogEntry(const DungeonIssueLogEntry &entry)
absl::StatusOr< std::filesystem::path > BuildDungeonIssueScreenshotPath(int room_id, const std::string &category_label, const std::string ×tamp_slug)
absl::StatusOr< DungeonIssueReportPaths > ResolveDungeonIssueReportPaths()
std::string observed_issue
std::string screenshot_path
std::string timestamp_display
std::string category_label
std::filesystem::path screenshots_dir
std::filesystem::path log_path
std::filesystem::path reports_dir