Porytiles
Loading...
Searching...
No Matches
diagnostics.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <any>
9#include <functional>
10#include <sstream>
11#include <string>
12#include <unordered_set>
13
14#include <fmt/color.h>
15#include <fmt/ranges.h>
16
17namespace porytiles {
18
19enum class DiagLevel {
23 kNote,
24 kRemark,
26 kError,
27 kFatal
28};
29
30std::string LevelToStr(DiagLevel level);
31
32fmt::terminal_color ColorForLevel(DiagLevel level);
33
34int LevelPriority(DiagLevel level);
35
36// The full definition for this class is in diagnostics/diagnostic_engine.hpp.
37class DiagEngine;
38
51using DynamicMsgBuilder = std::function<std::vector<std::string>(const DiagEngine &eng, DiagLevel in_flight_level,
52 const std::vector<std::any> &args)>;
53
63class DiagTempl {
64 public:
65 // clang-format off
66 explicit DiagTempl(std::string_view name, DiagLevel default_level,
67 DynamicMsgBuilder dynamic_msg_builder) noexcept
68 : name_{name},
69 default_level_{default_level},
70 dynamic_msg_builder_{std::move(dynamic_msg_builder)} {}
71
72 explicit DiagTempl(std::string_view name, DiagLevel default_level,
73 DynamicMsgBuilder dynamic_msg_builder, const std::vector<DiagTempl> &partner_diags) noexcept
74 : name_{name},
75 default_level_{default_level},
76 dynamic_msg_builder_{std::move(dynamic_msg_builder)},
77 partner_diags_{partner_diags} {}
78
79 explicit DiagTempl(std::string_view name, DiagLevel default_level,
80 std::string_view static_msg_templ) noexcept
81 : name_{name},
82 default_level_{default_level},
83 static_msg_templ_{static_msg_templ},
84 dynamic_msg_builder_{nullptr} {}
85
86 explicit DiagTempl(std::string_view name, DiagLevel default_level,
87 std::string_view static_msg_templ, const std::vector<DiagTempl> &partner_diags) noexcept
88 : name_{name},
89 default_level_{default_level},
90 static_msg_templ_{static_msg_templ},
91 dynamic_msg_builder_{nullptr},
92 partner_diags_{partner_diags} {}
93 // clang-format on
94
95 [[nodiscard]] std::string_view name() const {
96 return name_;
97 }
98
103 [[nodiscard]] DiagLevel level() const {
104 return default_level_;
105 }
106
115 [[nodiscard]] std::string_view static_msg_templ() const {
116 return static_msg_templ_;
117 }
118
127 template <typename... Args>
128 std::vector<std::string> BuildDynamicMsg(const DiagEngine &eng, const DiagLevel in_flight_level,
129 Args &&...args) const {
130 if (dynamic_msg_builder_ == nullptr) {
131 std::vector<std::string> v{};
132 v.push_back(fmt::format(fmt::runtime(static_msg_templ_), std::forward<Args>(args)...));
133 return v;
134 }
135 const std::vector<std::any> v{std::forward<Args>(args)...};
136 return dynamic_msg_builder_(eng, in_flight_level, v);
137 }
138
148 [[nodiscard]] const std::vector<DiagTempl> &partner_diags() const {
149 return partner_diags_;
150 }
151
152 private:
153 std::string name_;
154 DiagLevel default_level_;
155 std::string_view static_msg_templ_;
156 DynamicMsgBuilder dynamic_msg_builder_;
157 std::vector<DiagTempl> partner_diags_;
158};
159
167 public:
168 explicit InFlightDiag(const DiagLevel level, std::string msg, DiagTempl templ) noexcept
169 : level_{level}, msg_{std::move(msg)}, templ_{std::move(templ)} {}
170
171 [[nodiscard]] DiagLevel level() const noexcept {
172 return level_;
173 }
174
175 [[nodiscard]] std::string msg() const noexcept {
176 return msg_;
177 }
178
179 [[nodiscard]] const DiagTempl &templ() const noexcept {
180 return templ_;
181 }
182
183 private:
184 DiagLevel level_;
185 std::string msg_;
186 DiagTempl templ_;
187};
188
196 public:
197 virtual ~DiagConsumer() = default;
198 virtual void Consume(const InFlightDiag &diag) = 0;
199 [[nodiscard]] virtual bool IsATty() const = 0;
200 [[nodiscard]] virtual InFlightDiag ConsumedAt(std::size_t i) const = 0;
201 [[nodiscard]] virtual std::uint64_t ConsumedCount() const = 0;
202};
203
207class IgnoreConsumer final : public DiagConsumer {
208 public:
209 void Consume(const InFlightDiag &diag) override;
210 [[nodiscard]] bool IsATty() const override;
211 [[nodiscard]] InFlightDiag ConsumedAt(std::size_t i) const override;
212 [[nodiscard]] std::uint64_t ConsumedCount() const override;
213
214 private:
215 std::uint64_t consumed_count_{};
216};
217
221class StderrConsumer final : public DiagConsumer {
222 public:
223 void Consume(const InFlightDiag &diag) override;
224 [[nodiscard]] bool IsATty() const override;
225 [[nodiscard]] InFlightDiag ConsumedAt(std::size_t i) const override;
226 [[nodiscard]] std::uint64_t ConsumedCount() const override;
227
228 private:
229 std::uint64_t consumed_count_{};
230};
231
235class VectorConsumer final : public DiagConsumer {
236 public:
237 void Consume(const InFlightDiag &diag) override;
238 [[nodiscard]] bool IsATty() const override;
239 [[nodiscard]] InFlightDiag ConsumedAt(std::size_t i) const override;
240 [[nodiscard]] std::uint64_t ConsumedCount() const override;
241
242 private:
243 std::vector<InFlightDiag> diags_;
244};
245
246// clang-format off
247
253constexpr auto NoteGeneric = "note-generic";
254
260constexpr auto WarnColorPrecisionLoss = "color-precision-loss";
261constexpr auto WarnKeyFrameNoMatchingTile = "key-frame-no-matching-tile";
262constexpr auto WarnKeyFrameMissingColors = "key-frame-missing-colors";
263constexpr auto WarnAttributeFormatMismatch = "attribute-format-mismatch";
264constexpr auto WarnMissingAttributesCsv = "missing-attributes-csv";
265constexpr auto WarnUnusedAttribute = "unused-attribute";
266constexpr auto WarnTransparencyCollapse = "transparency-collapse";
267constexpr auto WarnUnusedManualPalColor = "unused-manual-pal-color";
268constexpr auto WarnTileIndexOutOfRange = "tile-index-out-of-range";
269constexpr auto WarnPaletteIndexOutOfRange = "palette-index-out-of-range";
270
271
277constexpr auto ErrGeneric = "error-generic";
278constexpr auto FatalGeneric = "error-fatal-generic";
279
280// clang-format on
281
294DiagTempl DiagFor(std::string_view name);
295
301std::vector<const char *> AllDiagNames();
302
304std::vector<const char *> AllDiagNames(DiagLevel level);
305
306} // namespace porytiles
307
315template <>
316struct std::hash<porytiles::DiagTempl> {
317 std::size_t operator()(const porytiles::DiagTempl &templ) const noexcept {
318 std::size_t seed = 0x39A9C07E;
319 seed ^= (seed << 6) + (seed >> 2) + 0x6EFC4121 + std::hash<std::string>{}(std::string{templ.name()});
320 seed ^= (seed << 6) + (seed >> 2) + 0x14AA7601 + static_cast<std::size_t>(templ.level());
321 return seed;
322 }
323};
A customizable consumer for diagnostic messages.
virtual bool IsATty() const =0
virtual InFlightDiag ConsumedAt(std::size_t i) const =0
virtual ~DiagConsumer()=default
virtual void Consume(const InFlightDiag &diag)=0
virtual std::uint64_t ConsumedCount() const =0
Coordinates the generation and consumption of diagnostic messages.
Defines a reusable template for standardized diagnostic reporting.
DiagTempl(std::string_view name, DiagLevel default_level, std::string_view static_msg_templ, const std::vector< DiagTempl > &partner_diags) noexcept
DiagLevel level() const
Gets the default diagnostic level of the template.
std::vector< std::string > BuildDynamicMsg(const DiagEngine &eng, const DiagLevel in_flight_level, Args &&...args) const
Builds a dynamic message for this DiagTempl based on the configured message builder.
DiagTempl(std::string_view name, DiagLevel default_level, DynamicMsgBuilder dynamic_msg_builder) noexcept
std::string_view static_msg_templ() const
Gets the static message template.
std::string_view name() const
DiagTempl(std::string_view name, DiagLevel default_level, std::string_view static_msg_templ) noexcept
const std::vector< DiagTempl > & partner_diags() const
Gets a vector of partner DiagTempl for this DiagTempl.
DiagTempl(std::string_view name, DiagLevel default_level, DynamicMsgBuilder dynamic_msg_builder, const std::vector< DiagTempl > &partner_diags) noexcept
A DiagConsumer implementation that simply ignores the provided diagnostic.
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.
const DiagTempl & templ() const noexcept
DiagLevel level() const noexcept
InFlightDiag(const DiagLevel level, std::string msg, DiagTempl templ) noexcept
std::string msg() const noexcept
A DiagConsumer implementation that pushes diagnostic messages to stderr.
bool IsATty() const override
InFlightDiag ConsumedAt(std::size_t i) const override
void Consume(const InFlightDiag &diag) override
std::uint64_t ConsumedCount() const override
A DiagConsumer implementation that pushes diagnostic messages to an internal vector.
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
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
std::function< std::vector< std::string >(const DiagEngine &eng, DiagLevel in_flight_level, const std::vector< std::any > &args)> DynamicMsgBuilder
An alias for a dynamic diagnostic message builder function.
constexpr auto WarnUnusedManualPalColor
std::size_t operator()(const porytiles::DiagTempl &templ) const noexcept