Porytiles
Loading...
Searching...
No Matches
diagnostic_engine.hpp
Go to the documentation of this file.
1#pragma once
2
8#include <algorithm>
9#include <memory>
10#include <ranges>
11#include <set>
12#include <sstream>
13#include <unordered_map>
14
17
18namespace porytiles {
19
28 public:
29 DiagEngine() : consumer_(std::make_unique<IgnoreConsumer>()), all_warnings_disabled_{false} {}
30
31 explicit DiagEngine(std::unique_ptr<DiagConsumer> consumer)
32 : consumer_(std::move(consumer)), all_warnings_disabled_{false} {}
33
34 void EnableAllWarnings();
35
36 void DisableAllWarnings();
37
39
40 void EnableAtLevel(std::string_view diag, DiagLevel override);
41
42 void DisableAtLevel(std::string_view diag, DiagLevel override);
43
44 [[nodiscard]] DiagLevel EnabledAt(std::string_view diag) const;
45
46 [[nodiscard]] std::uint64_t InFlightCountForLevel(DiagLevel level) const;
47
48 [[nodiscard]] std::uint64_t InFlightCountFor(std::string_view diag) const;
49
50 // ReSharper disable once CppParameterMayBeConst
51 template <typename... T>
52 void Report(std::string_view diag, T &&...args) {
53 // If this diagnostic is not enabled, exit now
54 if (!IsEnabled(diag)) {
55 return;
56 }
57 const auto &templ = DiagFor(diag);
58
59 // Compute in-flight level based on user settings
60 const auto in_flight_level = ComputeLevel(diag);
61
62 ReportHelper(templ, in_flight_level, std::forward<T>(args)...);
63
64 // Increment diagnostic counts
65 auto diag_str = std::string{diag};
66 if (!diag_counts_.contains(diag_str)) {
67 diag_counts_.insert({diag_str, 0});
68 }
69 diag_counts_[diag_str] += 1;
70 }
71
72 template <typename... T>
73 void ReportPartner(std::string_view diag, std::size_t partner_index, T &&...args) {
74 const auto &parent_templ = DiagFor(diag);
75
76 if (partner_index >= parent_templ.partner_diags().size()) {
77 Panic(fmt::format("partner index {} out of bounds for diag {}", partner_index, diag));
78 }
79
80 // If this diagnostic is not enabled, exit now
81 if (!IsEnabled(diag)) {
82 return;
83 }
84
85 const auto &partner_templ = parent_templ.partner_diags().at(partner_index);
86 const auto in_flight_level = partner_templ.level();
87
88 ReportHelper(partner_templ, in_flight_level, std::forward<T>(args)...);
89 }
90
91 template <typename T>
92 auto Style(const T &t, fmt::text_style ts) const {
93 return fmt::styled(t, consumer_->IsATty() ? ts : fmt::text_style{});
94 }
95
96 template <typename T>
97 auto Bold(const T &t) const {
98 return Style(t, fmt::emphasis::bold);
99 }
100
101 [[nodiscard]] const DiagConsumer &consumer() const;
102
103 private:
104 std::unique_ptr<DiagConsumer> consumer_;
105 bool all_warnings_disabled_;
106 std::unordered_map<std::string, std::set<DiagLevel>> enabled_at_level_;
107 std::unordered_map<std::string, std::uint64_t> diag_counts_;
108 std::vector<InFlightDiag> in_flight_diags_;
109
110 template <typename... T>
111 void ReportHelper(const DiagTempl &templ, DiagLevel in_flight_level, T &&...args) {
112 // Fill in message template
113 std::vector<std::string> raw_msg;
114 try {
115 raw_msg = templ.BuildDynamicMsg(*this, in_flight_level, std::forward<T>(args)...);
116 } catch (const std::exception &e) {
117 Panic(fmt::format("{} build_message failed: {}:", templ.name(), e.what()));
118 }
119
120 // Construct the message string for the consumer
121 if (raw_msg.empty()) {
122 Panic(fmt::format("diagnostic {} raw_msg vector was empty", templ.name()));
123 }
124 const std::string constructed_msg = ConstructMsgStr(in_flight_level, templ, raw_msg);
125
126 // Set diagnostic in-flight and then consume it
127 const auto in_flight = InFlightDiag{in_flight_level, constructed_msg, templ};
128 in_flight_diags_.push_back(in_flight);
129 consumer_->Consume(in_flight);
130 }
131
132 [[nodiscard]] DiagLevel ComputeLevel(std::string_view diag) const;
133
134 [[nodiscard]] bool IsEnabled(std::string_view diag) const;
135
136 [[nodiscard]] std::string ConstructMsgStr(DiagLevel in_flight_level, const DiagTempl &templ,
137 const std::vector<std::string> &msg) const;
138};
139
140} // namespace porytiles
A customizable consumer for diagnostic messages.
Coordinates the generation and consumption of diagnostic messages.
std::uint64_t InFlightCountForLevel(DiagLevel level) const
void ReportPartner(std::string_view diag, std::size_t partner_index, T &&...args)
void DisableAtLevel(std::string_view diag, DiagLevel override)
void EnableAtLevel(std::string_view diag, DiagLevel override)
std::uint64_t InFlightCountFor(std::string_view diag) const
DiagEngine(std::unique_ptr< DiagConsumer > consumer)
auto Bold(const T &t) const
DiagLevel EnabledAt(std::string_view diag) const
auto Style(const T &t, fmt::text_style ts) const
const DiagConsumer & consumer() const
void Report(std::string_view diag, T &&...args)
Defines a reusable template for standardized diagnostic reporting.
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.
std::string_view name() const
A DiagConsumer implementation that simply ignores the provided diagnostic.
void Panic(const StringViewSourceLoc &s) noexcept
Definition panic.hpp:31
DiagTempl DiagFor(std::string_view name)
Retrieves the DiagTempl corresponding to a given diagnostic name.