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

MikkelSchubert / adapterremoval / #51

25 Feb 2025 05:54PM UTC coverage: 27.154% (+0.002%) from 27.152%
#51

push

travis-ci

MikkelSchubert
typos

0 of 1 new or added line in 1 file covered. (0.0%)

266 existing lines in 2 files now uncovered.

2600 of 9575 relevant lines covered (27.15%)

4269.29 hits per line

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

92.11
/src/argparse.cpp
1
/*************************************************************************\
2
 * AdapterRemoval - cleaning next-generation sequencing reads            *
3
 *                                                                       *
4
 * Copyright (C) 2011 by Stinus Lindgreen - stinus@binf.ku.dk            *
5
 * Copyright (C) 2014 by Mikkel Schubert - mikkelsch@gmail.com           *
6
 *                                                                       *
7
 * This program is free software: you can redistribute it and/or modify  *
8
 * it under the terms of the GNU General Public License as published by  *
9
 * the Free Software Foundation, either version 3 of the License, or     *
10
 * (at your option) any later version.                                   *
11
 *                                                                       *
12
 * This program is distributed in the hope that it will be useful,       *
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15
 * GNU General Public License for more details.                          *
16
 *                                                                       *
17
 * You should have received a copy of the GNU General Public License     *
18
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. *
19
\*************************************************************************/
20
#include "argparse.hpp"
21
#include "debug.hpp"    // for AR_REQUIRE
22
#include "logging.hpp"  // for log_stream, error, cerr, warn
23
#include "strutils.hpp" // for string_vec, shell_escape, to_lower
24
#include <algorithm>    // for max, copy, find, min, sort
25
#include <limits>       // for numeric_limits
26
#include <memory>       // for __shared_ptr_access, unique_ptr, share...
27
#include <sstream>      // for operator<<, basic_ostream, ostringstream
28
#include <stdexcept>    // for invalid_argument
29
#include <utility>      // for pair
30

31
namespace adapterremoval {
32

33
namespace argparse {
34

35
namespace {
36

37
const size_t parsing_failed = static_cast<size_t>(-1);
38
const size_t invalid_choice = static_cast<size_t>(-2);
39

40
/** Detect similar arguments based on prefixes or max edit distance. */
41
bool
42
is_similar_argument(const std::string& user,
31✔
43
                    const std::string& ref,
44
                    size_t max_distance)
45
{
46
  const auto overlap = std::min(user.size(), ref.size());
124✔
47
  if (overlap == user.size() && user == ref.substr(0, overlap)) {
98✔
48
    return true;
49
  }
50

51
  const auto diff = std::max(user.size(), ref.size()) - overlap;
100✔
52

53
  return (diff <= max_distance && levenshtein(user, ref) <= max_distance);
35✔
54
}
55

56
} // namespace
57

58
///////////////////////////////////////////////////////////////////////////////
59

60
parser::parser()
210✔
61
{
62
  add_header("OPTIONS:");
60✔
63

64
  // Built-in arguments
65
  add("--help").abbreviation('h').help("Display this message");
120✔
66
  add("--version").abbreviation('v').help("Print the version string");
120✔
67
  add("--licenses").help("Print licenses for this software");
120✔
68
  add_separator();
30✔
69
}
30✔
70

71
void
72
parser::set_name(const std::string& name)
13✔
73
{
74
  m_name = name;
13✔
75
}
13✔
76

77
void
78
parser::set_version(const std::string& version)
12✔
79
{
80
  m_version = version;
12✔
81
}
12✔
82

83
void
84
parser::set_preamble(const std::string& text)
6✔
85
{
86
  m_preamble = text;
6✔
87
}
6✔
88

89
void
90
parser::set_licenses(const std::string& text)
1✔
91
{
92
  m_licenses = text;
1✔
93
}
1✔
94

95
parse_result
96
parser::parse_args(const string_vec& args)
21✔
97
{
98
  AR_REQUIRE(!args.empty());
42✔
99
  update_argument_map();
21✔
100

101
  for (auto it = args.begin() + 1; it != args.end();) {
156✔
102
    const auto argument = find_argument(*it);
50✔
103
    if (argument) {
25✔
104
      const size_t consumed = argument->parse(it, args.end());
57✔
105

106
      if (consumed == parsing_failed) {
19✔
107
        return parse_result::error;
108
      }
109

110
      it += static_cast<string_vec::iterator::difference_type>(consumed);
17✔
111
      AR_REQUIRE(it <= args.end());
51✔
112
    } else {
113
      return parse_result::error;
114
    }
115
  }
25✔
116

117
  parse_result result = parse_result::ok;
13✔
118
  for (const auto& arg : m_args) {
424✔
119
    if (arg.argument && arg.argument->is_set()) {
294✔
120
      const auto& key = arg.argument->key();
51✔
121

122
      for (const auto& requirement : arg.argument->depends_on()) {
110✔
123
        if (!is_set(requirement)) {
2✔
124
          result = parse_result::error;
1✔
125
          log::error() << "Option " << requirement << " is required when "
5✔
126
                       << "using option " << key;
3✔
127
        }
128
      }
129

130
      for (const auto& prohibited : arg.argument->conflicts_with()) {
110✔
131
        if (is_set(prohibited)) {
2✔
132
          result = parse_result::error;
1✔
133
          log::error() << "Option " << prohibited
4✔
134
                       << " cannot be used together with option " << key;
3✔
135
        }
136
      }
137
    }
138
  }
139

140
  if (is_set("--help")) {
26✔
141
    print_help();
4✔
142
    return parse_result::exit;
4✔
143
  } else if (is_set("--version")) {
18✔
144
    print_version();
2✔
145
    return parse_result::exit;
2✔
146
  } else if (is_set("--licenses")) {
14✔
147
    print_licenses();
1✔
148
    return parse_result::exit;
1✔
149
  }
150

151
  return result;
152
}
153

154
bool
155
parser::is_set(const std::string& key) const
36✔
156
{
157
  const auto it = m_keys.find(key);
36✔
158
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
108✔
159

160
  return it->second->is_set();
144✔
161
}
162

163
std::string
164
parser::value(const std::string& key) const
2✔
165
{
166
  const auto it = m_keys.find(key);
2✔
167
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
6✔
168

169
  return it->second->value();
8✔
170
}
171

172
argument&
173
parser::add(const std::string& name, const std::string& metavar)
122✔
174
{
175
  AR_REQUIRE(starts_with(name, "--"), name);
366✔
176
  auto ptr = std::make_shared<argument>(name, metavar);
122✔
177
  m_args.push_back({ std::string(), ptr });
488✔
178

179
  return *ptr;
244✔
180
}
122✔
181

182
void
183
parser::add_separator()
60✔
184
{
185
  m_args.push_back({ std::string(), argument_ptr() });
180✔
186
}
60✔
187

188
void
189
parser::add_header(const std::string& header)
30✔
190
{
191
  add_separator();
30✔
192
  m_args.push_back({ header, argument_ptr() });
60✔
193
}
30✔
194

195
void
196
parser::print_version() const
14✔
197
{
198
  log::cerr() << m_name << " " << m_version << "\n";
84✔
199
}
14✔
200

201
void
202
parser::print_help() const
12✔
203
{
204
  print_version();
12✔
205

206
  auto cerr = log::cerr();
12✔
207
  if (!m_preamble.empty()) {
24✔
208
    cli_formatter fmt;
6✔
209
    fmt.set_indent(0);
6✔
210
    fmt.set_indent_first_line(false);
6✔
211
    fmt.set_column_width(m_terminal_width);
6✔
212

213
    cerr << "\n";
6✔
214
    for (auto line : split_lines(m_preamble)) {
54✔
215
      if (starts_with(line, " ")) {
18✔
216
        cerr << line << "\n";
6✔
217
      } else {
218
        cerr << fmt.format(line) << "\n";
30✔
219
      }
220
    }
12✔
221
  }
222

223
  string_vec signatures;
12✔
224

225
  size_t indentation = 0;
12✔
226
  for (const auto& entry : m_args) {
376✔
227
    if (entry.argument && !entry.argument->is_hidden()) {
256✔
228
      const auto& arg = *entry.argument;
44✔
229

230
      std::ostringstream ss;
44✔
231
      if (arg.short_key().empty()) {
132✔
232
        ss << "   " << arg.key();
40✔
233
      } else {
234
        ss << "   " << arg.short_key() << ", " << arg.key();
96✔
235
      }
236

237
      for (size_t i = 0; i < arg.min_values(); ++i) {
50✔
238
        ss << " <" << arg.metavar() << ">";
18✔
239
      }
240

241
      if (arg.max_values() == std::numeric_limits<size_t>::max()) {
44✔
242
        ss << " [" << arg.metavar() << ", ...]";
6✔
243
      } else {
244
        for (size_t i = arg.min_values(); i < arg.max_values(); ++i) {
45✔
245
          ss << " [" << arg.metavar() << "]";
9✔
246
        }
247
      }
248

249
      indentation = std::max<size_t>(indentation, ss.str().size() + 3);
220✔
250
      signatures.emplace_back(ss.str());
132✔
251
    } else {
44✔
252
      signatures.emplace_back();
38✔
253
    }
254
  }
255

256
  cli_formatter fmt;
12✔
257
  fmt.set_indent(indentation);
12✔
258
  fmt.set_indent_first_line(false);
12✔
259
  fmt.set_column_width(m_terminal_width - indentation);
12✔
260

261
  for (size_t i = 0; i < m_args.size(); i++) {
188✔
262
    const auto& entry = m_args.at(i);
82✔
263
    if (entry.argument) {
164✔
264
      if (!entry.argument->is_hidden()) {
138✔
265
        const auto& arg = *entry.argument;
44✔
266
        const auto& signature = signatures.at(i);
44✔
267

268
        cerr << signature;
44✔
269

270
        const std::string help = arg.help();
44✔
271
        if (!help.empty()) {
88✔
272
          // Format into columns and indent lines (except the first line)
273
          cerr << std::string(indentation - signature.length(), ' ')
168✔
274
               << fmt.format(help);
168✔
275
        }
276

277
        cerr << "\n";
88✔
278
      }
44✔
279
    } else {
280
      cerr << entry.header << "\n";
154✔
281
    }
282
  }
283
}
24✔
284

285
void
286
parser::print_licenses() const
1✔
287
{
288
  auto cerr = log::cerr();
1✔
289

290
  for (const auto& block : split_lines(m_licenses)) {
15✔
291
    if (block.empty()) {
6✔
292
      cerr << "\n";
3✔
293
    } else {
294
      size_t indentation = block.find_first_not_of(' ');
3✔
295
      if (indentation == std::string::npos) {
3✔
296
        indentation = 0;
×
297
      }
298

299
      size_t ljust = 0;
3✔
300
      if (!block.empty() && block.at(indentation) == '*') {
6✔
301
        ljust = 2;
302
      } else if (block.size() > indentation + 1 &&
6✔
303
                 block.at(indentation + 1) == '.') {
3✔
304
        ljust = 3;
305
      }
306

307
      const auto width = 80 - indentation;
3✔
308
      for (const auto& line : wrap_text(block, width, ljust)) {
24✔
309
        cerr << std::string(indentation, ' ') << line << "\n";
18✔
310
      }
3✔
311
    }
312
  }
1✔
313
}
2✔
314

315
void
316
parser::set_terminal_width(unsigned w)
6✔
317
{
318
  m_terminal_width = w;
6✔
319
}
6✔
320

321
void
322
parser::update_argument_map()
21✔
323
{
324
  m_keys.clear();
21✔
325

326
  for (auto& it : m_args) {
680✔
327
    if (it.argument) {
298✔
328
      for (const auto& key : it.argument->keys()) {
814✔
329
        const auto result = m_keys.emplace(key, it.argument);
128✔
330
        AR_REQUIRE(result.second, shell_escape(key));
128✔
331
      }
86✔
332
    }
333
  }
334

335
  bool any_errors = false;
21✔
336
  for (const auto& it : m_args) {
680✔
337
    if (it.argument) {
298✔
338
      for (const auto& key : it.argument->conflicts_with()) {
524✔
339
        if (!m_keys.count(key)) {
2✔
340
          any_errors = true;
×
341
          log::error() << it.argument->key() << " conflicts with "
×
342
                       << "unknown command-line option " << key;
×
343
        }
344
      }
345

346
      for (const auto& key : it.argument->depends_on()) {
524✔
347
        if (!m_keys.count(key)) {
2✔
348
          any_errors = true;
×
349
          log::error() << it.argument->key() << " requires "
×
350
                       << "unknown command-line option " << key;
×
351
        }
352
      }
353
    }
354
  }
355

356
  AR_REQUIRE(!any_errors, "bugs in argument parsing");
21✔
357
}
21✔
358

359
argument_ptr
360
parser::find_argument(const std::string& key)
25✔
361
{
362
  auto it = m_keys.find(key);
25✔
363
  if (it != m_keys.end()) {
75✔
364
    return it->second;
38✔
365
  }
366

367
  if (key.size() > 1 && key.front() == '-' && key.back() != '-') {
12✔
368
    string_vec candidates;
5✔
369
    const size_t max_distance = 1 + key.size() / 4;
5✔
370

371
    for (const auto& arg : m_args) {
164✔
372
      if (arg.argument) {
72✔
373
        for (const auto& name : arg.argument->keys()) {
229✔
374

375
          if (is_similar_argument(key, name, max_distance)) {
31✔
376
            candidates.push_back(name);
7✔
377
          }
378
        }
21✔
379
      }
380
    }
381

382
    auto error = log::error();
5✔
383
    error << "Unknown argument '" << key << "'";
15✔
384

385
    if (!candidates.empty()) {
10✔
386
      std::sort(candidates.begin(), candidates.end());
12✔
387

388
      error << ". Did you mean " << candidates.front();
8✔
389
      for (size_t i = 1; i < candidates.size() - 1; ++i) {
10✔
390
        error << ", " << candidates.at(i);
3✔
391
      }
392

393
      if (candidates.size() > 1) {
8✔
394
        error << " or " << candidates.back();
4✔
395
      }
396

397
      error << "?";
4✔
398
    }
399
  } else {
5✔
400
    log::error() << "Unexpected positional argument '" << key << "'";
5✔
401
  }
402

403
  return {};
31✔
404
}
405

406
///////////////////////////////////////////////////////////////////////////////
407

408
argument::argument(const std::string& key, std::string metavar)
148✔
409
  : m_key_long(key)
148✔
410
  , m_metavar(std::move(metavar))
148✔
411
  , m_sink(std::make_unique<bool_sink>(&m_default_sink))
1,332✔
412
{
413
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
296✔
414
}
148✔
415

416
argument&
417
argument::help(const std::string& text)
103✔
418
{
419
  // Enforce this the style of help text
420
  AR_REQUIRE(!ends_with(text, "."),
318✔
421
             "Help text ends with dot: " + log_escape(text));
422
  m_help = text;
102✔
423

424
  return *this;
102✔
425
}
426

427
string_vec
428
argument::keys() const
110✔
429
{
430
  string_vec keys = m_deprecated_keys;
110✔
431
  keys.push_back(m_key_long);
110✔
432

433
  if (!m_key_short.empty()) {
220✔
434
    keys.push_back(m_key_short);
53✔
435
  }
436

437
  return keys;
110✔
UNCOV
438
}
×
439

440
std::string
441
argument::help() const
51✔
442
{
443
  // Append string representation of current (default) value
444
  std::ostringstream ss(m_help);
51✔
445
  ss << m_help;
51✔
446

447
  const auto choices = m_sink->choices();
102✔
448
  if (!choices.empty()) {
102✔
449
    ss << ". Possible values are " << join_text(choices, ", ", ", and ");
15✔
450
  }
451

452
  if (m_sink->has_default() && m_help.find("[default:") == std::string::npos) {
102✔
453
    ss << " [default: " << default_value() << "]";
6✔
454
  }
455

456
  return ss.str();
102✔
457
}
51✔
458

459
size_t
460
argument::min_values() const
92✔
461
{
462
  return m_sink->min_values();
184✔
463
}
464

465
size_t
466
argument::max_values() const
89✔
467
{
468
  return m_sink->max_values();
178✔
469
}
470

471
std::string
472
argument::value() const
3✔
473
{
474
  return m_sink->value();
6✔
475
}
476

477
std::string
478
argument::default_value() const
2✔
479
{
480
  return m_sink->default_value();
4✔
481
}
482

483
template<typename A, typename B>
484
A&
485
bind(std::unique_ptr<sink>& ptr, B* sink)
23✔
486
{
487
  ptr = std::make_unique<A>(sink);
23✔
488

489
  return static_cast<A&>(*ptr);
23✔
490
}
491

5✔
492
bool_sink&
493
argument::bind_bool(bool* ptr)
5✔
494
{
495
  return bind<bool_sink>(m_sink, ptr);
5✔
496
}
497

5✔
498
u32_sink&
499
argument::bind_u32(unsigned* ptr)
5✔
500
{
501
  return bind<u32_sink>(m_sink, ptr);
5✔
502
}
503

2✔
504
double_sink&
505
argument::bind_double(double* ptr)
2✔
506
{
507
  return bind<double_sink>(m_sink, ptr);
2✔
508
}
509

10✔
510
str_sink&
511
argument::bind_str(std::string* ptr)
10✔
512
{
513
  return bind<str_sink>(m_sink, ptr);
10✔
514
}
515

1✔
516
vec_sink&
517
argument::bind_vec(string_vec* ptr)
1✔
518
{
519
  return bind<vec_sink>(m_sink, ptr);
1✔
520
}
521

522
argument&
523
argument::abbreviation(char key)
1✔
524
{
525
  // To avoid ambiguity only lower-case alphabetical arguments are allowed
1✔
526
  AR_REQUIRE(key >= 'a' && key <= 'z', std::string{ key });
527
  m_key_short.clear();
528
  m_key_short.push_back('-');
529
  m_key_short.push_back(key);
10✔
530

531
  return *this;
10✔
532
}
533

534
argument&
535
argument::deprecated_alias(const std::string& key)
2✔
536
{
537
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
2✔
538
  m_deprecated_keys.emplace_back(key);
539

540
  return *this;
541
}
5✔
542

543
argument&
5✔
544
argument::deprecated()
545
{
546
  m_deprecated = true;
547

5✔
548
  return hidden();
549
}
5✔
550

551
argument&
552
argument::hidden()
553
{
61✔
554
  m_hidden = true;
555

556
  return *this;
61✔
557
}
61✔
558

61✔
559
argument&
61✔
560
argument::depends_on(const std::string& key)
561
{
61✔
562
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
563
  m_depends_on.push_back(key);
564

565
  return *this;
3✔
566
}
567

6✔
568
argument&
3✔
569
argument::conflicts_with(const std::string& key)
570
{
3✔
571
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
572
  m_conflicts_with.push_back(key);
573

574
  return *this;
2✔
575
}
576

2✔
577
void
578
n_args_error(const std::string& key,
2✔
579
             size_t limit,
580
             const char* relation,
581
             size_t n)
582
{
4✔
583
  auto out = log::error();
584

4✔
585
  out << "Command-line argument " << key << " takes" << relation << " " << limit
586
      << " value";
4✔
587

588
  if (limit != 1) {
589
    out << "s";
590
  }
4✔
591

592
  if (n == 1) {
8✔
593
    out << ", but 1 value was provided!";
4✔
594
  } else {
595
    out << ", but " << n << " values were provided!";
4✔
596
  }
597
}
598

599
size_t
4✔
600
argument::parse(string_vec_citer start, const string_vec_citer& end)
601
{
8✔
602
  AR_REQUIRE(start != end);
4✔
603

604
  const bool deprecated_alias = is_deprecated_alias(*start);
4✔
605
  AR_REQUIRE(deprecated_alias || *start == m_key_long ||
606
             (!m_key_short.empty() && *start == m_key_short));
607

608
  if (m_deprecated) {
1✔
609
    log::warn() << "Option " << *start << " is deprecated and will "
610
                << "be removed in the future.";
611
  } else if (deprecated_alias) {
612
    log::warn() << "Option " << *start << " has been renamed to " << key()
613
                << ". Support for the old name will be removed in the future.";
1✔
614
  }
615

6✔
616
  if (m_times_set == 1) {
1✔
617
    log::warn() << "Command-line option " << key()
618
                << " has been specified more than once.";
1✔
UNCOV
619
  }
×
620

621
  auto end_of_values = start + 1;
622
  for (; end_of_values != end; ++end_of_values) {
1✔
UNCOV
623
    if (end_of_values->size() > 1 && end_of_values->front() == '-') {
×
624
      // Avoid confusing numeric values for command-line arguments
625
      try {
3✔
626
        str_to_double(*end_of_values);
627
      } catch (const std::invalid_argument&) {
2✔
628
        break;
629
      }
630
    }
33✔
631
  }
632

66✔
633
  const auto min_values = m_sink->min_values();
634
  const auto max_values = m_sink->max_values();
66✔
635

77✔
636
  AR_REQUIRE(start < end_of_values);
637
  auto n_values = static_cast<size_t>(end_of_values - start - 1);
638

32✔
639
  if (n_values != min_values && min_values == max_values) {
6✔
640
    n_args_error(*start, min_values, "", n_values);
2✔
641

31✔
642
    return parsing_failed;
8✔
643
  } else if (n_values < min_values) {
2✔
644
    n_args_error(*start, min_values, " at least", n_values);
645

646
    return parsing_failed;
32✔
647
  } else if (n_values > max_values) {
5✔
648
    n_args_error(*start, max_values, " at most", n_values);
2✔
649

650
    return parsing_failed;
651
  }
32✔
652

94✔
653
  size_t result = parsing_failed;
42✔
654
  std::string error_message;
655

5✔
656
  try {
10✔
657
    result = m_sink->consume(start + 1, end_of_values);
4✔
658
  } catch (const std::invalid_argument& error) {
4✔
659
    error_message = error.what();
4✔
660
  }
661

662
  if (result == parsing_failed) {
663
    auto error = log::error();
64✔
664

64✔
665
    error << "Invalid command-line argument " << *start;
666
    for (auto it = start + 1; it != end_of_values; ++it) {
64✔
667
      error << " " << shell_escape(*it);
32✔
668
    }
669

32✔
670
    if (!error_message.empty()) {
2✔
671
      error << ": " << error_message;
672
    }
673

31✔
UNCOV
674
    return result;
×
675
  } else if (result == invalid_choice) {
676
    auto error = log::error();
677

31✔
UNCOV
678
    error << "Invalid command-line argument " << *start;
×
679
    for (auto it = start + 1; it != end_of_values; ++it) {
680
      error << " " << shell_escape(*it);
681
    }
682

683
    error << ". Valid values for " << *start << " are "
31✔
684
          << join_text(m_sink->choices(), ", ", ", and ");
31✔
685
  }
686

31✔
687
  m_times_set++;
93✔
688
  return result + 1;
1✔
689
}
1✔
UNCOV
690

×
691
bool
692
argument::is_deprecated_alias(const std::string& key) const
31✔
693
{
1✔
694
  return std::find(m_deprecated_keys.begin(), m_deprecated_keys.end(), key) !=
695
         m_deprecated_keys.end();
3✔
696
}
6✔
697

6✔
698
///////////////////////////////////////////////////////////////////////////////
699
// sink
700

2✔
701
sink::sink(size_t n_values)
2✔
702
  : sink(n_values, n_values)
703
{
704
}
1✔
705

31✔
706
sink::sink(size_t min_values, size_t max_values)
×
707
  : m_min_values(min_values)
UNCOV
708
  , m_max_values(max_values)
×
UNCOV
709
{
×
710
}
×
711

712
std::string
UNCOV
713
sink::default_value() const
×
UNCOV
714
{
×
715
  AR_FAIL("sink::default_value not implemented");
716
}
717

30✔
718
std::string
30✔
719
sink::preprocess(std::string value) const
32✔
720
{
721
  if (m_preprocess) {
722
    m_preprocess(value);
38✔
723
  }
724

152✔
725
  return value;
114✔
726
}
727

728
///////////////////////////////////////////////////////////////////////////////
729
// bool_sink
730

731
bool_sink::bool_sink(bool* ptr)
223✔
732
  : sink(0)
223✔
733
  , m_sink(ptr ? ptr : &m_fallback_sink)
734
{
223✔
735
  *m_sink = false;
736
}
239✔
737

239✔
738
std::string
478✔
739
bool_sink::value() const
740
{
239✔
741
  return *m_sink ? "on" : "off";
742
}
UNCOV
743

×
744
size_t
UNCOV
745
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
746
{
747
  AR_REQUIRE(start == end);
748
  *m_sink = true;
749

35✔
750
  return 0;
751
}
35✔
UNCOV
752

×
753
///////////////////////////////////////////////////////////////////////////////
754
// u32_sink
755

35✔
756
u32_sink::u32_sink(uint32_t* ptr)
757
  : sink(1)
758
  , m_sink(ptr)
759
  , m_minimum(std::numeric_limits<decltype(m_minimum)>::lowest())
760
  , m_maximum(std::numeric_limits<decltype(m_maximum)>::max())
761
{
154✔
762
  AR_REQUIRE(ptr);
763

154✔
764
  *m_sink = m_default;
765
}
154✔
766

154✔
767
u32_sink&
768
u32_sink::with_default(uint32_t value)
769
{
4✔
770
  AR_REQUIRE(value >= m_minimum && value <= m_maximum);
771
  set_has_default();
10✔
772
  *m_sink = m_default = value;
773

774
  return *this;
775
}
24✔
776

777
std::string
55✔
778
u32_sink::value() const
23✔
779
{
780
  return std::to_string(*m_sink);
23✔
781
}
782

783
std::string
784
u32_sink::default_value() const
785
{
786
  AR_REQUIRE(has_default());
35✔
787
  return std::to_string(m_default);
788
}
35✔
789

35✔
790
u32_sink&
105✔
791
u32_sink::with_minimum(uint32_t value)
792
{
42✔
793
  AR_REQUIRE(m_default >= value && value <= m_maximum);
794
  m_minimum = value;
34✔
795
  return *this;
35✔
796
}
797

798
u32_sink&
13✔
799
u32_sink::with_maximum(uint32_t value)
800
{
13✔
801
  AR_REQUIRE(m_default <= value && value >= m_minimum);
13✔
802
  m_maximum = value;
13✔
803
  return *this;
804
}
13✔
805

806
size_t
807
u32_sink::consume(string_vec_citer start, const string_vec_citer& end)
808
{
3✔
809
  AR_REQUIRE(end - start == 1);
810

3✔
811
  const auto value = str_to_u32(preprocess(*start));
812
  if (value < m_minimum) {
813
    throw std::invalid_argument("value must be at least " +
814
                                std::to_string(m_minimum));
2✔
815
  } else if (value > m_maximum) {
816
    throw std::invalid_argument("value must be at most " +
2✔
817
                                std::to_string(m_maximum));
2✔
818
  }
819

820
  *m_sink = value;
821
  return 1;
8✔
822
}
823

15✔
824
///////////////////////////////////////////////////////////////////////////////
7✔
825
// double_sink
7✔
826

827
double_sink::double_sink(double* ptr)
828
  : sink(1)
829
  , m_sink(ptr)
9✔
830
  , m_minimum(std::numeric_limits<decltype(m_minimum)>::lowest())
831
  , m_maximum(std::numeric_limits<decltype(m_maximum)>::max())
16✔
832
{
8✔
833
  AR_REQUIRE(ptr);
8✔
834
  *m_sink = m_default;
835
}
836

837
double_sink&
21✔
838
double_sink::with_default(double value)
839
{
56✔
840
  AR_REQUIRE(value >= m_minimum && value <= m_maximum);
841
  set_has_default();
95✔
842
  *m_sink = m_default = value;
14✔
843

3✔
844
  return *this;
3✔
845
}
13✔
846

3✔
847
namespace {
3✔
848

849
std::string
850
double_to_string(double value)
12✔
851
{
12✔
852
  auto s = std::to_string(value);
853
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
854
  s.erase(s.find_last_not_of('.') + 1, std::string::npos);
855

856
  return s;
857
}
12✔
858

859
} // namespace
12✔
860

12✔
861
std::string
36✔
862
double_sink::default_value() const
863
{
19✔
864
  AR_REQUIRE(has_default());
11✔
865
  return double_to_string(m_default);
12✔
866
}
867

868
double_sink&
2✔
869
double_sink::with_minimum(double value)
870
{
2✔
871
  AR_REQUIRE(m_default >= value && value >= m_minimum);
2✔
872
  m_minimum = value;
2✔
873

874
  return *this;
2✔
875
}
876

877
double_sink&
878
double_sink::with_maximum(double value)
879
{
880
  AR_REQUIRE(m_default <= value && value >= m_minimum);
3✔
881
  m_maximum = value;
882
  return *this;
3✔
883
}
3✔
884

3✔
885
std::string
886
double_sink::value() const
3✔
UNCOV
887
{
×
888
  return double_to_string(*m_sink);
889
}
890

891
size_t
892
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
893
{
UNCOV
894
  AR_REQUIRE(end - start == 1);
×
UNCOV
895

×
896
  const auto value = str_to_double(*start);
897
  if (value < m_minimum) {
898
    throw std::invalid_argument("value must be at least " +
899
                                double_to_string(m_minimum));
×
900
  } else if (value > m_maximum) {
901
    throw std::invalid_argument("value must be at most " +
×
UNCOV
902
                                double_to_string(m_maximum));
×
903
  }
UNCOV
904

×
905
  *m_sink = value;
906
  return 1;
907
}
908

×
909
///////////////////////////////////////////////////////////////////////////////
UNCOV
910
// str_sink
×
UNCOV
911

×
UNCOV
912
str_sink::str_sink(std::string* ptr)
×
913
  : sink(1)
914
  , m_sink(ptr ? ptr : &m_fallback_sink)
915
{
916
  AR_REQUIRE(m_sink);
3✔
917

918
  *m_sink = m_default;
3✔
919
}
920

921
str_sink&
922
str_sink::with_default(std::string_view value)
7✔
923
{
924
  set_has_default();
28✔
925
  *m_sink = m_default = value;
926

10✔
927
  return *this;
3✔
928
}
×
929

×
930
str_sink&
3✔
UNCOV
931
str_sink::with_choices(const string_vec& choices)
×
UNCOV
932
{
×
933
  m_choices = choices;
934

935
  return *this;
3✔
936
}
3✔
937

938
str_sink&
939
str_sink::with_implicit_argument(std::string_view value)
940
{
941
  m_has_implicit_argument = true;
942
  m_implicit_argument = value;
22✔
943
  set_min_values(0);
944

88✔
945
  return *this;
946
}
22✔
947

948
std::string
22✔
949
str_sink::value() const
22✔
950
{
951
  return *m_sink;
952
}
5✔
953

954
std::string
5✔
955
str_sink::default_value() const
10✔
956
{
957
  AR_REQUIRE(has_default());
5✔
958
  return m_default;
959
}
960

961
size_t
8✔
962
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
963
{
8✔
964
  AR_REQUIRE(end - start <= 1);
965

8✔
966
  size_t consumed = 0;
967
  std::string argument;
968
  if (start == end) {
969
    AR_REQUIRE(m_has_implicit_argument);
1✔
970
    argument = m_implicit_argument;
971
    consumed = 0;
1✔
972
  } else {
1✔
973
    argument = *start;
1✔
974
    consumed = 1;
975
  }
1✔
976

977
  if (m_choices.empty()) {
978
    *m_sink = preprocess(argument);
979
    return consumed;
3✔
980
  } else {
981
    const auto value = preprocess(argument);
3✔
982
    const auto choice = to_lower(value);
983
    for (const auto& it : m_choices) {
984
      if (choice == to_lower(it)) {
985
        *m_sink = it;
×
986
        return consumed;
UNCOV
987
      }
×
UNCOV
988
    }
×
989

990
    return invalid_choice;
991
  }
992
}
12✔
993

994
vec_sink::vec_sink(string_vec* ptr)
31✔
995
  : sink(1, std::numeric_limits<size_t>::max())
996
  , m_sink(ptr)
11✔
997
{
11✔
998
  AR_REQUIRE(ptr);
22✔
999

9✔
1000
  m_sink->clear();
1✔
1001
}
1✔
1002

1003
vec_sink&
18✔
1004
vec_sink::with_min_values(size_t n)
9✔
1005
{
1006
  AR_REQUIRE(n <= max_values());
1007
  set_min_values(n);
20✔
1008

14✔
1009
  return *this;
7✔
1010
}
1011

6✔
1012
vec_sink&
6✔
1013
vec_sink::with_max_values(size_t n)
42✔
1014
{
27✔
1015
  AR_REQUIRE(n >= min_values());
2✔
1016
  set_max_values(n);
2✔
1017

1018
  return *this;
1019
}
1020

1✔
1021
size_t
6✔
1022
vec_sink::consume(string_vec_citer start, const string_vec_citer& end)
11✔
1023
{
1024
  AR_REQUIRE(static_cast<size_t>(end - start) >= min_values());
16✔
1025
  AR_REQUIRE(static_cast<size_t>(end - start) <= max_values());
1026
  for (auto it = start; it != end; ++it) {
16✔
1027
    m_sink->emplace_back(preprocess(*it));
1028
  }
23✔
1029

1030
  return static_cast<size_t>(end - start);
15✔
1031
}
16✔
1032

1033
std::string
1034
vec_sink::value() const
5✔
1035
{
1036
  std::string output;
10✔
1037

5✔
1038
  for (const auto& s : *m_sink) {
1039
    if (!output.empty()) {
5✔
1040
      output.push_back(';');
1041
    }
1042

1043
    output.append(shell_escape(s));
3✔
1044
  }
1045

6✔
1046
  return output;
3✔
1047
}
1048

3✔
1049
} // namespace argparse
1050

1051
} // namespace adapterremoval
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

© 2025 Coveralls, Inc