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

mbits-os / quick_dra / 22077168793

16 Feb 2026 08:51PM UTC coverage: 97.995% (+11.0%) from 87.008%
22077168793

push

github

mzdun
chore: tweak Clang targets

4447 of 4538 relevant lines covered (97.99%)

1922.16 hits per line

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

94.82
/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 <map>
5
#include <quick_dra/base/str.hpp>
6
#include <quick_dra/models/types.hpp>
7
#include <set>
8

9
namespace quick_dra {
10
        namespace {
11
                compiled_value compile_field(std::string_view input);
12

13
                struct field_compiler {
14
                        maybe_list<compiled_value> operator()(
3,168✔
15
                            std::string const& field) const noexcept {
1✔
16
                                return compile_field(field);
9,505✔
17
                        }
6,337✔
18
                        maybe_list<compiled_value> operator()(
99✔
19
                            std::vector<std::string> const& fields) const noexcept {
1✔
20
                                std::vector<compiled_value> result{};
298✔
21
                                result.reserve(fields.size());
298✔
22
                                std::transform(fields.begin(), fields.end(),
298✔
23
                                               std::back_inserter(result), compile_field);
198✔
24
                                return result;
298✔
25
                        }
298✔
26
                };
27

28
                struct section_stats {
29
                        size_t block_count{0};
30
                        bool repeatable{false};
31
                };
32

33
                compiled_value compile_sum(std::string_view input) {
397✔
34
                        auto const parts = split_sv(input.substr(2), ','_sep);
1,189✔
35
                        std::vector<unsigned> refs{};
1,189✔
36
                        refs.reserve(parts.size());
1,189✔
37

792✔
38
                        for (auto const& part : parts) {
4,258✔
39
                                auto const trimmed = strip_sv(part);
3,862✔
40
                                unsigned ref{};
3,862✔
41

2,574✔
42
                                auto const begin = trimmed.data();
3,862✔
43
                                auto const end = begin + trimmed.size();
3,862✔
44
                                auto const [ptr, ec] = std::from_chars(begin, end, ref);
3,862✔
45
                                if (ptr != end || ec != std::errc{}) {
1,288✔
46
                                        fmt::print(stderr, "error while parsing `{}'\n", input);
×
47
                                        return fmt::format("unparsable: {}", input);
×
48
                                }
49

2,574✔
50
                                refs.push_back(ref);
1,288✔
51
                        }
2,575✔
52
                        return addition{.refs = std::move(refs)};
397✔
53
                }
1,189✔
54

55
                compiled_value compile_currency(std::string_view input) {
1,189✔
56
                        auto const view =
2,772✔
57
                            strip_sv(input.substr(0, input.size() - "zł"sv.size()));
3,565✔
58
                        currency value{};
3,565✔
59
                        if (!currency::parse(view, value)) {
1,189✔
60
                                fmt::print(stderr, "error while parsing `{}'\n", input);
×
61
                                return fmt::format("unparsable: {}", input);
×
62
                        }
63

2,376✔
64
                        return value;
1,189✔
65
                }
2,377✔
66

67
                compiled_value compile_field(std::string_view input) {
3,367✔
68
                        if (input.starts_with('$')) {
3,367✔
69
                                if (input.starts_with("$+"sv)) {
2,146✔
70
                                        return compile_sum(input);
1,189✔
71
                                }
792✔
72

3,498✔
73
                                return varname::parse(input);
1,750✔
74
                        }
4,290✔
75

2,442✔
76
                        if (input.ends_with("zł")) {
1,222✔
77
                                return compile_currency(input);
3,565✔
78
                        }
2,376✔
79

66✔
80
                        return std::string{input.data(), input.size()};
100✔
81
                }
2,443✔
82

83
                compiled_block compile_block(report_section const& input) {
562✔
84
                        compiled_block result{};
1,683✔
85

1,122✔
86
                        if (input.block) {
562✔
87
                                result.id = *input.block;
397✔
88
                        }
264✔
89

1,122✔
90
                        for (auto const& [index, field] : input.fields) {
10,363✔
91
                                result.fields[index] = std::visit(field_compiler{}, field);
9,802✔
92
                        }
6,535✔
93

1,122✔
94
                        return result;
1,684✔
95
                }  // GCOV_EXCL_LINE[GCC]
1,123✔
96

97
                std::vector<compiled_section> compile_report(
66✔
98
                    std::vector<report_section> const& input) {
1✔
99
                        std::map<std::string, section_stats> stats{};
199✔
100
                        for (auto const& section : input) {
1,849✔
101
                                auto it = stats.lower_bound(section.id);
1,783✔
102
                                if (it == stats.end() || it->first != section.id) {
595✔
103
                                        it = stats.insert(it, {section.id, section_stats{}});
1,387✔
104
                                }
924✔
105

1,188✔
106
                                auto& stat = it->second;
1,783✔
107

1,188✔
108
                                ++stat.block_count;
1,783✔
109
                                stat.repeatable |= section.repeatable.value_or(false);
1,783✔
110
                        }
1,189✔
111
                        std::vector<compiled_section> result{};
199✔
112
                        result.reserve(stats.size());
199✔
113
                        for (auto const& section : input) {
1,849✔
114
                                if (section.fields.empty() && !section.block) continue;
595✔
115

1,122✔
116
                                auto it = std::find_if(
749✔
117
                                    result.begin(), result.end(),
1,122✔
118
                                    [&id = section.id](compiled_section const& out) {
562✔
119
                                            return out.id == id;
5,941✔
120
                                    });
3,961✔
121

1,122✔
122
                                if (it == result.end()) {
562✔
123
                                        size_t block_count = 0;
1,387✔
124
                                        bool repeatable = false;
1,387✔
125
                                        auto stat_it = stats.find(section.id);
1,387✔
126

924✔
127
                                        if (stat_it != stats.end()) {
463✔
128
                                                block_count = stat_it->second.block_count;
1,387✔
129
                                                repeatable = stat_it->second.repeatable;
1,387✔
130
                                        }
924✔
131

924✔
132
                                        result.push_back({section.id, repeatable});
1,541✔
133

924✔
134
                                        if (block_count) {
463✔
135
                                                result.back().blocks.reserve(block_count);
1,387✔
136
                                        }
924✔
137

924✔
138
                                        it = std::prev(result.end());
1,849✔
139
                                }
925✔
140

1,122✔
141
                                auto& output = *it;
1,684✔
142
                                output.blocks.push_back(compile_block(section));
1,684✔
143
                        }
1,123✔
144
                        return result;
221✔
145
                }
661✔
146

147
                struct data_calculator {
148
                        std::string_view id;
149
                        global_object const& ctx;
150
                        mapped_value<compiled_value> fields;
151
                        std::map<unsigned, std::set<unsigned>> crossrefs{};
1✔
152

153
                        static mapped_value<calculated_value> calculate(
561✔
154
                            std::string_view id,
155
                            mapped_value<compiled_value> const& fields,
156
                            global_object const& ctx) {
1✔
157
                                data_calculator src{id, ctx, fields};
1,684✔
158
                                return src.calculate();
2,245✔
159
                        }
1,684✔
160

161
                private:
162
                        static constexpr auto invalid_index =
163
                            std::numeric_limits<size_t>::max();
164

165
                        template <typename From, typename To>
166
                        struct value_extractor {
167
                                To operator()(auto const& value) const noexcept {
5,248✔
168
                                        return value;
15,742✔
169
                                }
10,495✔
170

171
                                To operator()(addition const&) const noexcept { return {}; }
172
                                To operator()(varname const&) const noexcept { return {}; }
173

174
                                maybe_list<To> operator()(From const& field) const noexcept {
4,654✔
175
                                        return std::visit(*this, field);
13,960✔
176
                                }
9,307✔
177

178
                                maybe_list<To> operator()(
165✔
179
                                    std::vector<From> const& fields) const noexcept {
1✔
180
                                        std::vector<To> result{};
496✔
181
                                        result.reserve(fields.size());
496✔
182
                                        for (auto const& field : fields) {
1,354✔
183
                                                result.push_back(std::visit(*this, field));
1,189✔
184
                                        }
793✔
185
                                        return result;
496✔
186
                                }
496✔
187
                        };
188

189
                        template <typename To, typename From>
190
                        maybe_list<To> extract(maybe_list<From> const& val) {
4,819✔
191
                                return std::visit(value_extractor<From, To>{}, val);
14,455✔
192
                        }
9,637✔
193

194
                        template <typename To, typename From>
195
                        To extract(From const& val) {
199✔
196
                                return std::visit(value_extractor<From, To>{}, val);
595✔
197
                        }
397✔
198

199
                        inline std::string label(unsigned key,
200
                                                 size_t index = invalid_index) {
201
                                return fmt::format(
202
                                    fmt::runtime(index == invalid_index ? "{} p{}"
203
                                                                        : "{} p{}.{}"),
204
                                    id, key, index);
205
                        }  // GCOV_EXCL_LINE[WIN32]
206

207
                        void fill_var(unsigned key,
1,749✔
208
                                      size_t index,
209
                                      compiled_value& tgt,
210
                                      varname const& var) {
1✔
211
                                auto const ptr = ctx.peek(var);
5,248✔
212
                                if (!ptr) {
1,750✔
213
                                        fmt::print(stderr, "{}: error: cannot find `${}'\n",
×
214
                                                   label(key, index), join(var.path, '.'_sep));
215
                                        return;
×
216
                                }
217

3,498✔
218
                                auto const& data = *ptr;
1,750✔
219

3,498✔
220
                                if (!data.value) {
1,750✔
221
                                        fmt::print(stderr,
×
222
                                                   "{}: error: reference `${}' contains no "
223
                                                   "value\n",
224
                                                   label(key, index), join(var.path, '.'_sep));
225
                                        return;
×
226
                                }
227

3,498✔
228
                                auto const& src = *data.value;
1,750✔
229
                                if (index == invalid_index) {
1,750✔
230
                                        fields.find(key)->second = extract<compiled_value>(src);
4,654✔
231
                                        return;
4,654✔
232
                                }
3,102✔
233

396✔
234
                                if (std::holds_alternative<calculated_value>(src)) {
199✔
235
                                        tgt = extract<compiled_value>(
793✔
236
                                            std::get<calculated_value>(src));
594✔
237
                                        return;
595✔
238
                                }
396✔
239

240
                                fmt::print(stderr,
241
                                           "{}: error: cannot assign a list to a "
242
                                           "list item when checking `${}'\n",
243
                                           label(key, index), join(var.path, '.'_sep));
244
                        }
1✔
245

246
                        void precalc(unsigned key, size_t index, compiled_value& tgt) {
3,367✔
247
                                if (std::holds_alternative<varname>(tgt)) {
3,367✔
248
                                        fill_var(key, index, tgt, std::get<varname>(tgt));
5,248✔
249
                                        return;
5,248✔
250
                                }
3,498✔
251

3,234✔
252
                                if (std::holds_alternative<addition>(tgt)) {
1,618✔
253
                                        auto const& refs = std::get<addition>(tgt).refs;
1,189✔
254

792✔
255
                                        if (index != invalid_index) {
397✔
256
                                                fmt::print(stderr,
×
257
                                                           "{}: error: field addition inside a "
258
                                                           "sub-field ({})\n",
259
                                                           label(key, index), fmt::join(refs, " + "));
260
                                                return;
×
261
                                        }
262

792✔
263
                                        crossrefs[key] =
397✔
264
                                            std::set<unsigned>{refs.begin(), refs.end()};
1,584✔
265
                                }
792✔
266
                        }
3,235✔
267

268
                        void print_crossrefs() {
269
                                for (auto const& pair : crossrefs) {
270
                                        fmt::print("{} {}\n", id, pair);
271
                                }
272
                        }
273

274
                        std::set<unsigned> get_crossrefs_layer() {
661✔
275
                                std::set<unsigned> result{};
1,981✔
276

1,320✔
277
                                for (auto& [key, refs] : crossrefs) {
2,476✔
278
                                        auto copy = refs;
1,486✔
279
                                        for (auto ref : copy) {
5,149✔
280
                                                auto it = crossrefs.find(ref);
4,654✔
281
                                                if (it == crossrefs.end()) {
1,552✔
282
                                                        refs.erase(ref);
3,862✔
283
                                                }
2,574✔
284
                                        }
3,103✔
285

990✔
286
                                        if (refs.empty()) {
496✔
287
                                                result.insert(key);
1,189✔
288
                                        }
792✔
289
                                }
1,486✔
290

1,320✔
291
                                for (auto key : result) {
2,377✔
292
                                        crossrefs.erase(key);
1,189✔
293
                                }
793✔
294

1,320✔
295
                                return result;
1,981✔
296
                        }  // GCOV_EXCL_LINE[GCC]
1,321✔
297

298
                        void calculate_sum(unsigned key) {
397✔
299
                                auto& tgt = std::get<compiled_value>(fields.find(key)->second);
1,189✔
300
                                auto& refs = std::get<addition>(tgt).refs;
1,189✔
301

792✔
302
                                currency result{};
1,189✔
303

792✔
304
                                for (auto ref : refs) {
4,258✔
305
                                        auto it = fields.find(ref);
3,862✔
306
                                        if (it == fields.end()) {
1,288✔
307
                                                fmt::print(stderr, "{}: error: cannot find p{}\n",
×
308
                                                           label(key), ref);
309
                                                return;
×
310
                                        }
311

2,574✔
312
                                        auto src = std::get_if<compiled_value>(&it->second);
1,288✔
313
                                        if (!src) {
1,288✔
314
                                                fmt::print(stderr, "{}: error: p{} is not a scalar\n",
×
315
                                                           label(key), ref);
316
                                                return;
×
317
                                        }
318

2,574✔
319
                                        auto val = std::get_if<currency>(src);
1,288✔
320
                                        if (!val) {
1,288✔
321
                                                fmt::print(stderr, "{}: error: p{} is not a number\n",
×
322
                                                           label(key), ref);
323
                                                return;
×
324
                                        }
325

2,574✔
326
                                        result = result + *val;
1,288✔
327
                                }
2,575✔
328

792✔
329
                                tgt = result;
397✔
330
                        }
793✔
331

332
                        void remove_crossrefs() {
562✔
333
                                // print_crossrefs();
1,122✔
334
                                auto layer = get_crossrefs_layer();
1,684✔
335
                                while (!layer.empty()) {
661✔
336
                                        for (auto key : layer) {
1,288✔
337
                                                calculate_sum(key);
1,189✔
338
                                        }
793✔
339
                                        layer = get_crossrefs_layer();
298✔
340
                                }
199✔
341
                        }
1,871✔
342

343
                        mapped_value<calculated_value> calculate() {
562✔
344
                                for (auto& [key, field] : fields) {
10,363✔
345
                                        if (std::holds_alternative<compiled_value>(field)) {
3,268✔
346
                                                precalc(key, invalid_index,
10,561✔
347
                                                        std::get<compiled_value>(field));
9,504✔
348
                                        } else {
6,337✔
349
                                                size_t index = 0;
298✔
350
                                                for (auto& child :
298✔
351
                                                     std::get<std::vector<compiled_value>>(field)) {
793✔
352
                                                        precalc(key, index, child);
595✔
353
                                                        ++index;
595✔
354
                                                }
397✔
355
                                        }
198✔
356
                                }
6,535✔
357

1,122✔
358
                                remove_crossrefs();
1,684✔
359

1,122✔
360
                                mapped_value<calculated_value> result{};
1,684✔
361
                                for (auto const& [key, field] : fields) {
10,363✔
362
                                        result[key] = extract<calculated_value>(field);
9,802✔
363
                                }
6,535✔
364
                                return result;
1,684✔
365
                        }  // GCOV_EXCL_LINE[GCC]
1,123✔
366
                };
367
        }  // namespace
368

369
        calculated_block calculate(compiled_block const& self,
561✔
370
                                   global_object const& ctx,
371
                                   std::string_view log_name) {
1✔
372
                std::string extended_name;
1,684✔
373
                if (!self.id.empty()) {
562✔
374
                        extended_name = fmt::format("{}.{}", log_name, self.id);
529✔
375
                        log_name = extended_name;
397✔
376
                }
264✔
377
                // GCOV_EXCL_START[GCC]
1,122✔
378
                return calculated_block{
1,123✔
379
                    // GCOV_EXCL_STOP
1,122✔
380
                    .id = self.id,
1,683✔
381
                    .fields = data_calculator::calculate(log_name, self.fields, ctx)};
2,431✔
382
        }
1,684✔
383

384
        calculated_section calculate(compiled_section const& self,
462✔
385
                                     global_object const& ctx) {
1✔
386
                calculated_section result{.id = self.id, .repeatable = self.repeatable};
1,387✔
387
                result.blocks.reserve(self.blocks.size());
1,387✔
388
                for (auto const& block : self.blocks) {
2,146✔
389
                        result.blocks.push_back(calculate(block, ctx, self.id));
1,684✔
390
                }
1,123✔
391
                return result;
1,387✔
392
        }  // GCOV_EXCL_LINE[GCC]
925✔
393

394
        std::vector<calculated_section> calculate(
66✔
395
            std::vector<compiled_section> const& report,
396
            global_object const& ctx) {
1✔
397
                std::vector<calculated_section> result{};
199✔
398
                result.reserve(report.size());
199✔
399
                for (auto const& section : report) {
1,453✔
400
                        result.push_back(calculate(section, ctx));
1,387✔
401
                }
925✔
402
                return result;
199✔
403
        }  // GCOV_EXCL_LINE[GCC]
133✔
404

405
        compiled_templates compiled_templates::compile(templates const& input) {
34✔
406
                compiled_templates result{};
100✔
407
                for (auto const& [key, report] : input.reports) {
232✔
408
                        result.reports[key] = compile_report(report);
199✔
409
                }
133✔
410
                return result;
100✔
411
        }  // GCOV_EXCL_LINE[GCC]
67✔
412
}  // 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