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

MikkelSchubert / adapterremoval / #37

28 Jul 2024 09:48PM UTC coverage: 86.839% (-0.4%) from 87.26%
#37

push

travis-ci

MikkelSchubert
allow bool arguments without a sink variable

2151 of 2477 relevant lines covered (86.84%)

16470.77 hits per line

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

95.03
/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 " << join_text(choices, ", ", ", and ");
15✔
446
  }
447

448
  if (m_sink->has_default() && m_help.find("[default:") == std::string::npos) {
102✔
449
    ss << " [default: " << default_value() << "]";
8✔
450
  }
451

452
  return ss.str();
102✔
453
}
51✔
454

455
size_t
456
argument::min_values() const
92✔
457
{
458
  return m_sink->min_values();
276✔
459
}
460

461
size_t
462
argument::max_values() const
89✔
463
{
464
  return m_sink->max_values();
267✔
465
}
466

467
std::string
468
argument::value() const
3✔
469
{
470
  return m_sink->value();
6✔
471
}
472

473
std::string
474
argument::default_value() const
2✔
475
{
476
  return m_sink->default_value();
4✔
477
}
478

479
template<typename A, typename B>
480
A&
481
bind(std::unique_ptr<sink>& ptr, B* sink)
23✔
482
{
483
  ptr = std::make_unique<A>(sink);
69✔
484

485
  return static_cast<A&>(*ptr);
46✔
486
}
487

5✔
488
bool_sink&
489
argument::bind_bool(bool* ptr)
15✔
490
{
491
  return bind<bool_sink>(m_sink, ptr);
10✔
492
}
493

5✔
494
uint_sink&
495
argument::bind_uint(unsigned* ptr)
15✔
496
{
497
  return bind<uint_sink>(m_sink, ptr);
10✔
498
}
499

2✔
500
double_sink&
501
argument::bind_double(double* ptr)
6✔
502
{
503
  return bind<double_sink>(m_sink, ptr);
4✔
504
}
505

10✔
506
str_sink&
507
argument::bind_str(std::string* ptr)
30✔
508
{
509
  return bind<str_sink>(m_sink, ptr);
20✔
510
}
511

1✔
512
vec_sink&
513
argument::bind_vec(string_vec* ptr)
3✔
514
{
515
  return bind<vec_sink>(m_sink, ptr);
2✔
516
}
517

518
argument&
519
argument::abbreviation(char key)
1✔
520
{
521
  m_key_short.clear();
1✔
522
  m_key_short.push_back('-');
523
  m_key_short.push_back(key);
524

525
  return *this;
10✔
526
}
527

10✔
528
argument&
529
argument::deprecated_alias(const std::string& key)
530
{
531
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
2✔
532
  m_deprecated_keys.emplace_back(key);
533

2✔
534
  return *this;
535
}
536

537
argument&
5✔
538
argument::deprecated()
539
{
5✔
540
  m_deprecated = true;
541

542
  return hidden();
543
}
5✔
544

545
argument&
5✔
546
argument::hidden()
547
{
548
  m_hidden = true;
549

59✔
550
  return *this;
551
}
59✔
552

59✔
553
argument&
59✔
554
argument::depends_on(const std::string& key)
555
{
59✔
556
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
557
  m_depends_on.push_back(key);
558

559
  return *this;
3✔
560
}
561

9✔
562
argument&
3✔
563
argument::conflicts_with(const std::string& key)
564
{
3✔
565
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
566
  m_conflicts_with.push_back(key);
567

568
  return *this;
2✔
569
}
570

2✔
571
void
572
n_args_error(const std::string& key,
2✔
573
             size_t limit,
574
             const char* relation,
575
             size_t n)
576
{
4✔
577
  auto out = log::error();
578

4✔
579
  out << "Command-line argument " << key << " takes" << relation << " " << limit
580
      << " value";
4✔
581

582
  if (limit != 1) {
583
    out << "s";
584
  }
4✔
585

586
  if (n == 1) {
12✔
587
    out << ", but 1 value was provided!";
4✔
588
  } else {
589
    out << ", but " << n << " values were provided!";
4✔
590
  }
591
}
592

593
size_t
4✔
594
argument::parse(string_vec_citer start, const string_vec_citer& end)
595
{
12✔
596
  AR_REQUIRE(start != end);
4✔
597

598
  const bool deprecated_alias = is_deprecated_alias(*start);
4✔
599
  AR_REQUIRE(deprecated_alias || *start == m_key_long ||
600
             (!m_key_short.empty() && *start == m_key_short));
601

602
  if (m_deprecated) {
1✔
603
    log::warn() << "Option " << *start << " is deprecated and will "
604
                << "be removed in the future.";
605
  } else if (deprecated_alias) {
606
    log::warn() << "Option " << *start << " has been renamed to " << key()
607
                << ". Support for the old name will be removed in the future.";
1✔
608
  }
609

6✔
610
  if (m_times_set == 1) {
1✔
611
    log::warn() << "Command-line option " << key()
612
                << " has been specified more than once.";
1✔
613
  }
×
614

615
  double numeric_sink = 0;
616
  auto end_of_values = start + 1;
1✔
617
  for (; end_of_values != end; ++end_of_values) {
×
618
    if (end_of_values->size() > 1 && end_of_values->front() == '-' &&
619
        // Avoid confusing numeric values for command-line arguments
3✔
620
        !to_double(*end_of_values, numeric_sink)) {
621
      break;
2✔
622
    }
623
  }
624

33✔
625
  const auto min_values = m_sink->min_values();
626
  const auto max_values = m_sink->max_values();
66✔
627

628
  AR_REQUIRE(start < end_of_values);
66✔
629
  auto n_values = static_cast<size_t>(end_of_values - start - 1);
77✔
630

631
  if (n_values != min_values && min_values == max_values) {
632
    n_args_error(*start, min_values, "", n_values);
32✔
633

6✔
634
    return parsing_failed;
2✔
635
  } else if (n_values < min_values) {
31✔
636
    n_args_error(*start, min_values, " at least", n_values);
8✔
637

2✔
638
    return parsing_failed;
639
  } else if (n_values > max_values) {
640
    n_args_error(*start, max_values, " at most", n_values);
32✔
641

5✔
642
    return parsing_failed;
2✔
643
  }
644

645
  const auto result = m_sink->consume(start + 1, end_of_values);
32✔
646
  if (result == parsing_failed) {
32✔
647
    log::error() << "Invalid value for " << *start << ": "
94✔
648
                 << shell_escape(*(start + 1));
61✔
649

650
    return result;
5✔
651
  }
652

653
  m_times_set++;
654
  return result + 1;
655
}
64✔
656

64✔
657
bool
658
argument::is_deprecated_alias(const std::string& key) const
64✔
659
{
32✔
660
  return std::find(m_deprecated_keys.begin(), m_deprecated_keys.end(), key) !=
661
         m_deprecated_keys.end();
32✔
662
}
2✔
663

664
///////////////////////////////////////////////////////////////////////////////
665
// sink
31✔
666

×
667
sink::sink(size_t n_values)
668
  : sink(n_values, n_values)
669
{
31✔
670
}
×
671

672
sink::sink(size_t min_values, size_t max_values)
673
  : m_min_values(min_values)
674
  , m_max_values(max_values)
675
{
93✔
676
}
31✔
677

6✔
678
std::string
5✔
679
sink::default_value() const
680
{
1✔
681
  AR_FAIL("sink::default_value not implemented");
682
}
683

30✔
684
std::string
30✔
685
sink::preprocess(std::string value) const
686
{
687
  if (m_preprocess) {
688
    m_preprocess(value);
38✔
689
  }
690

152✔
691
  return value;
114✔
692
}
693

694
///////////////////////////////////////////////////////////////////////////////
695
// bool_sink
696

697
bool_sink::bool_sink(bool* ptr)
206✔
698
  : sink(0)
206✔
699
  , m_sink(ptr ? ptr : &m_fallback_sink)
700
{
701
  *m_sink = false;
702
}
222✔
703

222✔
704
std::string
444✔
705
bool_sink::value() const
706
{
707
  return *m_sink ? "on" : "off";
708
}
709

×
710
size_t
711
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
712
{
713
  AR_REQUIRE(start == end);
714
  *m_sink = true;
715

30✔
716
  return 0;
717
}
30✔
718

×
719
///////////////////////////////////////////////////////////////////////////////
720
// uint_sink
721

30✔
722
uint_sink::uint_sink(unsigned* ptr)
723
  : sink(1)
724
  , m_sink(ptr)
725
{
726
  AR_REQUIRE(ptr);
727

150✔
728
  *m_sink = m_default;
729
}
300✔
730

731
uint_sink&
150✔
732
uint_sink::with_default(unsigned value)
5✔
733
{
734
  set_has_default();
735
  *m_sink = m_default = value;
4✔
736

737
  return *this;
10✔
738
}
739

740
std::string
741
uint_sink::value() const
24✔
742
{
743
  return std::to_string(*m_sink);
52✔
744
}
23✔
745

746
std::string
23✔
747
uint_sink::default_value() const
748
{
749
  AR_REQUIRE(has_default());
750
  return std::to_string(m_default);
751
}
752

23✔
753
size_t
754
uint_sink::consume(string_vec_citer start, const string_vec_citer& end)
46✔
755
{
756
  AR_REQUIRE(end - start == 1);
27✔
757

758
  try {
22✔
759
    *m_sink = str_to_unsigned(preprocess(*start));
23✔
760

761
    return 1;
762
  } catch (const std::invalid_argument&) {
5✔
763
    return parsing_failed;
764
  }
5✔
765
}
5✔
766

767
///////////////////////////////////////////////////////////////////////////////
5✔
768
// double_sink
769

770
double_sink::double_sink(double* ptr)
771
  : sink(1)
3✔
772
  , m_sink(ptr)
773
{
3✔
774
  AR_REQUIRE(ptr);
775

776
  *m_sink = m_default;
777
}
2✔
778

779
double_sink&
2✔
780
double_sink::with_default(double value)
2✔
781
{
782
  set_has_default();
783
  *m_sink = m_default = value;
784

13✔
785
  return *this;
786
}
34✔
787

788
namespace {
11✔
789

49✔
790
std::string
791
double_to_string(double value)
6✔
792
{
5✔
793
  auto s = std::to_string(value);
5✔
794
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
5✔
795
  s.erase(s.find_last_not_of('.') + 1, std::string::npos);
796

797
  return s;
798
}
799

800
} // namespace
12✔
801

802
std::string
24✔
803
double_sink::default_value() const
804
{
16✔
805
  AR_REQUIRE(has_default());
806
  return double_to_string(m_default);
11✔
807
}
12✔
808

809
std::string
810
double_sink::value() const
2✔
811
{
812
  return double_to_string(*m_sink);
2✔
813
}
2✔
814

815
size_t
2✔
816
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
817
{
818
  AR_REQUIRE(end - start == 1);
819

820
  double value = 0;
821
  if (!to_double(preprocess(*start), value)) {
3✔
822
    return parsing_failed;
823
  }
3✔
824

3✔
825
  *m_sink = value;
3✔
826
  return 1;
827
}
3✔
828

×
829
///////////////////////////////////////////////////////////////////////////////
830
// str_sink
831

832
str_sink::str_sink(std::string* ptr)
833
  : sink(1)
×
834
  , m_sink(ptr ? ptr : &m_fallback_sink)
835
{
×
836
  AR_REQUIRE(m_sink);
×
837

838
  *m_sink = m_default;
839
}
840

3✔
841
str_sink&
842
str_sink::with_default(const char* value)
3✔
843
{
844
  set_has_default();
845
  *m_sink = m_default = value;
846

7✔
847
  return *this;
848
}
22✔
849

850
str_sink&
5✔
851
str_sink::with_default(const std::string& value)
25✔
852
{
853
  set_has_default();
854
  *m_sink = m_default = value;
855

3✔
856
  return *this;
3✔
857
}
858

859
str_sink&
860
str_sink::with_choices(const string_vec& choices)
861
{
862
  m_choices = choices;
21✔
863

864
  return *this;
84✔
865
}
866

21✔
867
std::string
868
str_sink::value() const
21✔
869
{
21✔
870
  return *m_sink;
871
}
872

3✔
873
std::string
874
str_sink::default_value() const
3✔
875
{
6✔
876
  AR_REQUIRE(has_default());
877
  return m_default;
3✔
878
}
879

880
size_t
881
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
2✔
882
{
883
  AR_REQUIRE(end - start == 1);
2✔
884

4✔
885
  if (m_choices.empty()) {
886
    *m_sink = preprocess(*start);
2✔
887
    return 1;
888
  } else {
889
    const auto value = preprocess(*start);
890
    const auto choice = to_lower(value);
8✔
891
    for (const auto& it : m_choices) {
892
      if (choice == to_lower(it)) {
8✔
893
        *m_sink = it;
894
        return 1;
8✔
895
      }
896
    }
897

898
    return parsing_failed;
3✔
899
  }
900
}
3✔
901

902
vec_sink::vec_sink(string_vec* ptr)
903
  : sink(1, std::numeric_limits<size_t>::max())
904
  , m_sink(ptr)
×
905
{
906
  AR_REQUIRE(ptr);
×
907

×
908
  m_sink->clear();
909
}
910

911
vec_sink&
10✔
912
vec_sink::with_min_values(size_t n)
913
{
28✔
914
  AR_REQUIRE(n <= max_values());
915
  set_min_values(n);
16✔
916

20✔
917
  return *this;
5✔
918
}
919

12✔
920
vec_sink&
3✔
921
vec_sink::with_max_values(size_t n)
42✔
922
{
27✔
923
  AR_REQUIRE(n >= min_values());
2✔
924
  set_max_values(n);
2✔
925

926
  return *this;
927
}
928

1✔
929
size_t
6✔
930
vec_sink::consume(string_vec_citer start, const string_vec_citer& end)
931
{
932
  AR_REQUIRE(static_cast<size_t>(end - start) >= min_values());
16✔
933
  AR_REQUIRE(static_cast<size_t>(end - start) <= max_values());
934
  for (auto it = start; it != end; ++it) {
32✔
935
    m_sink->emplace_back(preprocess(*it));
936
  }
20✔
937

938
  return static_cast<size_t>(end - start);
15✔
939
}
16✔
940

941
std::string
942
vec_sink::value() const
5✔
943
{
944
  std::string output;
10✔
945

5✔
946
  for (const auto& s : *m_sink) {
947
    if (!output.empty()) {
5✔
948
      output.push_back(';');
949
    }
950

951
    output.append(shell_escape(s));
3✔
952
  }
953

6✔
954
  return output;
3✔
955
}
956

3✔
957
} // namespace argparse
958

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