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

mbits-os / quick_dra / 22303029837

23 Feb 2026 10:52AM UTC coverage: 98.665% (+0.006%) from 98.659%
22303029837

Pull #43

github

web-flow
Merge 7b192c135 into c23350102
Pull Request #43: test: fully cover models library

33 of 33 new or added lines in 5 files covered. (100.0%)

10 existing lines in 4 files now uncovered.

3179 of 3222 relevant lines covered (98.67%)

0.99 hits per line

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

90.52
/libs/libconv/src/low_level.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/ranges.h>
6
#include <iostream>
7
#include <quick_dra/conv/low_level.hpp>
8
#include <string>
9
#include <utility>
10

11
namespace quick_dra {
12
        using std::literals::operator""s;
13
        using std::literals::operator""sv;
14

15
        void comment(std::string_view msg) {
1✔
16
                fmt::print("\033[0;90m - {}\033[m\n", msg);
1✔
17
        }
1✔
18

19
        bool get_answer(std::string_view label,
20
                        std::string_view hint,
21
                        std::function<bool(std::string&&)> const& answer_is_valid) {
1✔
22
                auto const hint_brackets =
1✔
23
                    hint.empty() ? ""s : fmt::format("\033[0;90m [{}]", hint);
24

25
                while (true) {
1✔
26
                        std::string answer;
1✔
27
                        fmt::print("\033[0;36m{}{}\033[m> ", label, hint_brackets);
1✔
28
                        if (!std::getline(std::cin, answer)) {
1✔
29
                                return false;
1✔
30
                        }
31
                        if (answer_is_valid(std::move(answer))) {
1✔
32
                                return true;
1✔
33
                        }
34
                }
1✔
UNCOV
35
        }
×
36

37
        bool get_yes_no(std::string_view label, bool hint, bool& dst) {
1✔
38
                auto const handler = [&, hint](std::string&& input) -> bool {
1✔
39
                        if (input.empty()) {
1✔
40
                                dst = hint;
×
41
                                return true;
×
42
                        }
43
                        if (input == "y"sv || input == "Y"sv) {
1✔
44
                                dst = true;
1✔
45
                                return true;
1✔
46
                        }
47
                        if (input == "n"sv || input == "N"sv) {
1✔
48
                                dst = false;
1✔
49
                                return true;
1✔
50
                        }
51
                        return false;
1✔
52
                };
1✔
53

54
                return get_answer(label, hint ? "Y/n"sv : "y/N"sv, handler);
1✔
55
        }
1✔
56

57
        bool get_enum_answer(
58
            std::string_view label,
59
            std::span<std::pair<char, std::string_view> const> const& items,
60
            std::function<void(char)> const& store_enum,
61
            char selected) {
1✔
62
                std::string hint{};
1✔
63
                hint.reserve([&items]() {
1✔
64
                        size_t hint_size = items.size() * 4 + (items.size() - 1) * 2;
1✔
65
                        for (auto const& [_, description] : items) {
1✔
66
                                hint_size += description.size();
1✔
67
                        }
68
                        return hint_size;
1✔
69
                }() + static_cast<size_t>(selected ? 2u : 0u));
1✔
70

71
                for (auto const& [value, description] : items) {
1✔
72
                        if (!hint.empty()) hint.append(", "sv);
1✔
73
                        if (value == selected) hint.push_back('[');
1✔
74
                        hint.push_back(value);
1✔
75
                        if (value == selected) hint.push_back(']');
1✔
76
                        hint.append(" - "sv);
1✔
77
                        hint.append(description);
1✔
78
                }
1✔
79

80
                return get_answer(label, hint, [&, selected](std::string&& answer) {
1✔
81
                        if (selected && answer.empty()) {
1✔
82
                                store_enum(selected);
×
83
                                return true;
×
84
                        }
85

86
                        if (answer.size() != 1) {
1✔
87
                                return false;
×
88
                        }
89

90
                        for (auto const& [value, _] : items) {
1✔
91
                                if (value == answer.front()) {
1✔
92
                                        store_enum(value);
1✔
93
                                        return true;
1✔
94
                                }
95
                        }
96

UNCOV
97
                        return false;
×
98
                });
1✔
99
        }
1✔
100

101
        std::string as_string(insurance_title const& value) {
1✔
102
                return fmt::to_string(fmt::join(value.split(), " "));
1✔
103
        }
1✔
104

105
        std::string as_string(ratio const& value) {
1✔
106
                return fmt::format("{}/{}", value.num, value.den);
1✔
107
        }
1✔
108

109
        std::string as_string(currency const& value) {
1✔
110
                if (value == minimal_salary) {
1✔
111
                        return "minimal for a given month"s;
1✔
112
                }
113
                return fmt::format("{} zł", value);
1✔
114
        }
1✔
115

116
        std::optional<std::string> as_string(std::optional<currency> const& value) {
1✔
117
                return value
1✔
118
                    .transform([](auto const& value) { return as_string(value); })
1✔
119
                    .value_or("none"s);
120
        }
1✔
121

122
        template <typename Arg>
123
        bool get_field_answer_impl(
124
            bool ask_questions,
125
            std::string_view label,
126
            std::optional<Arg>& dst,
127
            std::optional<Arg>&& opt,
128
            std::function<bool(std::string&&, std::optional<Arg>&, bool)> const&
129
                validator) {
1✔
130
                if (opt) {
1✔
131
                        dst = std::move(opt);
1✔
132
                }
133

134
                if (!ask_questions) {
1✔
135
                        if (dst) {
1✔
136
                                auto copy = *dst;
1✔
137
                                auto const is_valid =
1✔
138
                                    validator(as_string(std::move(copy)), dst, false);
139
                                if (!is_valid) {
1✔
140
                                        comment("Cannot save invalid data with -y. Stopping.");
1✔
141
                                        return false;
1✔
142
                                }
143
                        }
1✔
144
                        if constexpr (std::same_as<Arg, currency>) {
145
                                if (!dst) {
1✔
146
                                        auto const is_valid = validator("none"s, dst, false);
×
UNCOV
147
                                        if (!is_valid) {
×
148
                                                comment("Cannot save invalid data with -y. Stopping.");
×
149
                                                return false;
×
150
                                        }
151
                                }
152
                        }
153
                        return true;
1✔
154
                }
155

156
                auto def_value = as_string(dst);
1✔
157
                return get_answer(label, def_value.value_or(""s),
1✔
158
                                  [&](std::string&& answer) {
1✔
159
                                          if (answer.empty() && def_value) {
1✔
160
                                                  answer = *def_value;
1✔
161
                                          }
162
                                          return validator(std::move(answer), dst, true);
1✔
163
                                  });
1✔
164
        }
1✔
165

166
        bool get_field_answer(bool ask_questions,
167
                              std::string_view label,
168
                              std::optional<std::string>& dst,
169
                              std::optional<std::string>&& opt,
170
                              std::function<bool(std::string&&,
171
                                                 std::optional<std::string>&,
172
                                                 bool)> const& validator) {
1✔
173
                return get_field_answer_impl(ask_questions, label, dst, std::move(opt),
1✔
174
                                             validator);
175
        }
1✔
176

177
        bool get_field_answer(bool ask_questions,
178
                              std::string_view label,
179
                              std::optional<insurance_title>& dst,
180
                              std::optional<insurance_title>&& opt,
181
                              std::function<bool(std::string&&,
182
                                                 std::optional<insurance_title>&,
183
                                                 bool)> const& validator) {
1✔
184
                return get_field_answer_impl(ask_questions, label, dst, std::move(opt),
1✔
185
                                             validator);
186
        }
1✔
187

188
        static constexpr auto magic_currency = "minimal"sv;
189
        bool get_field_answer(bool ask_questions,
190
                              std::string_view label,
191
                              std::optional<currency>& dst,
192
                              std::optional<currency>&& opt,
193
                              std::function<bool(std::string&&,
194
                                                 std::optional<currency>&,
195
                                                 bool)> const& validator) {
1✔
196
                std::function<bool(std::string&&, std::optional<currency>&, bool)> const
197
                    wrapped = [validator](std::string&& in,
1✔
198
                                          std::optional<currency>& out,
199
                                          bool ask_questions) {
1✔
200
                            if (in.starts_with(magic_currency)) {
1✔
201
                                    in = magic_currency;
1✔
202
                            }
203
                            return validator(std::move(in), out, ask_questions);
1✔
204
                    };
1✔
205
                return get_field_answer_impl(ask_questions, label, dst, std::move(opt),
1✔
206
                                             wrapped);
207
        }
1✔
208

209
        bool get_field_answer(
210
            bool ask_questions,
211
            std::string_view label,
212
            std::optional<ratio>& dst,
213
            std::optional<ratio>&& opt,
214
            std::function<bool(std::string&&, std::optional<ratio>&, bool)> const&
215
                validator) {
1✔
216
                return get_field_answer_impl(ask_questions, label, dst, std::move(opt),
1✔
217
                                             validator);
218
        }
1✔
219
}  // 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