8#include <unordered_set>
16constexpr std::size_t DIAG_MARGIN_SIZE = 7;
18void AssertArgSize(std::size_t expected, std::size_t actual,
const char *func_name) {
19 if (actual != expected) {
20 Panic(fmt::format(
"{}: found {} args but expected {}", func_name, actual, expected));
25T AnyCastOrPanic(
const std::any &a,
const std::source_location &loc) {
27 return std::any_cast<T>(a);
28 }
catch (std::bad_any_cast &) {
29 Panic(fmt::format(
"bad any cast: {}:{}", loc.file_name(), loc.line()));
34const T &AnyCastOrPanic(
const std::any *a,
const std::source_location &loc) {
35 auto any_unwrapped = any_cast<T>(a);
36 if (any_unwrapped ==
nullptr) {
37 Panic(fmt::format(
"bad any cast: {}:{}", loc.file_name(), loc.line()));
39 return *any_unwrapped;
42void PushToStream(std::stringstream &ss,
const std::string_view s,
const std::size_t n) {
43 for (std::size_t i = 0; i < n; i++) {
48void ResetStream(std::stringstream &ss) {
50 ss.str(std::string{});
147 return "fatal error";
149 Panic(
"level_to_str: unknown diag_level");
156 return fmt::terminal_color::white;
158 return fmt::terminal_color::cyan;
160 return fmt::terminal_color::green;
162 return fmt::terminal_color::magenta;
165 return fmt::terminal_color::red;
167 Panic(
"color_for_level: unknown diag_level");
198 Panic(
"ignore_consumer::consumed_at: not implemented");
202 return consumed_count_;
207 const auto msg = diag.
msg();
208 std::fputs(msg.c_str(), stderr);
212 return isatty(fileno(stderr));
216 Panic(
"stderr_consumer::consumed_at: not implemented");
220 return consumed_count_;
224 diags_.emplace_back(diag);
234 }
catch (
const std::out_of_range &) {
235 Panic(fmt::format(
"vector_consumer::at: index {} out of range for size {}", i, diags_.size()));
240 return diags_.size();
246static const DiagTempl W_COLOR_PRECISION_LOSS_NOTE_TEMPL{
247 "color-precision-loss-previously-seen-note",
249 [](
const DiagEngine &eng,
const DiagLevel in_flight_level,
const std::vector<std::any> &args) -> std::vector<std::string> {
250 AssertArgSize(4, args.size(), std::source_location::current().function_name());
251 std::vector<std::string> msg{};
269static const DiagTempl W_COLOR_PRECISION_LOSS_TEMPL{
272 [](
const DiagEngine &eng,
const DiagLevel in_flight_level,
const std::vector<std::any> &args) -> std::vector<std::string> {
273 AssertArgSize(5, args.size(), std::source_location::current().function_name());
274 std::vector<std::string> msg{};
291 W_COLOR_PRECISION_LOSS_NOTE_TEMPL
296static const DiagTempl W_KEY_FRAME_NO_MATCHING_TILE_TEMPL{
299 "animation '{}' key frame tile '{}' was not present in any metatile entries",
304static const DiagTempl W_KEY_FRAME_MISSING_COLORS_NOTE_TEMPL{
305 "key-frame-missing-colors-list-note",
307 [](
const DiagEngine &eng,
const DiagLevel in_flight_level,
const std::vector<std::any> &args) -> std::vector<std::string> {
308 AssertArgSize(1, args.size(), std::source_location::current().function_name());
309 std::vector<std::string> msg{};
331static const DiagTempl W_KEY_FRAME_MISSING_COLORS_TEMPL{
334 [](
const DiagEngine &eng,
const DiagLevel in_flight_level,
const std::vector<std::any> &args) -> std::vector<std::string> {
335 AssertArgSize(2, args.size(), std::source_location::current().function_name());
336 std::vector<std::string> msg{};
338 const auto anim_name = AnyCastOrPanic<std::string>(args[0], std::source_location::current());
339 const auto tile_index = AnyCastOrPanic<std::size_t>(args[1], std::source_location::current());
340 constexpr auto msg_templ =
"anim '{}' key frame tile '{}' missing essential colors";
342 msg.push_back(fmt::format(msg_templ, eng.Bold(anim_name), eng.Bold(tile_index)));
346 W_KEY_FRAME_MISSING_COLORS_NOTE_TEMPL
351static const DiagTempl W_ATTRIBUTE_FORMAT_MISMATCH_TEMPL{
354 "{}: too {} attribute columns for base game '{}'",
357 "attribute-format-mismatch-note",
359 "unspecified columns will receive default values"
364static const DiagTempl W_MISSING_ATTRIBUTES_CSV_TEMPL{
367 "{}: attributes.csv did not exist",
370 "missing-attr-csv-note",
372 "all attributes will receive default or inferred values"
377static const DiagTempl W_UNUSED_ATTRIBUTE_TEMPL{
380 "found attribute for nonexistent metatile ID '{}'",
383 "unused-attribute-note",
385 "{} metatiles found at source path '{}'"
390static const DiagTempl W_TRANSPARENCY_COLLAPSE_TEMPL{
393 "color '{}' at {} '{}' subtile pixel col '{}', row '{}' collapsed to transparent under BGR conversion",
396 "transparency-collapse-note",
398 "if you did not intend this pixel to be transparent, edit the color on the respective layer sheet"
403static const DiagTempl W_UNUSED_MANUAL_PAL_COLOR_TEMPL{
407static const DiagTempl W_TILE_INDEX_OUT_OF_RANGE_TEMPL{
410 "{} '{}': tile index '{}' out of range (sheet size = {})",
413 "tile-index-out-of-range-note",
415 "substituting primary tile 0 (transparent tile) so decompilation can continue"
420static const DiagTempl W_PALETTE_INDEX_OUT_OF_RANGE_TEMPL{
423 "{} '{}': palette index '{}' out of range (numPalettesTotal = {})",
426 "palette-index-out-of-range-note",
428 "substituting palette 0 so decompilation can continue"
437static const std::unordered_map<const char *, DiagTempl> DIAG_TEMPLS{
462 AssertOrPanic(DIAG_TEMPLS.contains(name.data()), fmt::format(
"diag_template_for: unknown diagnostic: {}", name));
463 return DIAG_TEMPLS.at(name.data());
467 std::vector<const char *> keys{};
468 keys.reserve(DIAG_TEMPLS.size());
469 for (
const auto &key : DIAG_TEMPLS | std::views::keys) {
476 std::vector<const char *> keys{};
477 keys.reserve(DIAG_TEMPLS.size());
478 for (
const auto &[name, templ] : DIAG_TEMPLS) {
479 if (templ.level() == level) {
480 keys.push_back(name);
Coordinates the generation and consumption of diagnostic messages.
Defines a reusable template for standardized diagnostic reporting.
bool IsATty() const override
InFlightDiag ConsumedAt(std::size_t i) const override
std::uint64_t ConsumedCount() const override
void Consume(const InFlightDiag &diag) override
Represents an in-flight diagnostic.
std::string msg() const noexcept
bool IsATty() const override
InFlightDiag ConsumedAt(std::size_t i) const override
void Consume(const InFlightDiag &diag) override
std::uint64_t ConsumedCount() const override
std::uint64_t ConsumedCount() const override
bool IsATty() const override
void Consume(const InFlightDiag &diag) override
InFlightDiag ConsumedAt(std::size_t i) const override
constexpr auto WarnTransparencyCollapse
void Panic(const StringViewSourceLoc &s) noexcept
void AssertOrPanic(const bool condition, const StringViewSourceLoc &s)
DiagTempl DiagFor(std::string_view name)
Retrieves the DiagTempl corresponding to a given diagnostic name.
constexpr auto WarnColorPrecisionLoss
constexpr auto NoteGeneric
constexpr auto FatalGeneric
std::vector< const char * > AllDiagNames()
Gets an iterable view of all DiagTempl names in the internal table.
constexpr auto WarnUnusedAttribute
constexpr auto WarnAttributeFormatMismatch
constexpr auto WarnMissingAttributesCsv
constexpr auto WarnTileIndexOutOfRange
constexpr auto WarnPaletteIndexOutOfRange
std::string LevelToStr(DiagLevel level)
int LevelPriority(DiagLevel level)
fmt::terminal_color ColorForLevel(DiagLevel level)
constexpr auto ErrGeneric
constexpr auto WarnKeyFrameMissingColors
constexpr auto WarnKeyFrameNoMatchingTile
constexpr auto WarnUnusedManualPalColor