• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mbits-os / quick_dra / 22263246436

21 Feb 2026 07:51PM UTC coverage: 98.659%. Remained the same
22263246436

push

github

mzdun
ci: adding coverage comment to GitHub PR

4562 of 4624 relevant lines covered (98.66%)

2266.46 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

96.57
/libs/libmodels/src/models/compiler.cpp
1
// Copyright (c) 2026 midnightBITS
2
// This code is licensed under MIT license (see LICENSE for details)
3

4
#include <algorithm>
5
#include <limits>
6
#include <map>
7
#include <quick_dra/base/str.hpp>
8
#include <quick_dra/models/types.hpp>
9
#include <set>
10
#include <string>
11
#include <utility>
12
#include <vector>
13

14
namespace quick_dra {
15
        namespace {
16
                compiled_value compile_field(std::string_view input);
17

18
                struct field_compiler {
19
                        maybe_list<compiled_value> operator()(
3,168✔
20
                            std::string const& field) const noexcept {
1✔
21
                                return compile_field(field);
12,673✔
22
                        }
9,505✔
23
                        maybe_list<compiled_value> operator()(
99✔
24
                            std::vector<std::string> const& fields) const noexcept {
1✔
25
                                std::vector<compiled_value> result{};
397✔
26
                                result.reserve(fields.size());
397✔
27
                                std::transform(fields.begin(), fields.end(),
397✔
28
                                               std::back_inserter(result), compile_field);
297✔
29
                                return result;
397✔
30
                        }
397✔
31
                };
32

33
                struct section_stats {
34
                        size_t block_count{0};
35
                        bool repeatable{false};
36
                };
37

38
                compiled_value compile_sum(std::string_view input) {
397✔
39
                        auto const parts = split_sv(input.substr(2), ','_sep);
1,585✔
40
                        std::vector<unsigned> refs{};
1,585✔
41
                        refs.reserve(parts.size());
1,585✔
42

1,188✔
43
                        for (auto const& part : parts) {
5,545✔
44
                                auto const trimmed = strip_sv(part);
5,149✔
45
                                unsigned ref{};
5,149✔
46

3,861✔
47
                                auto const begin = trimmed.data();
5,149✔
48
                                auto const end = begin + trimmed.size();
5,149✔
49
                                auto const [ptr, ec] = std::from_chars(begin, end, ref);
5,149✔
50
                                if (ptr != end || ec != std::errc{}) {
1,288✔
51
                                        fmt::print(stderr, "error while parsing `{}'\n", input);
×
52
                                        return fmt::format("unparsable: {}", input);
×
53
                                }
54

3,861✔
55
                                refs.push_back(ref);
1,288✔
56
                        }
3,862✔
57
                        return addition{.refs = std::move(refs)};
397✔
58
                }
1,585✔
59

60
                compiled_value compile_currency(std::string_view input) {
1,189✔
61
                        auto const view =
3,960✔
62
                            strip_sv(input.substr(0, input.size() - "zł"sv.size()));
4,753✔
63
                        currency value{};
4,753✔
64
                        if (!currency::parse(view, value)) {
1,189✔
65
                                fmt::print(stderr, "error while parsing `{}'\n", input);
×
66
                                return fmt::format("unparsable: {}", input);
×
67
                        }
68

3,564✔
69
                        return value;
1,189✔
70
                }
3,565✔
71

72
                compiled_value compile_field(std::string_view input) {
3,367✔
73
                        if (input.starts_with('$')) {
3,367✔
74
                                if (input.starts_with("$+"sv)) {
2,146✔
75
                                        return compile_sum(input);
1,585✔
76
                                }
1,188✔
77

5,247✔
78
                                return varname::parse(input);
1,750✔
79
                        }
6,435✔
80

3,663✔
81
                        if (input.ends_with("zł")) {
1,222✔
82
                                return compile_currency(input);
4,753✔
83
                        }
3,564✔
84

99✔
85
                        return std::string{input.data(), input.size()};
100✔
86
                }
3,664✔
87

88
                compiled_block compile_block(report_section const& input) {
562✔
89
                        compiled_block result{};
2,244✔
90

1,683✔
91
                        if (input.block) {
562✔
92
                                result.id = *input.block;
529✔
93
                        }
396✔
94

1,683✔
95
                        for (auto const& [index, field] : input.fields) {
13,630✔
96
                                result.fields[index] = std::visit(field_compiler{}, field);
13,069✔
97
                        }
9,802✔
98

1,683✔
99
                        return result;
2,245✔
100
                }  // GCOV_EXCL_LINE[GCC]
1,684✔
101

102
                std::vector<compiled_section> compile_report(
66✔
103
                    std::vector<report_section> const& input) {
1✔
104
                        std::map<std::string, section_stats> stats{};
265✔
105
                        for (auto const& section : input) {
2,443✔
106
                                auto it = stats.lower_bound(section.id);
2,377✔
107
                                if (it == stats.end() || it->first != section.id) {
595✔
108
                                        it = stats.insert(it, {section.id, section_stats{}});
1,849✔
109
                                }
1,386✔
110

1,782✔
111
                                auto& stat = it->second;
2,377✔
112

1,782✔
113
                                ++stat.block_count;
2,377✔
114
                                stat.repeatable |= section.repeatable.value_or(false);
2,377✔
115
                        }
1,783✔
116
                        std::vector<compiled_section> result{};
265✔
117
                        result.reserve(stats.size());
265✔
118
                        for (auto const& section : input) {
2,443✔
119
                                if (section.fields.empty() && !section.block) continue;
595✔
120

1,683✔
121
                                auto it = std::find_if(
749✔
122
                                    result.begin(), result.end(),
1,683✔
123
                                    [&id = section.id](compiled_section const& out) {
562✔
124
                                            return out.id == id;
7,921✔
125
                                    });
5,941✔
126

1,683✔
127
                                if (it == result.end()) {
562✔
128
                                        size_t block_count = 0;
1,849✔
129
                                        bool repeatable = false;
1,849✔
130
                                        auto stat_it = stats.find(section.id);
1,849✔
131

1,386✔
132
                                        if (stat_it != stats.end()) {
463✔
133
                                                block_count = stat_it->second.block_count;
1,849✔
134
                                                repeatable = stat_it->second.repeatable;
1,849✔
135
                                        }
1,386✔
136

1,386✔
137
                                        result.push_back({section.id, repeatable});
2,003✔
138

1,386✔
139
                                        if (block_count) {
463✔
140
                                                result.back().blocks.reserve(block_count);
1,849✔
141
                                        }
1,386✔
142

1,386✔
143
                                        it = std::prev(result.end());
2,311✔
144
                                }
1,387✔
145

1,683✔
146
                                auto& output = *it;
2,245✔
147
                                output.blocks.push_back(compile_block(section));
2,245✔
148
                        }
1,684✔
149
                        return result;
287✔
150
                }
727✔
151

152
                struct data_calculator {
153
                        std::string_view id;
154
                        global_object const& ctx;
155
                        mapped_value<compiled_value> fields;
156
                        std::map<unsigned, std::set<unsigned>> crossrefs{};
1✔
157

158
                        static mapped_value<calculated_value> calculate(
573✔
159
                            std::string_view id,
160
                            mapped_value<compiled_value> const& fields,
161
                            global_object const& ctx) {
1✔
162
                                data_calculator src{id, ctx, fields};
2,293✔
163
                                return src.calculate();
2,866✔
164
                        }
2,293✔
165

166
                private:
167
                        static constexpr auto invalid_index =
168
                            std::numeric_limits<size_t>::max();
169

170
                        template <typename From, typename To>
171
                        struct value_extractor {
172
                                To operator()(auto const& value) const noexcept {
5,302✔
173
                                        return value;
21,205✔
174
                                }
15,904✔
175

176
                                To operator()(addition const&) const noexcept { return {}; }
4✔
177
                                To operator()(varname const&) const noexcept { return {}; }
4✔
178

179
                                maybe_list<To> operator()(From const& field) const noexcept {
4,705✔
180
                                        return std::visit(*this, field);
18,817✔
181
                                }
14,113✔
182

183
                                maybe_list<To> operator()(
168✔
184
                                    std::vector<From> const& fields) const noexcept {
1✔
185
                                        std::vector<To> result{};
673✔
186
                                        result.reserve(fields.size());
673✔
187
                                        for (auto const& field : fields) {
1,789✔
188
                                                result.push_back(std::visit(*this, field));
1,621✔
189
                                        }
1,216✔
190
                                        return result;
673✔
191
                                }
673✔
192
                        };
193

194
                        template <typename To, typename From>
195
                        maybe_list<To> extract(maybe_list<From> const& val) {
4,873✔
196
                                return std::visit(value_extractor<From, To>{}, val);
19,489✔
197
                        }
14,617✔
198

199
                        template <typename To, typename From>
200
                        To extract(From const& val) {
199✔
201
                                return std::visit(value_extractor<From, To>{}, val);
793✔
202
                        }
595✔
203

204
                        inline std::string label(unsigned key,
6✔
205
                                                 size_t index = invalid_index) {
1✔
206
                                return fmt::format(
21✔
207
                                    fmt::runtime(index == invalid_index ? "{} p{}"
6✔
208
                                                                        : "{} p{}.{}"),
209
                                    id, key, index);
34✔
210
                        }  // GCOV_EXCL_LINE[WIN32]
18✔
211

212
                        void fill_var(unsigned key,
1,755✔
213
                                      size_t index,
214
                                      compiled_value& tgt,
215
                                      varname const& var) {
1✔
216
                                auto const ptr = ctx.peek(var);
7,021✔
217
                                if (!ptr) {
1,756✔
218
                                        fmt::print(stderr, "{}: error: cannot find `${}'\n",
13✔
219
                                                   label(key, index), join(var.path, '.'_sep));
15✔
220
                                        return;
13✔
221
                                }
9✔
222

5,256✔
223
                                auto const& data = *ptr;
1,753✔
224

5,256✔
225
                                if (!data.value) {
1,753✔
226
                                        fmt::print(stderr,
×
227
                                                   "{}: error: reference `${}' contains no "
228
                                                   "value\n",
229
                                                   label(key, index), join(var.path, '.'_sep));
230
                                        return;
×
231
                                }
232

5,256✔
233
                                auto const& src = *data.value;
1,753✔
234
                                if (index == invalid_index) {
1,753✔
235
                                        fields.find(key)->second = extract<compiled_value>(src);
6,217✔
236
                                        return;
6,217✔
237
                                }
4,662✔
238

594✔
239
                                if (std::holds_alternative<calculated_value>(src)) {
199✔
240
                                        tgt = extract<compiled_value>(
991✔
241
                                            std::get<calculated_value>(src));
792✔
242
                                        return;
793✔
243
                                }
594✔
244

245
                                fmt::print(stderr,
246
                                           "{}: error: cannot assign a list to a "
247
                                           "list item when checking `${}'\n",
248
                                           label(key, index), join(var.path, '.'_sep));
249
                        }
1✔
250

251
                        void precalc(unsigned key, size_t index, compiled_value& tgt) {
3,424✔
252
                                if (std::holds_alternative<varname>(tgt)) {
3,424✔
253
                                        fill_var(key, index, tgt, std::get<varname>(tgt));
7,021✔
254
                                        return;
7,021✔
255
                                }
5,265✔
256

5,004✔
257
                                if (std::holds_alternative<addition>(tgt)) {
1,669✔
258
                                        auto const& refs = std::get<addition>(tgt).refs;
1,609✔
259

1,206✔
260
                                        if (index != invalid_index) {
403✔
261
                                                fmt::print(stderr,
×
262
                                                           "{}: error: field addition inside a "
263
                                                           "sub-field ({})\n",
264
                                                           label(key, index), fmt::join(refs, " + "));
265
                                                return;
×
266
                                        }
267

1,206✔
268
                                        crossrefs[key] =
403✔
269
                                            std::set<unsigned>{refs.begin(), refs.end()};
2,010✔
270
                                }
1,206✔
271
                        }
5,005✔
272

273
                        void print_crossrefs() {
274
                                for (auto const& pair : crossrefs) {
275
                                        fmt::print("{} {}\n", id, pair);
276
                                }
277
                        }
278

279
                        std::set<unsigned> get_crossrefs_layer() {
676✔
280
                                std::set<unsigned> result{};
2,701✔
281

2,025✔
282
                                for (auto& [key, refs] : crossrefs) {
3,202✔
283
                                        auto copy = refs;
2,005✔
284
                                        for (auto ref : copy) {
6,778✔
285
                                                auto it = crossrefs.find(ref);
6,277✔
286
                                                if (it == crossrefs.end()) {
1,570✔
287
                                                        refs.erase(ref);
5,221✔
288
                                                }
3,915✔
289
                                        }
4,708✔
290

1,503✔
291
                                        if (refs.empty()) {
502✔
292
                                                result.insert(key);
1,609✔
293
                                        }
1,206✔
294
                                }
2,005✔
295

2,025✔
296
                                for (auto key : result) {
3,103✔
297
                                        crossrefs.erase(key);
1,609✔
298
                                }
1,207✔
299

2,025✔
300
                                return result;
2,701✔
301
                        }  // GCOV_EXCL_LINE[GCC]
2,026✔
302

303
                        void calculate_sum(unsigned key) {
403✔
304
                                auto& tgt = std::get<compiled_value>(fields.find(key)->second);
1,609✔
305
                                auto& refs = std::get<addition>(tgt).refs;
1,609✔
306

1,206✔
307
                                currency result{};
1,609✔
308

1,206✔
309
                                for (auto ref : refs) {
5,608✔
310
                                        auto it = fields.find(ref);
5,209✔
311
                                        if (it == fields.end()) {
1,303✔
312
                                                fmt::print(stderr, "{}: error: cannot find p{}\n",
×
313
                                                           label(key), ref);
314
                                                return;
2✔
315
                                        }
316

3,906✔
317
                                        auto src = std::get_if<compiled_value>(&it->second);
1,303✔
318
                                        if (!src) {
1,303✔
319
                                                fmt::print(stderr, "{}: error: p{} is not a scalar\n",
×
320
                                                           label(key), ref);
321
                                                return;
×
322
                                        }
323

3,906✔
324
                                        auto val = std::get_if<currency>(src);
1,303✔
325
                                        if (!val) {
1,303✔
326
                                                fmt::print(stderr, "{}: error: p{} is not a number\n",
13✔
327
                                                           label(key), ref);
15✔
328
                                                return;
13✔
329
                                        }
9✔
330

3,897✔
331
                                        result = result + *val;
1,300✔
332
                                }
3,898✔
333

1,197✔
334
                                tgt = result;
400✔
335
                        }
1,198✔
336

337
                        void remove_crossrefs() {
574✔
338
                                // print_crossrefs();
1,719✔
339
                                auto layer = get_crossrefs_layer();
2,293✔
340
                                while (!layer.empty()) {
676✔
341
                                        for (auto key : layer) {
1,711✔
342
                                                calculate_sum(key);
1,609✔
343
                                        }
1,207✔
344
                                        layer = get_crossrefs_layer();
409✔
345
                                }
307✔
346
                        }
2,484✔
347

348
                        mapped_value<calculated_value> calculate() {
574✔
349
                                for (auto& [key, field] : fields) {
13,846✔
350
                                        if (std::holds_alternative<compiled_value>(field)) {
3,319✔
351
                                                precalc(key, invalid_index,
13,937✔
352
                                                        std::get<compiled_value>(field));
12,864✔
353
                                        } else {
9,649✔
354
                                                size_t index = 0;
409✔
355
                                                for (auto& child :
409✔
356
                                                     std::get<std::vector<compiled_value>>(field)) {
1,033✔
357
                                                        precalc(key, index, child);
829✔
358
                                                        ++index;
829✔
359
                                                }
622✔
360
                                        }
306✔
361
                                }
9,955✔
362

1,719✔
363
                                remove_crossrefs();
2,293✔
364

1,719✔
365
                                mapped_value<calculated_value> result{};
2,293✔
366
                                for (auto const& [key, field] : fields) {
13,846✔
367
                                        result[key] = extract<calculated_value>(field);
13,273✔
368
                                }
9,955✔
369
                                return result;
2,293✔
370
                        }  // GCOV_EXCL_LINE[GCC]
1,720✔
371
                };
372
        }  // namespace
373

374
        calculated_block calculate(compiled_block const& self,
573✔
375
                                   global_object const& ctx,
376
                                   std::string_view log_name) {
1✔
377
                std::string extended_name;
2,293✔
378
                if (!self.id.empty()) {
574✔
379
                        extended_name = fmt::format("{}.{}", log_name, self.id);
691✔
380
                        log_name = extended_name;
553✔
381
                }
414✔
382
                // GCOV_EXCL_START[GCC]
1,719✔
383
                return calculated_block{
1,720✔
384
                    // GCOV_EXCL_STOP
1,719✔
385
                    .id = self.id,
2,292✔
386
                    .fields = data_calculator::calculate(log_name, self.fields, ctx)};
3,056✔
387
        }
2,293✔
388

389
        calculated_section calculate(compiled_section const& self,
468✔
390
                                     global_object const& ctx) {
1✔
391
                calculated_section result{.id = self.id, .repeatable = self.repeatable};
1,873✔
392
                result.blocks.reserve(self.blocks.size());
1,873✔
393
                for (auto const& block : self.blocks) {
2,761✔
394
                        result.blocks.push_back(calculate(block, ctx, self.id));
2,293✔
395
                }
1,720✔
396
                return result;
1,873✔
397
        }  // GCOV_EXCL_LINE[GCC]
1,405✔
398

399
        std::vector<calculated_section> calculate(
69✔
400
            std::vector<compiled_section> const& report,
401
            global_object const& ctx) {
1✔
402
                std::vector<calculated_section> result{};
277✔
403
                result.reserve(report.size());
277✔
404
                for (auto const& section : report) {
1,942✔
405
                        result.push_back(calculate(section, ctx));
1,873✔
406
                }
1,405✔
407
                return result;
277✔
408
        }  // GCOV_EXCL_LINE[GCC]
208✔
409

410
        compiled_templates compiled_templates::compile(templates const& input) {
34✔
411
                compiled_templates result{};
133✔
412
                for (auto const& [key, report] : input.reports) {
298✔
413
                        result.reports[key] = compile_report(report);
265✔
414
                }
199✔
415
                return result;
133✔
416
        }  // GCOV_EXCL_LINE[GCC]
100✔
417
}  // namespace quick_dra
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc