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

MikkelSchubert / adapterremoval / #36

22 Jul 2024 09:33AM UTC coverage: 87.26% (-12.7%) from 100.0%
#36

push

travis-ci

MikkelSchubert
remove duplicate tests

2185 of 2504 relevant lines covered (87.26%)

16293.15 hits per line

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

95.12
/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

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

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

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

55
bool
56
to_double(const std::string& value, double& out)
10✔
57
{
58
  std::istringstream stream(value);
10✔
59
  if (!(stream >> out)) {
30✔
60
    return false;
61
  }
62

63
  // Failing on trailing, non-numerical values
64
  char trailing = 0;
5✔
65
  return (!(stream >> trailing));
10✔
66
}
10✔
67

68
} // namespace
69

70
///////////////////////////////////////////////////////////////////////////////
71

72
parser::parser()
203✔
73
{
74
  add_header("OPTIONS:");
58✔
75

76
  // Built-in arguments
77
  add("--help").abbreviation('h').help("Display this message.");
145✔
78
  add("--version").abbreviation('v').help("Print the version string.");
145✔
79
  add("--licenses").help("Print licenses for this software.");
232✔
80
  add_separator();
29✔
81
}
29✔
82

83
void
84
parser::set_name(const std::string& name)
13✔
85
{
86
  m_name = name;
13✔
87
}
13✔
88

89
void
90
parser::set_version(const std::string& version)
12✔
91
{
92
  m_version = version;
12✔
93
}
12✔
94

95
void
96
parser::set_preamble(const std::string& text)
6✔
97
{
98
  m_preamble = text;
6✔
99
}
6✔
100

101
void
102
parser::set_licenses(const std::string& text)
1✔
103
{
104
  m_licenses = text;
1✔
105
}
1✔
106

107
parse_result
108
parser::parse_args(const string_vec& args)
21✔
109
{
110
  AR_REQUIRE(!args.empty());
42✔
111
  update_argument_map();
21✔
112

113
  for (auto it = args.begin() + 1; it != args.end();) {
156✔
114
    const auto argument = find_argument(*it);
50✔
115
    if (argument) {
25✔
116
      const size_t consumed = argument->parse(it, args.end());
57✔
117

118
      if (consumed == parsing_failed) {
19✔
119
        return parse_result::error;
120
      }
121

122
      it += static_cast<string_vec::iterator::difference_type>(consumed);
17✔
123
      AR_REQUIRE(it <= args.end());
51✔
124
    } else {
125
      return parse_result::error;
126
    }
127
  }
25✔
128

129
  parse_result result = parse_result::ok;
13✔
130
  for (const auto& arg : m_args) {
424✔
131
    if (arg.argument && arg.argument->is_set()) {
294✔
132
      const auto& key = arg.argument->key();
51✔
133

134
      for (const auto& requirement : arg.argument->depends_on()) {
110✔
135
        if (!is_set(requirement)) {
2✔
136
          result = parse_result::error;
1✔
137
          log::error() << "Option " << requirement << " is required when "
5✔
138
                       << "using option " << key;
3✔
139
        }
140
      }
141

142
      for (const auto& prohibited : arg.argument->conflicts_with()) {
110✔
143
        if (is_set(prohibited)) {
2✔
144
          result = parse_result::error;
1✔
145
          log::error() << "Option " << prohibited
4✔
146
                       << " cannot be used together with option " << key;
3✔
147
        }
148
      }
149
    }
150
  }
151

152
  if (is_set("--help")) {
26✔
153
    print_help();
4✔
154
    return parse_result::exit;
4✔
155
  } else if (is_set("--version")) {
18✔
156
    print_version();
2✔
157
    return parse_result::exit;
2✔
158
  } else if (is_set("--licenses")) {
14✔
159
    print_licenses();
1✔
160
    return parse_result::exit;
1✔
161
  }
162

163
  return result;
164
}
165

166
bool
167
parser::is_set(const std::string& key) const
36✔
168
{
169
  const auto it = m_keys.find(key);
36✔
170
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
108✔
171

172
  return it->second->is_set();
144✔
173
}
174

175
std::string
176
parser::value(const std::string& key) const
2✔
177
{
178
  const auto it = m_keys.find(key);
2✔
179
  AR_REQUIRE(it != m_keys.end(), shell_escape(key));
6✔
180

181
  return it->second->value();
8✔
182
}
183

184
argument&
185
parser::add(const std::string& name, const std::string& metavar)
118✔
186
{
187
  auto ptr = std::make_shared<argument>(name, metavar);
118✔
188
  m_args.push_back({ std::string(), ptr });
472✔
189

190
  return *ptr;
354✔
191
}
118✔
192

193
void
194
parser::add_separator()
58✔
195
{
196
  m_args.push_back({ std::string(), argument_ptr() });
174✔
197
}
58✔
198

199
void
200
parser::add_header(const std::string& header)
29✔
201
{
202
  add_separator();
29✔
203
  m_args.push_back({ header, argument_ptr() });
58✔
204
}
29✔
205

206
void
207
parser::print_version() const
14✔
208
{
209
  log::cerr() << m_name << " " << m_version << "\n";
84✔
210
}
14✔
211

212
void
213
parser::print_help() const
12✔
214
{
215
  print_version();
12✔
216

217
  auto cerr = log::cerr();
12✔
218
  if (!m_preamble.empty()) {
24✔
219
    cerr << "\n" << m_preamble;
12✔
220
  }
221

222
  string_vec signatures;
12✔
223

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

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

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

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

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

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

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

267
        cerr << signature;
44✔
268

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

402
  return {};
31✔
403
}
404

405
///////////////////////////////////////////////////////////////////////////////
406

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

415
argument&
416
argument::help(const std::string& text)
99✔
417
{
418
  m_help = text;
99✔
419

420
  return *this;
99✔
421
}
422

423
string_vec
424
argument::keys() const
110✔
425
{
426
  string_vec keys = m_deprecated_keys;
110✔
427
  keys.push_back(m_key_long);
110✔
428

429
  if (!m_key_short.empty()) {
220✔
430
    keys.push_back(m_key_short);
53✔
431
  }
432

433
  return keys;
110✔
434
}
×
435

436
std::string
437
argument::help() const
51✔
438
{
439
  // Append string representation of current (default) value
440
  std::ostringstream ss(m_help);
51✔
441
  ss << m_help;
51✔
442

443
  const auto choices = m_sink->choices();
102✔
444
  if (!choices.empty()) {
102✔
445
    ss << ". Possible values are ";
3✔
446

447
    for (size_t i = 0; i < choices.size(); ++i) {
18✔
448
      if (i && i == choices.size() - 1) {
9✔
449
        ss << ", and ";
2✔
450
      } else if (i) {
4✔
451
        ss << ", ";
1✔
452
      }
453

454
      ss << choices.at(i);
18✔
455
    }
456
  }
457

458
  if (m_sink->has_default() && m_help.find("[default:") == std::string::npos) {
102✔
459
    ss << " [default: " << default_value() << "]";
8✔
460
  }
461

462
  return ss.str();
102✔
463
}
51✔
464

465
size_t
466
argument::min_values() const
92✔
467
{
468
  return m_sink->min_values();
276✔
469
}
470

471
size_t
472
argument::max_values() const
89✔
473
{
474
  return m_sink->max_values();
267✔
475
}
476

477
std::string
478
argument::value() const
3✔
479
{
480
  return m_sink->value();
6✔
481
}
482

483
std::string
484
argument::default_value() const
2✔
485
{
486
  return m_sink->default_value();
4✔
487
}
488

489
template<typename A, typename B>
490
A&
491
bind(std::unique_ptr<sink>& ptr, B* sink)
23✔
492
{
493
  ptr = std::make_unique<A>(sink);
69✔
494

495
  return static_cast<A&>(*ptr);
46✔
496
}
497

5✔
498
bool_sink&
499
argument::bind_bool(bool* ptr)
15✔
500
{
501
  return bind<bool_sink>(m_sink, ptr);
10✔
502
}
503

5✔
504
uint_sink&
505
argument::bind_uint(unsigned* ptr)
15✔
506
{
507
  return bind<uint_sink>(m_sink, ptr);
10✔
508
}
509

2✔
510
double_sink&
511
argument::bind_double(double* ptr)
6✔
512
{
513
  return bind<double_sink>(m_sink, ptr);
4✔
514
}
515

10✔
516
str_sink&
517
argument::bind_str(std::string* ptr)
30✔
518
{
519
  return bind<str_sink>(m_sink, ptr);
20✔
520
}
521

1✔
522
vec_sink&
523
argument::bind_vec(string_vec* ptr)
3✔
524
{
525
  return bind<vec_sink>(m_sink, ptr);
2✔
526
}
527

528
argument&
529
argument::abbreviation(char key)
1✔
530
{
531
  m_key_short.clear();
1✔
532
  m_key_short.push_back('-');
533
  m_key_short.push_back(key);
534

535
  return *this;
10✔
536
}
537

10✔
538
argument&
539
argument::deprecated_alias(const std::string& key)
540
{
541
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
2✔
542
  m_deprecated_keys.emplace_back(key);
543

2✔
544
  return *this;
545
}
546

547
argument&
5✔
548
argument::deprecated()
549
{
5✔
550
  m_deprecated = true;
551

552
  return hidden();
553
}
5✔
554

555
argument&
5✔
556
argument::hidden()
557
{
558
  m_hidden = true;
559

59✔
560
  return *this;
561
}
59✔
562

59✔
563
argument&
59✔
564
argument::depends_on(const std::string& key)
565
{
59✔
566
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
567
  m_depends_on.push_back(key);
568

569
  return *this;
3✔
570
}
571

9✔
572
argument&
3✔
573
argument::conflicts_with(const std::string& key)
574
{
3✔
575
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
576
  m_conflicts_with.push_back(key);
577

578
  return *this;
2✔
579
}
580

2✔
581
void
582
n_args_error(const std::string& key,
2✔
583
             size_t limit,
584
             const char* relation,
585
             size_t n)
586
{
4✔
587
  auto out = log::error();
588

4✔
589
  out << "Command-line argument " << key << " takes" << relation << " " << limit
590
      << " value";
4✔
591

592
  if (limit != 1) {
593
    out << "s";
594
  }
4✔
595

596
  if (n == 1) {
12✔
597
    out << ", but 1 value was provided!";
4✔
598
  } else {
599
    out << ", but " << n << " values were provided!";
4✔
600
  }
601
}
602

603
size_t
4✔
604
argument::parse(string_vec_citer start, const string_vec_citer& end)
605
{
12✔
606
  AR_REQUIRE(start != end);
4✔
607

608
  bool is_deprecated = is_deprecated_alias(*start);
4✔
609
  AR_REQUIRE(is_deprecated || *start == m_key_long ||
610
             (!m_key_short.empty() && *start == m_key_short));
611

612
  if (m_deprecated) {
1✔
613
    log::warn() << "Option " << *start << " is deprecated and will "
614
                << "be removed in the future.";
615
  } else if (is_deprecated) {
616
    log::warn() << "Option " << *start << " is deprecated and will "
617
                << "be removed in the future. Please use " << key()
1✔
618
                << " instead.";
619
  }
6✔
620

1✔
621
  if (m_times_set == 1) {
622
    log::warn() << "Command-line option " << key()
1✔
623
                << " has been specified more than once.";
×
624
  }
625

626
  double numeric_sink = 0;
1✔
627
  auto end_of_values = start + 1;
×
628
  for (; end_of_values != end; ++end_of_values) {
629
    if (end_of_values->size() > 1 && end_of_values->front() == '-' &&
3✔
630
        // Avoid confusing numeric values for command-line arguments
631
        !to_double(*end_of_values, numeric_sink)) {
2✔
632
      break;
633
    }
634
  }
33✔
635

636
  const auto min_values = m_sink->min_values();
66✔
637
  const auto max_values = m_sink->max_values();
638

66✔
639
  AR_REQUIRE(start < end_of_values);
77✔
640
  auto n_values = static_cast<size_t>(end_of_values - start - 1);
641

642
  if (n_values != min_values && min_values == max_values) {
32✔
643
    n_args_error(*start, min_values, "", n_values);
6✔
644

2✔
645
    return parsing_failed;
31✔
646
  } else if (n_values < min_values) {
6✔
647
    n_args_error(*start, min_values, " at least", n_values);
3✔
648

2✔
649
    return parsing_failed;
650
  } else if (n_values > max_values) {
651
    n_args_error(*start, max_values, " at most", n_values);
32✔
652

5✔
653
    return parsing_failed;
2✔
654
  }
655

656
  const auto result = m_sink->consume(start + 1, end_of_values);
32✔
657
  if (result == parsing_failed) {
32✔
658
    log::error() << "Invalid value for " << *start << ": "
94✔
659
                 << shell_escape(*(start + 1));
61✔
660

661
    return result;
5✔
662
  }
663

664
  m_times_set++;
665
  return result + 1;
666
}
64✔
667

64✔
668
bool
669
argument::is_deprecated_alias(const std::string& key) const
64✔
670
{
32✔
671
  return std::find(m_deprecated_keys.begin(), m_deprecated_keys.end(), key) !=
672
         m_deprecated_keys.end();
32✔
673
}
2✔
674

675
///////////////////////////////////////////////////////////////////////////////
676
// sink
31✔
677

×
678
sink::sink(size_t n_values)
679
  : sink(n_values, n_values)
680
{
31✔
681
}
×
682

683
sink::sink(size_t min_values, size_t max_values)
684
  : m_min_values(min_values)
685
  , m_max_values(max_values)
686
{
93✔
687
}
31✔
688

6✔
689
std::string
5✔
690
sink::default_value() const
691
{
1✔
692
  AR_FAIL("sink::default_value not implemented");
693
}
694

30✔
695
std::string
30✔
696
sink::preprocess(std::string value) const
697
{
698
  if (m_preprocess) {
699
    m_preprocess(value);
38✔
700
  }
701

152✔
702
  return value;
114✔
703
}
704

705
///////////////////////////////////////////////////////////////////////////////
706
// bool_sink
707

708
bool_sink::bool_sink(bool* ptr)
207✔
709
  : sink(0)
207✔
710
  , m_sink(ptr)
711
{
712
  AR_REQUIRE(ptr);
713

223✔
714
  *m_sink = false;
223✔
715
}
446✔
716

717
std::string
718
bool_sink::value() const
719
{
720
  return *m_sink ? "on" : "off";
×
721
}
722

×
723
size_t
724
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
725
{
726
  AR_REQUIRE(start == end);
30✔
727
  *m_sink = true;
728

30✔
729
  return 0;
×
730
}
731

732
///////////////////////////////////////////////////////////////////////////////
30✔
733
// uint_sink
734

735
uint_sink::uint_sink(unsigned* ptr)
736
  : sink(1)
737
  , m_sink(ptr)
738
{
151✔
739
  AR_REQUIRE(ptr);
740

302✔
741
  *m_sink = m_default;
742
}
155✔
743

744
uint_sink&
150✔
745
uint_sink::with_default(unsigned value)
151✔
746
{
747
  set_has_default();
748
  *m_sink = m_default = value;
4✔
749

750
  return *this;
10✔
751
}
752

753
std::string
754
uint_sink::value() const
24✔
755
{
756
  return std::to_string(*m_sink);
52✔
757
}
23✔
758

759
std::string
23✔
760
uint_sink::default_value() const
761
{
762
  AR_REQUIRE(has_default());
763
  return std::to_string(m_default);
764
}
765

23✔
766
size_t
767
uint_sink::consume(string_vec_citer start, const string_vec_citer& end)
46✔
768
{
769
  AR_REQUIRE(end - start == 1);
27✔
770

771
  try {
22✔
772
    *m_sink = str_to_unsigned(preprocess(*start));
23✔
773

774
    return 1;
775
  } catch (const std::invalid_argument&) {
5✔
776
    return parsing_failed;
777
  }
5✔
778
}
5✔
779

780
///////////////////////////////////////////////////////////////////////////////
5✔
781
// double_sink
782

783
double_sink::double_sink(double* ptr)
784
  : sink(1)
3✔
785
  , m_sink(ptr)
786
{
3✔
787
  AR_REQUIRE(ptr);
788

789
  *m_sink = m_default;
790
}
2✔
791

792
double_sink&
2✔
793
double_sink::with_default(double value)
2✔
794
{
795
  set_has_default();
796
  *m_sink = m_default = value;
797

13✔
798
  return *this;
799
}
34✔
800

801
namespace {
11✔
802

49✔
803
std::string
804
double_to_string(double value)
6✔
805
{
5✔
806
  auto s = std::to_string(value);
5✔
807
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
5✔
808
  s.erase(s.find_last_not_of('.') + 1, std::string::npos);
809

810
  return s;
811
}
812

813
} // namespace
12✔
814

815
std::string
24✔
816
double_sink::default_value() const
817
{
16✔
818
  AR_REQUIRE(has_default());
819
  return double_to_string(m_default);
11✔
820
}
12✔
821

822
std::string
823
double_sink::value() const
2✔
824
{
825
  return double_to_string(*m_sink);
2✔
826
}
2✔
827

828
size_t
2✔
829
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
830
{
831
  AR_REQUIRE(end - start == 1);
832

833
  double value = 0;
834
  if (!to_double(preprocess(*start), value)) {
3✔
835
    return parsing_failed;
836
  }
3✔
837

3✔
838
  *m_sink = value;
3✔
839
  return 1;
840
}
3✔
841

×
842
///////////////////////////////////////////////////////////////////////////////
843
// str_sink
844

845
str_sink::str_sink(std::string* ptr)
846
  : sink(1)
×
847
  , m_sink(ptr ? ptr : &m_fallback_sink)
848
{
×
849
  AR_REQUIRE(m_sink);
×
850

851
  *m_sink = m_default;
852
}
853

3✔
854
str_sink&
855
str_sink::with_default(const char* value)
3✔
856
{
857
  set_has_default();
858
  *m_sink = m_default = value;
859

7✔
860
  return *this;
861
}
22✔
862

863
str_sink&
5✔
864
str_sink::with_default(const std::string& value)
25✔
865
{
866
  set_has_default();
867
  *m_sink = m_default = value;
868

3✔
869
  return *this;
3✔
870
}
871

872
str_sink&
873
str_sink::with_choices(const string_vec& choices)
874
{
875
  m_choices = choices;
21✔
876

877
  return *this;
84✔
878
}
879

21✔
880
std::string
881
str_sink::value() const
21✔
882
{
21✔
883
  return *m_sink;
884
}
885

3✔
886
std::string
887
str_sink::default_value() const
3✔
888
{
6✔
889
  AR_REQUIRE(has_default());
890
  return m_default;
3✔
891
}
892

893
size_t
894
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
2✔
895
{
896
  AR_REQUIRE(end - start == 1);
2✔
897

4✔
898
  if (m_choices.empty()) {
899
    *m_sink = preprocess(*start);
2✔
900
    return 1;
901
  } else {
902
    const auto value = preprocess(*start);
903
    const auto choice = to_lower(value);
8✔
904
    for (const auto& it : m_choices) {
905
      if (choice == to_lower(it)) {
8✔
906
        *m_sink = it;
907
        return 1;
8✔
908
      }
909
    }
910

911
    return parsing_failed;
3✔
912
  }
913
}
3✔
914

915
vec_sink::vec_sink(string_vec* ptr)
916
  : sink(1, std::numeric_limits<size_t>::max())
917
  , m_sink(ptr)
×
918
{
919
  AR_REQUIRE(ptr);
×
920

×
921
  m_sink->clear();
922
}
923

924
vec_sink&
10✔
925
vec_sink::with_min_values(size_t n)
926
{
28✔
927
  AR_REQUIRE(n <= max_values());
928
  set_min_values(n);
16✔
929

20✔
930
  return *this;
5✔
931
}
932

12✔
933
vec_sink&
3✔
934
vec_sink::with_max_values(size_t n)
42✔
935
{
27✔
936
  AR_REQUIRE(n >= min_values());
2✔
937
  set_max_values(n);
2✔
938

939
  return *this;
940
}
941

1✔
942
size_t
6✔
943
vec_sink::consume(string_vec_citer start, const string_vec_citer& end)
944
{
945
  AR_REQUIRE(static_cast<size_t>(end - start) >= min_values());
16✔
946
  AR_REQUIRE(static_cast<size_t>(end - start) <= max_values());
947
  for (auto it = start; it != end; ++it) {
32✔
948
    m_sink->emplace_back(preprocess(*it));
949
  }
20✔
950

951
  return static_cast<size_t>(end - start);
15✔
952
}
16✔
953

954
std::string
955
vec_sink::value() const
5✔
956
{
957
  std::string output;
10✔
958

5✔
959
  for (const auto& s : *m_sink) {
960
    if (!output.empty()) {
5✔
961
      output.push_back(';');
962
    }
963

964
    output.append(shell_escape(s));
3✔
965
  }
966

6✔
967
  return output;
3✔
968
}
969

3✔
970
} // namespace argparse
971

972
} // 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