• 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

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

4
#include <fmt/format.h>
5
#include <fmt/std.h>
6
#include <cmath>
7
#include <fstream>
8
#include <quick_dra/base/str.hpp>
9
#include <quick_dra/models/project_reader.hpp>
10

11
namespace quick_dra::v1 {
12
        namespace {
13
                static constexpr auto app_name = "Quick-DRA"sv;
14
        }
15

16
        std::optional<config> config::parse_yaml(
36✔
17
            std::filesystem::path const& path) {
1✔
18
                return parser::parse_yaml_file<config>(path, app_name);
109✔
19
        }
73✔
20

21
        bool config::postprocess() { return version == kVersion; }
34✔
22

23
        template <typename Named>
24
        bool parse_and_validate_name(Named& named) noexcept {
67✔
25
                auto const name = split_sv(named.last_name, ", "_sep, 1);
199✔
26
                if (name.size() != 2) return false;
67✔
27
                named.first_name = strip_sv(name[1]);
67✔
28
                named.last_name = strip_sv(name[0]);
199✔
29
                return !(named.last_name.empty() || named.first_name.empty());
67✔
30
        }
199✔
31

32
        bool payer_t::postprocess() {
34✔
33
                kind.clear();
100✔
34
                document.clear();
100✔
35

66✔
36
                if (id_card) {
34✔
37
                        if (passport) {
38
                                return false;
×
39
                        }
40
                        kind = "1"sv;
41
                        document = std::move(*id_card);
×
42
                }
43

66✔
44
                if (passport) {
34✔
45
                        kind = "2"sv;
100✔
46
                        document = std::move(*passport);
100✔
47
                }
66✔
48

66✔
49
                return parse_and_validate_name(*this) &&
67✔
50
                       !(social_id.empty() || tax_id.empty() || kind.empty() ||
33✔
51
                         document.empty());
66✔
52
        }
67✔
53

54
        bool insured_t::postprocess() {
34✔
55
                if (!parse_and_validate_name(*this)) return false;
34✔
56

66✔
57
                kind.clear();
34✔
58
                document.clear();
100✔
59

66✔
60
                if (social_id) {
34✔
61
                        if (id_card || passport) {
34✔
62
                                return false;
×
63
                        }
64
                        kind = "P"sv;
34✔
65
                        document = std::move(*social_id);
100✔
66
                }
66✔
67

66✔
68
                if (id_card) {
34✔
69
                        if (social_id || passport) {
70
                                return false;
×
71
                        }
72
                        kind = "1"sv;
73
                        document = std::move(*id_card);
×
74
                }
75

66✔
76
                if (passport) {
34✔
77
                        if (social_id || id_card) {
78
                                return false;
×
79
                        }
80
                        kind = "2"sv;
81
                        document = std::move(*passport);
×
82
                }
83

66✔
84
                if (kind.empty() || document.empty()) return false;
34✔
85
                return true;
34✔
86
        }
67✔
87

88
        std::optional<templates> templates::parse_yaml(
33✔
89
            std::filesystem::path const& path) {
1✔
90
                return parser::parse_yaml_file<templates>(path, app_name);
100✔
91
        }
67✔
92

93
        bool templates::validate() noexcept {
94
                for (auto& [kedu, report] : reports) {
×
95
                        if (kedu.empty()) return false;
96
                        for (auto& section : report) {
97
                                if (!section.validate()) return false;
98
                        }
99
                }
100
                return true;
101
        }
102

103
        bool report_section::validate() noexcept {
104
                if (id.empty()) return false;
105

106
                struct validator {
107
                        bool operator()(std::string const& str) const noexcept {
108
                                return !str.empty();
×
109
                        }
110
                        bool operator()(
111
                            std::vector<std::string> const& vec) const noexcept {
112
                                if (vec.empty()) return false;
113
                                for (auto const& str : vec) {
114
                                        if (str.empty()) return false;
115
                                }
116
                                return true;
117
                        }
118
                } value{};
119

120
                for (auto const& [_, field] : fields) {
121
                        if (!std::visit(value, field)) return false;
122
                }
123
                return true;
124
        }
125

126
        std::optional<tax_config> tax_config::parse_yaml(
33✔
127
            std::filesystem::path const& path) {
1✔
128
                return parser::parse_yaml_file<tax_config>(path, app_name);
100✔
129
        }
67✔
130

131
        std::optional<tax_config> tax_config::parse_from_text(
33✔
132
            std::string const& text,
133
            std::string const& path) {
1✔
134
                return parser::parse_yaml_text<tax_config>(text, path);
100✔
135
        }
67✔
136

137
        bool tax_config::postprocess() { return version == kVersion; }
67✔
138

139
        void tax_config::merge(tax_config&& newer) {
67✔
140
                scale.merge(std::move(newer.scale));
199✔
141
                minimal_pay.merge(std::move(newer.minimal_pay));
199✔
142
                costs_of_obtaining.merge(std::move(newer.costs_of_obtaining));
199✔
143
                contributions.merge(std::move(newer.contributions));
199✔
144
        }
199✔
145
}  // namespace quick_dra::v1
146

147
namespace quick_dra::v1::partial {
148
        load_status config::load(std::filesystem::path const& path) {
277✔
149
                std::error_code ec{};
829✔
150
                if (!std::filesystem::exists(path, ec) || ec) {
277✔
151
                        return load_status::file_not_found;
262✔
152
                }
174✔
153

378✔
154
                auto result = load_status::errors_encountered;
190✔
155

378✔
156
                auto object = parser::parse_yaml_file<config>(
442✔
157
                    path, [&]() { result = load_status::file_not_readable; });
190✔
158
                if (!object) {
190✔
159
                        return result;
×
160
                }
161

378✔
162
                *this = std::move(*object);
190✔
163
                return load_status::loaded;
568✔
164
        }
568✔
165

166
        config config::load_partial(std::filesystem::path const& path,
276✔
167
                                    bool writeable) {
1✔
168
                partial::config cfg{};
829✔
169
                auto const load = cfg.load(path);
829✔
170
                switch (load) {
829✔
171
                        case load_status::file_not_found:
87✔
172
                                if (writeable) {
88✔
173
                                        fmt::print(
309✔
174
                                            stderr,
168✔
175
                                            "Quick-DRA: file {} will be created as needed.\n",
168✔
176
                                            path);
168✔
177
                                }
168✔
178
                                break;
262✔
179
                        case load_status::file_not_readable:
180
                                fmt::print(stderr, "Quick-DRA: error: could not read {}\n",
×
181
                                           path);
182
                                std::exit(1);
×
183
                        case load_status::errors_encountered:
184
                                fmt::print(stderr, "Quick-DRA: error: {} needs to be updated\n",
×
185
                                           path);
186
                                std::exit(1);
×
187
                        default:
189✔
188
                                break;
567✔
189
                }
552✔
190
                return cfg;
277✔
191
        }
553✔
192

193
        bool config::store(std::filesystem::path const& path) {
214✔
194
                prepare_for_write();
640✔
195
                ryml::Tree tree{};
640✔
196
                auto ref = tree.rootref();
640✔
197
                yaml::write_value(ref, *this);
640✔
198

426✔
199
                ryml::csubstr output = ryml::emit_yaml(
853✔
200
                    tree, tree.root_id(), ryml::substr{}, /*error_on_excess*/ false);
781✔
201

426✔
202
                std::vector<char> buf(output.len);
782✔
203
                output = ryml::emit_yaml(tree, tree.root_id(), ryml::to_substr(buf),
711✔
204
                                         /*error_on_excess*/ true);
781✔
205

426✔
206
                std::ofstream out{path, std::ios::out | std::ios::binary};
640✔
207
                if (!out) {
214✔
208
                        return false;
37✔
209
                }
24✔
210

402✔
211
                out.write(output.data(), static_cast<std::streamsize>(output.size()));
470✔
212
                return true;
604✔
213
        }
640✔
214

215
        bool config::postprocess() {
190✔
216
                if (version && *version != kVersion) version = std::nullopt;
190✔
217
                if (!insured) insured.emplace();
190✔
218
                return true;
568✔
219
        }
379✔
220

221
        void config::preprocess() {
214✔
222
                if (!version) version = static_cast<unsigned short>(kVersion);
214✔
223
                if (!insured) insured.emplace();
214✔
224
        }
640✔
225

226
        template <typename Named>
227
        void parse_name(Named& named) noexcept {
352✔
228
                if (!named.last_name) return;
586✔
229

702✔
230
                auto const name = split_s(*named.last_name, ", "_sep, 1);
352✔
231
                if (name.size() == 2) {
352✔
232
                        named.first_name.emplace(strip_sv(name[1]));
1,054✔
233
                        named.last_name.emplace(strip_sv(name[0]));
1,054✔
234
                        if (!(named.last_name->empty() || named.first_name->empty())) {
352✔
235
                                return;
1,054✔
236
                        }
702✔
237
                }
702✔
238

239
                named.first_name = std::nullopt;
240
                named.last_name = std::nullopt;
×
241
        }
351✔
242

243
        template <typename Named>
244
        void preprocess_name(Named& named) noexcept {
382✔
245
                if (!named.last_name || !named.first_name) {
382✔
246
                        named.last_name = std::nullopt;
×
247
                        return;
×
248
                }
249

762✔
250
                named.last_name =
890✔
251
                    fmt::format("{}, {}", *named.last_name, *named.first_name);
1,270✔
252
                named.first_name = std::nullopt;
1,144✔
253
        }
763✔
254

255
        void payer_t::postprocess_document_kind() noexcept {
37✔
256
                kind = std::nullopt;
109✔
257
                document = std::nullopt;
109✔
258

72✔
259
                if (id_card) {
37✔
260
                        kind = "1"sv;
64✔
261
                        document = std::move(*id_card);
64✔
262
                } else if (passport) {
58✔
263
                        kind = "2"sv;
37✔
264
                        document = std::move(*passport);
37✔
265
                }
24✔
266
        }
109✔
267

268
        bool payer_t::postprocess() {
10✔
269
                postprocess_document_kind();
28✔
270

18✔
271
                parse_name(*this);
28✔
272

18✔
273
#define NULLIFY(STR)           \
18✔
274
        if (STR && STR->empty()) { \
275
                STR.reset();           \
684✔
276
        }
684✔
277

18✔
278
                NULLIFY(social_id);
10✔
279
                NULLIFY(tax_id);
10✔
280
                NULLIFY(kind);
10✔
281
                NULLIFY(document);
10✔
282
                return true;
28✔
283
        }
19✔
284

285
        void payer_t::preprocess() {
13✔
286
                preprocess_name(*this);
37✔
287
                preprocess_document_kind();
37✔
288
        }
37✔
289

290
        void payer_t::preprocess_document_kind() noexcept {
43✔
291
                auto kind_ = kind.value_or(""s);
127✔
292
                auto document_ = document.value_or(""s);
127✔
293

84✔
294
                id_card = std::nullopt;
127✔
295
                passport = std::nullopt;
127✔
296
                kind = std::nullopt;
127✔
297
                document = std::nullopt;
127✔
298

84✔
299
                if (kind_ == "1"sv) {
43✔
300
                        id_card = std::move(document_);
55✔
301
                } else if (kind_ == "2"sv) {
73✔
302
                        passport = std::move(document_);
64✔
303
                }
42✔
304
        }
127✔
305

306
        void insured_t::postprocess_document_kind() noexcept {
730✔
307
                kind = std::nullopt;
2,188✔
308
                document = std::nullopt;
2,188✔
309

1,458✔
310
                if (id_card) {
730✔
311
                        kind = "1"sv;
982✔
312
                        document = std::move(*id_card);
982✔
313
                } else if (passport) {
1,207✔
314
                        kind = "2"sv;
694✔
315
                        document = std::move(*passport);
694✔
316
                } else if (social_id) {
634✔
317
                        kind = "P"sv;
514✔
318
                        document = std::move(*social_id);
514✔
319
                }
342✔
320
        }
2,188✔
321

322
        bool insured_t::postprocess() {
343✔
323
                postprocess_document_kind();
1,027✔
324
                parse_name(*this);
1,027✔
325

684✔
326
                NULLIFY(id_card);
343✔
327
                NULLIFY(passport);
343✔
328
                NULLIFY(social_id);
343✔
329
                NULLIFY(kind);
343✔
330
                NULLIFY(document);
343✔
331

684✔
332
                return true;
1,027✔
333
        }
685✔
334

335
        void insured_t::preprocess() {
370✔
336
                preprocess_name(*this);
1,108✔
337
                preprocess_document_kind();
1,108✔
338
        }
1,108✔
339

340
        void insured_t::preprocess_document_kind() noexcept {
760✔
341
                auto kind_ = kind.value_or(""s);
2,278✔
342
                auto document_ = document.value_or(""s);
2,278✔
343

1,518✔
344
                id_card = std::nullopt;
2,278✔
345
                passport = std::nullopt;
2,278✔
346
                social_id = std::nullopt;
2,278✔
347
                kind = std::nullopt;
2,278✔
348
                document = std::nullopt;
2,278✔
349

1,518✔
350
                if (kind_ == "1"sv) {
760✔
351
                        id_card = std::move(document_);
1,036✔
352
                } else if (kind_ == "2"sv) {
1,243✔
353
                        passport = std::move(document_);
730✔
354
                } else if (kind_ == "P"sv) {
658✔
355
                        social_id = std::move(document_);
514✔
356
                }
342✔
357
        }
2,278✔
358
}  // namespace quick_dra::v1::partial
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