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

MikkelSchubert / adapterremoval / #78

03 Apr 2025 03:38PM UTC coverage: 27.703% (-0.08%) from 27.778%
#78

push

travis-ci

web-flow
improve ccache hit rates (#101)

There is generally a high amount of churn in ccache files, due to the (currently) high level of interdependence between files, and cached files are therefore not expected have be relevant for a long time. The maximum size of the cache is therefore reduced to 100 MB, compared to the default maximum of 500 MB, in order to reduce the amount of time spent on (re)storing the cache.

Additionally, some tweaks were made to the build process, to ensure that building was separated from installing and testing, and `ubuntu-22.04` was replaced with `ubuntu-24.04` in order to (hopefully) fix an issue were unit test compilation was not ccache'd

2701 of 9750 relevant lines covered (27.7%)

4134.79 hits per line

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

91.88
/src/argparse.cpp
1
// SPDX-License-Identifier: GPL-3.0-or-later
2
// SPDX-FileCopyrightText: 2011 Stinus Lindgreen <stinus@binf.ku.dk>
3
// SPDX-FileCopyrightText: 2014 Mikkel Schubert <mikkelsch@gmail.com>
4
#include "argparse.hpp"
5
#include "debug.hpp"    // for AR_REQUIRE
6
#include "logging.hpp"  // for log_stream, error, cerr, warn
7
#include "strutils.hpp" // for string_vec, shell_escape, to_lower
8
#include <algorithm>    // for max, copy, find, min, sort
9
#include <limits>       // for numeric_limits
10
#include <memory>       // for __shared_ptr_access, unique_ptr, share...
11
#include <sstream>      // for operator<<, basic_ostream, ostringstream
12
#include <stdexcept>    // for invalid_argument
13
#include <utility>      // for pair
14

15
namespace adapterremoval {
16

17
namespace argparse {
18

19
namespace {
20

21
const size_t parsing_failed = static_cast<size_t>(-1);
22
const size_t invalid_choice = static_cast<size_t>(-2);
23

24
/** Detect similar arguments based on prefixes or max edit distance. */
25
bool
26
is_similar_argument(const std::string& user,
31✔
27
                    const std::string& ref,
28
                    size_t max_distance)
29
{
30
  const auto overlap = std::min(user.size(), ref.size());
93✔
31
  if (overlap == user.size() && user == ref.substr(0, overlap)) {
83✔
32
    return true;
33
  }
34

35
  const auto diff = std::max(user.size(), ref.size()) - overlap;
75✔
36

37
  return (diff <= max_distance && levenshtein(user, ref) <= max_distance);
25✔
38
}
39

40
} // namespace
41

42
///////////////////////////////////////////////////////////////////////////////
43

44
parser::parser()
90✔
45
{
46
  add_header("OPTIONS:");
60✔
47

48
  // Built-in arguments
49
  add("--help").abbreviation('h').help("Display this message");
120✔
50
  add("--version").abbreviation('v').help("Print the version string");
120✔
51
  add("--licenses").help("Print licenses for this software");
120✔
52
  add_separator();
30✔
53
}
30✔
54

55
void
56
parser::set_name(const std::string& name)
13✔
57
{
58
  m_name = name;
13✔
59
}
13✔
60

61
void
62
parser::set_version(const std::string& version)
12✔
63
{
64
  m_version = version;
12✔
65
}
12✔
66

67
void
68
parser::set_preamble(const std::string& text)
6✔
69
{
70
  m_preamble = text;
6✔
71
}
6✔
72

73
void
74
parser::set_licenses(const std::string& text)
1✔
75
{
76
  m_licenses = text;
1✔
77
}
1✔
78

79
parse_result
80
parser::parse_args(const string_vec& args)
21✔
81
{
82
  AR_REQUIRE(!args.empty());
21✔
83
  update_argument_map();
21✔
84

85
  for (auto it = args.begin() + 1; it != args.end();) {
97✔
86
    const auto argument = find_argument(*it);
25✔
87
    if (argument) {
25✔
88
      const size_t consumed = argument->parse(it, args.end());
57✔
89

90
      if (consumed == parsing_failed) {
19✔
91
        return parse_result::error;
92
      }
93

94
      it += static_cast<string_vec::iterator::difference_type>(consumed);
17✔
95
      AR_REQUIRE(it <= args.end());
34✔
96
    } else {
97
      return parse_result::error;
98
    }
99
  }
25✔
100

101
  parse_result result = parse_result::ok;
13✔
102
  for (const auto& arg : m_args) {
132✔
103
    if (arg.argument && arg.argument->is_set()) {
294✔
104
      const auto& key = arg.argument->key();
51✔
105

106
      for (const auto& requirement : arg.argument->depends_on()) {
87✔
107
        if (!is_set(requirement)) {
2✔
108
          result = parse_result::error;
1✔
109
          log::error() << "Option " << requirement << " is required when "
5✔
110
                       << "using option " << key;
3✔
111
        }
112
      }
113

114
      for (const auto& prohibited : arg.argument->conflicts_with()) {
87✔
115
        if (is_set(prohibited)) {
2✔
116
          result = parse_result::error;
1✔
117
          log::error() << "Option " << prohibited
4✔
118
                       << " cannot be used together with option " << key;
3✔
119
        }
120
      }
121
    }
122
  }
123

124
  if (is_set("--help")) {
26✔
125
    print_help();
4✔
126
    return parse_result::exit;
4✔
127
  } else if (is_set("--version")) {
18✔
128
    print_version();
2✔
129
    return parse_result::exit;
2✔
130
  } else if (is_set("--licenses")) {
14✔
131
    print_licenses();
1✔
132
    return parse_result::exit;
1✔
133
  }
134

135
  return result;
136
}
137

138
bool
139
parser::is_set(const std::string& key) const
36✔
140
{
141
  const auto it = m_keys.find(key);
36✔
142
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
72✔
143

144
  return it->second->is_set();
108✔
145
}
146

147
std::string
148
parser::value(const std::string& key) const
2✔
149
{
150
  const auto it = m_keys.find(key);
2✔
151
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
4✔
152

153
  return it->second->value();
6✔
154
}
155

156
argument&
157
parser::add(const std::string& name, const std::string& metavar)
122✔
158
{
159
  AR_REQUIRE(starts_with(name, "--"), name);
122✔
160
  auto ptr = std::make_shared<argument>(name, metavar);
122✔
161
  m_args.push_back({ std::string(), ptr });
488✔
162

163
  return *ptr;
244✔
164
}
366✔
165

166
void
167
parser::add_separator()
60✔
168
{
169
  m_args.push_back({ std::string(), argument_ptr() });
240✔
170
}
120✔
171

172
void
173
parser::add_header(const std::string& header)
30✔
174
{
175
  add_separator();
30✔
176
  m_args.push_back({ header, argument_ptr() });
120✔
177
}
60✔
178

179
void
180
parser::print_version() const
14✔
181
{
182
  log::cerr() << m_name << " " << m_version << "\n";
84✔
183
}
14✔
184

185
void
186
parser::print_help() const
12✔
187
{
188
  print_version();
12✔
189

190
  auto cerr = log::cerr();
12✔
191
  if (!m_preamble.empty()) {
24✔
192
    cli_formatter fmt;
6✔
193
    fmt.set_indent(0);
6✔
194
    fmt.set_indent_first_line(false);
6✔
195
    fmt.set_column_width(m_terminal_width);
6✔
196

197
    cerr << "\n";
6✔
198
    for (auto line : split_lines(m_preamble)) {
24✔
199
      if (starts_with(line, " ")) {
6✔
200
        cerr << line << "\n";
×
201
      } else {
202
        cerr << fmt.format(line) << "\n";
24✔
203
      }
204
    }
12✔
205
  }
206

207
  string_vec signatures;
12✔
208

209
  size_t indentation = 0;
12✔
210
  for (const auto& entry : m_args) {
118✔
211
    if (entry.argument && !entry.argument->is_hidden()) {
256✔
212
      const auto& arg = *entry.argument;
44✔
213

214
      std::ostringstream ss;
44✔
215
      if (arg.short_key().empty()) {
132✔
216
        ss << "   " << arg.key();
40✔
217
      } else {
218
        ss << "   " << arg.short_key() << ", " << arg.key();
72✔
219
      }
220

221
      for (size_t i = 0; i < arg.min_values(); ++i) {
50✔
222
        ss << " <" << arg.metavar() << ">";
12✔
223
      }
224

225
      if (arg.max_values() == std::numeric_limits<size_t>::max()) {
44✔
226
        ss << " [" << arg.metavar() << ", ...]";
4✔
227
      } else {
228
        for (size_t i = arg.min_values(); i < arg.max_values(); ++i) {
45✔
229
          ss << " [" << arg.metavar() << "]";
6✔
230
        }
231
      }
232

233
      indentation = std::max<size_t>(indentation, ss.str().size() + 3);
132✔
234
      signatures.emplace_back(ss.str());
88✔
235
    } else {
44✔
236
      signatures.emplace_back();
38✔
237
    }
238
  }
239

240
  cli_formatter fmt;
12✔
241
  fmt.set_indent(indentation);
12✔
242
  fmt.set_indent_first_line(false);
12✔
243
  fmt.set_column_width(m_terminal_width - indentation);
12✔
244

245
  for (size_t i = 0; i < m_args.size(); i++) {
94✔
246
    const auto& entry = m_args.at(i);
82✔
247
    if (entry.argument) {
164✔
248
      if (!entry.argument->is_hidden()) {
138✔
249
        const auto& arg = *entry.argument;
44✔
250
        const auto& signature = signatures.at(i);
44✔
251

252
        cerr << signature;
44✔
253

254
        const std::string help = arg.help();
44✔
255
        if (!help.empty()) {
88✔
256
          // Format into columns and indent lines (except the first line)
257
          cerr << std::string(indentation - signature.length(), ' ')
126✔
258
               << fmt.format(help);
126✔
259
        }
260

261
        cerr << "\n";
88✔
262
      }
44✔
263
    } else {
264
      cerr << entry.header << "\n";
108✔
265
    }
266
  }
267
}
24✔
268

269
void
270
parser::print_licenses() const
1✔
271
{
272
  auto cerr = log::cerr();
1✔
273

274
  for (const auto& block : split_lines(m_licenses)) {
6✔
275
    if (block.empty()) {
6✔
276
      cerr << "\n";
×
277
    } else {
278
      size_t indentation = block.find_first_not_of(' ');
3✔
279
      if (indentation == std::string::npos) {
3✔
280
        indentation = 0;
×
281
      }
282

283
      size_t ljust = 0;
3✔
284
      if (!block.empty() && block.at(indentation) == '*') {
6✔
285
        ljust = 2;
286
      } else if (block.size() > indentation + 1 &&
6✔
287
                 block.at(indentation + 1) == '.') {
3✔
288
        ljust = 3;
289
      }
290

291
      const auto width = 80 - indentation;
3✔
292
      for (const auto& line : wrap_text(block, width, ljust)) {
12✔
293
        cerr << std::string(indentation, ' ') << line << "\n";
15✔
294
      }
3✔
295
    }
296
  }
1✔
297
}
2✔
298

299
void
300
parser::set_terminal_width(unsigned w)
6✔
301
{
302
  m_terminal_width = w;
6✔
303
}
6✔
304

305
void
306
parser::update_argument_map()
21✔
307
{
308
  m_keys.clear();
21✔
309

310
  for (auto& it : m_args) {
212✔
311
    if (it.argument) {
298✔
312
      for (const auto& key : it.argument->keys()) {
472✔
313
        const auto result = m_keys.emplace(key, it.argument);
128✔
314
        AR_REQUIRE(result.second, shell_escape(key));
128✔
315
      }
86✔
316
    }
317
  }
318

319
  bool any_errors = false;
21✔
320
  for (const auto& it : m_args) {
212✔
321
    if (it.argument) {
298✔
322
      for (const auto& key : it.argument->conflicts_with()) {
432✔
323
        if (!m_keys.count(key)) {
2✔
324
          any_errors = true;
×
325
          log::error() << it.argument->key() << " conflicts with "
×
326
                       << "unknown command-line option " << key;
×
327
        }
328
      }
329

330
      for (const auto& key : it.argument->depends_on()) {
432✔
331
        if (!m_keys.count(key)) {
2✔
332
          any_errors = true;
×
333
          log::error() << it.argument->key() << " requires "
×
334
                       << "unknown command-line option " << key;
×
335
        }
336
      }
337
    }
338
  }
339

340
  AR_REQUIRE(!any_errors, "bugs in argument parsing");
21✔
341
}
21✔
342

343
argument_ptr
344
parser::find_argument(const std::string& key)
25✔
345
{
346
  auto it = m_keys.find(key);
25✔
347
  if (it != m_keys.end()) {
50✔
348
    return it->second;
38✔
349
  }
350

351
  if (key.size() > 1 && key.front() == '-' && key.back() != '-') {
12✔
352
    string_vec candidates;
5✔
353
    const size_t max_distance = 1 + key.size() / 4;
5✔
354

355
    for (const auto& arg : m_args) {
51✔
356
      if (arg.argument) {
72✔
357
        for (const auto& name : arg.argument->keys()) {
115✔
358

359
          if (is_similar_argument(key, name, max_distance)) {
31✔
360
            candidates.push_back(name);
7✔
361
          }
362
        }
21✔
363
      }
364
    }
365

366
    auto error = log::error();
5✔
367
    error << "Unknown argument '" << key << "'";
15✔
368

369
    if (!candidates.empty()) {
5✔
370
      std::sort(candidates.begin(), candidates.end());
12✔
371

372
      error << ". Did you mean " << candidates.front();
8✔
373
      for (size_t i = 1; i < candidates.size() - 1; ++i) {
5✔
374
        error << ", " << candidates.at(i);
3✔
375
      }
376

377
      if (candidates.size() > 1) {
4✔
378
        error << " or " << candidates.back();
6✔
379
      }
380

381
      error << "?";
8✔
382
    }
383
  } else {
5✔
384
    log::error() << "Unexpected positional argument '" << key << "'";
5✔
385
  }
386

387
  return {};
12✔
388
}
389

390
///////////////////////////////////////////////////////////////////////////////
391

392
argument::argument(const std::string& key, std::string metavar)
148✔
393
  : m_key_long(key)
148✔
394
  , m_metavar(std::move(metavar))
148✔
395
  , m_sink(std::make_unique<bool_sink>(&m_default_sink))
888✔
396
{
397
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
296✔
398
}
148✔
399

400
argument&
401
argument::help(const std::string& text)
103✔
402
{
403
  // Enforce this the style of help text
404
  AR_REQUIRE(!ends_with(text, "."),
108✔
405
             "Help text ends with dot: " + log_escape(text));
406
  m_help = text;
102✔
407

408
  return *this;
102✔
409
}
410

411
string_vec
412
argument::keys() const
110✔
413
{
414
  string_vec keys = m_deprecated_keys;
110✔
415
  keys.push_back(m_key_long);
110✔
416

417
  if (!m_key_short.empty()) {
220✔
418
    keys.push_back(m_key_short);
53✔
419
  }
420

421
  return keys;
110✔
422
}
×
423

424
std::string
425
argument::help() const
51✔
426
{
427
  // Append string representation of current (default) value
428
  std::ostringstream ss(m_help);
51✔
429
  ss << m_help;
51✔
430

431
  const auto choices = m_sink->choices();
102✔
432
  if (!choices.empty()) {
51✔
433
    ss << ". Possible values are " << join_text(choices, ", ", ", and ");
6✔
434
  }
435

436
  if (m_sink->has_default() && m_help.find("[default:") == std::string::npos) {
102✔
437
    ss << " [default: " << default_value() << "]";
4✔
438
  }
439

440
  return ss.str();
102✔
441
}
51✔
442

443
size_t
444
argument::min_values() const
92✔
445
{
446
  return m_sink->min_values();
184✔
447
}
448

449
size_t
450
argument::max_values() const
89✔
451
{
452
  return m_sink->max_values();
178✔
453
}
454

455
std::string
456
argument::value() const
3✔
457
{
458
  return m_sink->value();
6✔
459
}
460

461
std::string
462
argument::default_value() const
2✔
463
{
464
  return m_sink->default_value();
4✔
465
}
466

467
template<typename A, typename B>
468
A&
469
bind(std::unique_ptr<sink>& ptr, B* sink)
23✔
470
{
471
  ptr = std::make_unique<A>(sink);
23✔
472

473
  return static_cast<A&>(*ptr);
23✔
474
}
475

5✔
476
bool_sink&
477
argument::bind_bool(bool* ptr)
5✔
478
{
479
  return bind<bool_sink>(m_sink, ptr);
5✔
480
}
481

5✔
482
u32_sink&
483
argument::bind_u32(unsigned* ptr)
5✔
484
{
485
  return bind<u32_sink>(m_sink, ptr);
5✔
486
}
487

2✔
488
double_sink&
489
argument::bind_double(double* ptr)
2✔
490
{
491
  return bind<double_sink>(m_sink, ptr);
2✔
492
}
493

10✔
494
str_sink&
495
argument::bind_str(std::string* ptr)
10✔
496
{
497
  return bind<str_sink>(m_sink, ptr);
10✔
498
}
499

1✔
500
vec_sink&
501
argument::bind_vec(string_vec* ptr)
1✔
502
{
503
  return bind<vec_sink>(m_sink, ptr);
1✔
504
}
505

506
argument&
507
argument::abbreviation(char key)
1✔
508
{
509
  // To avoid ambiguity only lower-case alphabetical arguments are allowed
1✔
510
  AR_REQUIRE(key >= 'a' && key <= 'z', std::string{ key });
511
  m_key_short.clear();
512
  m_key_short.push_back('-');
513
  m_key_short.push_back(key);
10✔
514

515
  return *this;
10✔
516
}
517

518
argument&
519
argument::deprecated_alias(const std::string& key)
2✔
520
{
521
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
2✔
522
  m_deprecated_keys.emplace_back(key);
523

524
  return *this;
525
}
5✔
526

527
argument&
5✔
528
argument::deprecated()
529
{
530
  m_deprecated = true;
531

5✔
532
  return hidden();
533
}
5✔
534

535
argument&
536
argument::hidden()
537
{
61✔
538
  m_hidden = true;
539

540
  return *this;
61✔
541
}
61✔
542

61✔
543
argument&
61✔
544
argument::depends_on(const std::string& key)
545
{
61✔
546
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
547
  m_depends_on.push_back(key);
548

549
  return *this;
3✔
550
}
551

6✔
552
argument&
3✔
553
argument::conflicts_with(const std::string& key)
554
{
3✔
555
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
556
  m_conflicts_with.push_back(key);
557

558
  return *this;
2✔
559
}
560

2✔
561
void
562
n_args_error(const std::string& key,
2✔
563
             size_t limit,
564
             const char* relation,
565
             size_t n)
566
{
4✔
567
  auto out = log::error();
568

4✔
569
  out << "Command-line argument " << key << " takes" << relation << " " << limit
570
      << " value";
4✔
571

572
  if (limit != 1) {
573
    out << "s";
574
  }
4✔
575

576
  if (n == 1) {
8✔
577
    out << ", but 1 value was provided!";
4✔
578
  } else {
579
    out << ", but " << n << " values were provided!";
4✔
580
  }
581
}
582

583
size_t
4✔
584
argument::parse(string_vec_citer start, const string_vec_citer& end)
585
{
8✔
586
  AR_REQUIRE(start != end);
4✔
587

588
  const bool deprecated_alias = is_deprecated_alias(*start);
4✔
589
  AR_REQUIRE(deprecated_alias || *start == m_key_long ||
590
             (!m_key_short.empty() && *start == m_key_short));
591

592
  if (m_deprecated) {
1✔
593
    log::warn() << "Option " << *start << " is deprecated and will "
594
                << "be removed in the future.";
595
  } else if (deprecated_alias) {
596
    log::warn() << "Option " << *start << " has been renamed to " << key()
597
                << ". Support for the old name will be removed in the future.";
1✔
598
  }
599

5✔
600
  if (m_times_set == 1) {
1✔
601
    log::warn() << "Command-line option " << key()
602
                << " has been specified more than once.";
1✔
603
  }
×
604

605
  auto end_of_values = start + 1;
606
  for (; end_of_values != end; ++end_of_values) {
1✔
607
    if (end_of_values->size() > 1 && end_of_values->front() == '-') {
×
608
      // Avoid confusing numeric values for command-line arguments
609
      try {
3✔
610
        str_to_double(*end_of_values);
611
      } catch (const std::invalid_argument&) {
2✔
612
        break;
613
      }
614
    }
33✔
615
  }
616

33✔
617
  const auto min_values = m_sink->min_values();
618
  const auto max_values = m_sink->max_values();
33✔
619

39✔
620
  AR_REQUIRE(start < end_of_values);
621
  auto n_values = static_cast<size_t>(end_of_values - start - 1);
622

32✔
623
  if (n_values != min_values && min_values == max_values) {
5✔
624
    n_args_error(*start, min_values, "", n_values);
2✔
625

31✔
626
    return parsing_failed;
7✔
627
  } else if (n_values < min_values) {
2✔
628
    n_args_error(*start, min_values, " at least", n_values);
629

630
    return parsing_failed;
32✔
631
  } else if (n_values > max_values) {
5✔
632
    n_args_error(*start, max_values, " at most", n_values);
2✔
633

634
    return parsing_failed;
635
  }
32✔
636

42✔
637
  size_t result = parsing_failed;
42✔
638
  std::string error_message;
639

5✔
640
  try {
5✔
641
    result = m_sink->consume(start + 1, end_of_values);
4✔
642
  } catch (const std::invalid_argument& error) {
4✔
643
    error_message = error.what();
4✔
644
  }
645

646
  if (result == parsing_failed) {
647
    auto error = log::error();
64✔
648

64✔
649
    error << "Invalid command-line argument " << *start;
650
    for (auto it = start + 1; it != end_of_values; ++it) {
32✔
651
      error << " " << shell_escape(*it);
32✔
652
    }
653

32✔
654
    if (!error_message.empty()) {
1✔
655
      error << ": " << error_message;
656
    }
657

31✔
658
    return result;
×
659
  } else if (result == invalid_choice) {
660
    auto error = log::error();
661

31✔
662
    error << "Invalid command-line argument " << *start;
×
663
    for (auto it = start + 1; it != end_of_values; ++it) {
664
      error << " " << shell_escape(*it);
665
    }
666

667
    error << ". Valid values for " << *start << " are "
31✔
668
          << join_text(m_sink->choices(), ", ", ", and ");
31✔
669
  }
670

31✔
671
  m_times_set++;
62✔
672
  return result + 1;
1✔
673
}
1✔
674

×
675
bool
676
argument::is_deprecated_alias(const std::string& key) const
31✔
677
{
1✔
678
  return std::find(m_deprecated_keys.begin(), m_deprecated_keys.end(), key) !=
679
         m_deprecated_keys.end();
2✔
680
}
2✔
681

4✔
682
///////////////////////////////////////////////////////////////////////////////
683
// sink
684

2✔
685
sink::sink(size_t n_values)
3✔
686
  : sink(n_values, n_values)
687
{
688
}
1✔
689

31✔
690
sink::sink(size_t min_values, size_t max_values)
×
691
  : m_min_values(min_values)
692
  , m_max_values(max_values)
×
693
{
×
694
}
×
695

696
std::string
697
sink::default_value() const
×
698
{
×
699
  AR_FAIL("sink::default_value not implemented");
700
}
701

30✔
702
std::string
30✔
703
sink::preprocess(std::string value) const
31✔
704
{
705
  if (m_preprocess) {
706
    m_preprocess(value);
38✔
707
  }
708

114✔
709
  return value;
114✔
710
}
711

712
///////////////////////////////////////////////////////////////////////////////
713
// bool_sink
714

715
bool_sink::bool_sink(bool* ptr)
223✔
716
  : sink(0)
223✔
717
  , m_sink(ptr ? ptr : &m_fallback_sink)
718
{
223✔
719
  *m_sink = false;
720
}
239✔
721

239✔
722
std::string
478✔
723
bool_sink::value() const
724
{
239✔
725
  return *m_sink ? "on" : "off";
726
}
727

×
728
size_t
729
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
730
{
731
  AR_REQUIRE(start == end);
732
  *m_sink = true;
733

35✔
734
  return 0;
735
}
35✔
736

×
737
///////////////////////////////////////////////////////////////////////////////
738
// u32_sink
739

35✔
740
u32_sink::u32_sink(uint32_t* ptr)
741
  : sink(1)
742
  , m_sink(ptr)
743
  , m_minimum(std::numeric_limits<decltype(m_minimum)>::lowest())
744
  , m_maximum(std::numeric_limits<decltype(m_maximum)>::max())
745
{
154✔
746
  AR_REQUIRE(ptr);
747

154✔
748
  *m_sink = m_default;
749
}
154✔
750

154✔
751
u32_sink&
752
u32_sink::with_default(uint32_t value)
753
{
4✔
754
  AR_REQUIRE(value >= m_minimum && value <= m_maximum);
755
  set_has_default();
10✔
756
  *m_sink = m_default = value;
757

758
  return *this;
759
}
24✔
760

761
std::string
27✔
762
u32_sink::value() const
23✔
763
{
764
  return std::to_string(*m_sink);
23✔
765
}
766

767
std::string
768
u32_sink::default_value() const
769
{
770
  AR_REQUIRE(has_default());
35✔
771
  return std::to_string(m_default);
772
}
35✔
773

35✔
774
u32_sink&
105✔
775
u32_sink::with_minimum(uint32_t value)
776
{
38✔
777
  AR_REQUIRE(m_default >= value && value <= m_maximum);
778
  m_minimum = value;
34✔
779
  return *this;
35✔
780
}
781

782
u32_sink&
13✔
783
u32_sink::with_maximum(uint32_t value)
784
{
13✔
785
  AR_REQUIRE(m_default <= value && value >= m_minimum);
13✔
786
  m_maximum = value;
13✔
787
  return *this;
788
}
13✔
789

790
size_t
791
u32_sink::consume(string_vec_citer start, const string_vec_citer& end)
792
{
3✔
793
  AR_REQUIRE(end - start == 1);
794

3✔
795
  const auto value = str_to_u32(preprocess(*start));
796
  if (value < m_minimum) {
797
    throw std::invalid_argument("value must be at least " +
798
                                std::to_string(m_minimum));
2✔
799
  } else if (value > m_maximum) {
800
    throw std::invalid_argument("value must be at most " +
2✔
801
                                std::to_string(m_maximum));
2✔
802
  }
803

804
  *m_sink = value;
805
  return 1;
8✔
806
}
807

11✔
808
///////////////////////////////////////////////////////////////////////////////
7✔
809
// double_sink
7✔
810

811
double_sink::double_sink(double* ptr)
812
  : sink(1)
813
  , m_sink(ptr)
9✔
814
  , m_minimum(std::numeric_limits<decltype(m_minimum)>::lowest())
815
  , m_maximum(std::numeric_limits<decltype(m_maximum)>::max())
12✔
816
{
8✔
817
  AR_REQUIRE(ptr);
8✔
818
  *m_sink = m_default;
819
}
820

821
double_sink&
21✔
822
double_sink::with_default(double value)
823
{
27✔
824
  AR_REQUIRE(value >= m_minimum && value <= m_maximum);
825
  set_has_default();
62✔
826
  *m_sink = m_default = value;
14✔
827

4✔
828
  return *this;
5✔
829
}
13✔
830

4✔
831
namespace {
5✔
832

833
std::string
834
double_to_string(double value)
12✔
835
{
12✔
836
  auto s = std::to_string(value);
837
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
838
  s.erase(s.find_last_not_of('.') + 1, std::string::npos);
839

840
  return s;
841
}
12✔
842

843
} // namespace
12✔
844

12✔
845
std::string
36✔
846
double_sink::default_value() const
847
{
15✔
848
  AR_REQUIRE(has_default());
11✔
849
  return double_to_string(m_default);
12✔
850
}
851

852
double_sink&
2✔
853
double_sink::with_minimum(double value)
854
{
2✔
855
  AR_REQUIRE(m_default >= value && value >= m_minimum);
2✔
856
  m_minimum = value;
2✔
857

858
  return *this;
2✔
859
}
860

861
double_sink&
862
double_sink::with_maximum(double value)
863
{
864
  AR_REQUIRE(m_default <= value && value >= m_minimum);
3✔
865
  m_maximum = value;
866
  return *this;
3✔
867
}
3✔
868

3✔
869
std::string
870
double_sink::value() const
3✔
871
{
×
872
  return double_to_string(*m_sink);
873
}
874

875
size_t
876
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
877
{
878
  AR_REQUIRE(end - start == 1);
×
879

×
880
  const auto value = str_to_double(*start);
881
  if (value < m_minimum) {
882
    throw std::invalid_argument("value must be at least " +
883
                                double_to_string(m_minimum));
×
884
  } else if (value > m_maximum) {
885
    throw std::invalid_argument("value must be at most " +
×
886
                                double_to_string(m_maximum));
×
887
  }
888

×
889
  *m_sink = value;
890
  return 1;
891
}
892

×
893
///////////////////////////////////////////////////////////////////////////////
894
// str_sink
×
895

×
896
str_sink::str_sink(std::string* ptr)
×
897
  : sink(1)
898
  , m_sink(ptr ? ptr : &m_fallback_sink)
899
{
900
  AR_REQUIRE(m_sink);
3✔
901

902
  *m_sink = m_default;
3✔
903
}
904

905
str_sink&
906
str_sink::with_default(std::string_view value)
7✔
907
{
908
  set_has_default();
13✔
909
  *m_sink = m_default = value;
910

5✔
911
  return *this;
3✔
912
}
×
913

×
914
str_sink&
3✔
915
str_sink::with_choices(const string_vec& choices)
×
916
{
×
917
  m_choices = choices;
918

919
  return *this;
3✔
920
}
3✔
921

922
str_sink&
923
str_sink::with_implicit_argument(std::string_view value)
924
{
925
  m_has_implicit_argument = true;
926
  m_implicit_argument = value;
22✔
927
  set_min_values(0);
928

44✔
929
  return *this;
930
}
22✔
931

932
std::string
22✔
933
str_sink::value() const
22✔
934
{
935
  return *m_sink;
936
}
5✔
937

938
std::string
5✔
939
str_sink::default_value() const
10✔
940
{
941
  AR_REQUIRE(has_default());
5✔
942
  return m_default;
943
}
944

945
size_t
8✔
946
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
947
{
8✔
948
  AR_REQUIRE(end - start <= 1);
949

8✔
950
  size_t consumed = 0;
951
  std::string argument;
952
  if (start == end) {
953
    AR_REQUIRE(m_has_implicit_argument);
1✔
954
    argument = m_implicit_argument;
955
    consumed = 0;
1✔
956
  } else {
1✔
957
    argument = *start;
1✔
958
    consumed = 1;
959
  }
1✔
960

961
  if (m_choices.empty()) {
962
    *m_sink = preprocess(argument);
963
    return consumed;
3✔
964
  } else {
965
    const auto value = preprocess(argument);
3✔
966
    const auto choice = to_lower(value);
967
    for (const auto& it : m_choices) {
968
      if (choice == to_lower(it)) {
969
        *m_sink = it;
×
970
        return consumed;
971
      }
×
972
    }
×
973

974
    return invalid_choice;
975
  }
976
}
12✔
977

978
vec_sink::vec_sink(string_vec* ptr)
15✔
979
  : sink(1, std::numeric_limits<size_t>::max())
980
  , m_sink(ptr)
11✔
981
{
11✔
982
  AR_REQUIRE(ptr);
11✔
983

5✔
984
  m_sink->clear();
1✔
985
}
1✔
986

987
vec_sink&
9✔
988
vec_sink::with_min_values(size_t n)
9✔
989
{
990
  AR_REQUIRE(n <= max_values());
991
  set_min_values(n);
10✔
992

21✔
993
  return *this;
7✔
994
}
995

6✔
996
vec_sink&
6✔
997
vec_sink::with_max_values(size_t n)
16✔
998
{
27✔
999
  AR_REQUIRE(n >= min_values());
2✔
1000
  set_max_values(n);
2✔
1001

1002
  return *this;
1003
}
1004

1✔
1005
size_t
6✔
1006
vec_sink::consume(string_vec_citer start, const string_vec_citer& end)
11✔
1007
{
1008
  AR_REQUIRE(static_cast<size_t>(end - start) >= min_values());
16✔
1009
  AR_REQUIRE(static_cast<size_t>(end - start) <= max_values());
1010
  for (auto it = start; it != end; ++it) {
16✔
1011
    m_sink->emplace_back(preprocess(*it));
1012
  }
19✔
1013

1014
  return static_cast<size_t>(end - start);
15✔
1015
}
16✔
1016

1017
std::string
1018
vec_sink::value() const
5✔
1019
{
1020
  std::string output;
10✔
1021

5✔
1022
  for (const auto& s : *m_sink) {
1023
    if (!output.empty()) {
5✔
1024
      output.push_back(';');
1025
    }
1026

1027
    output.append(shell_escape(s));
3✔
1028
  }
1029

6✔
1030
  return output;
3✔
1031
}
1032

3✔
1033
std::ostream&
1034
operator<<(std::ostream& os, const parse_result& value)
1035
{
1036
  switch (value) {
5✔
1037
    case parse_result::exit:
1038
      return os << "parse_result::exit";
13✔
1039
    case parse_result::error:
8✔
1040
      return os << "parse_result::error";
10✔
1041
    case parse_result::ok:
18✔
1042
      return os << "parse_result::ok";
1043
    default:
1044
      AR_FAIL("invalid parse_result");
4✔
1045
  }
1046
}
1047

1048
} // namespace argparse
3✔
1049

1050
} // namespace adapterremoval
3✔
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