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

MikkelSchubert / adapterremoval / #39

20 Aug 2024 01:31PM UTC coverage: 79.602% (-3.8%) from 83.365%
#39

push

travis-ci

MikkelSchubert
update schema URL to use github pages

2279 of 2863 relevant lines covered (79.6%)

14257.45 hits per line

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

93.7
/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)) {
104✔
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
bool
57
to_double(const std::string& value, double& out)
5✔
58
{
59
  std::istringstream stream(value);
5✔
60
  if (!(stream >> out)) {
15✔
61
    return false;
62
  }
63

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

69
} // namespace
70

71
///////////////////////////////////////////////////////////////////////////////
72

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

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

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

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

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

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

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

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

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

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

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

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

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

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

164
  return result;
165
}
166

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

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

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

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

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

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

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

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

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

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

218
  auto cerr = log::cerr();
12✔
219
  if (!m_preamble.empty()) {
24✔
220
    cli_formatter fmt;
6✔
221
    fmt.set_indent(0);
6✔
222
    fmt.set_indent_first_line(false);
6✔
223
    fmt.set_column_width(m_terminal_width);
6✔
224

225
    cerr << "\n";
6✔
226
    for (auto line : split_lines(m_preamble)) {
54✔
227
      if (starts_with(line, " ")) {
18✔
228
        cerr << line << "\n";
6✔
229
      } else {
230
        cerr << fmt.format(line) << "\n";
30✔
231
      }
232
    }
12✔
233
  }
234

235
  string_vec signatures;
12✔
236

237
  size_t indentation = 0;
12✔
238
  for (const auto& entry : m_args) {
376✔
239
    if (entry.argument && !entry.argument->is_hidden()) {
256✔
240
      const auto& arg = *entry.argument;
88✔
241

242
      std::ostringstream ss;
44✔
243
      if (arg.short_key().empty()) {
132✔
244
        ss << "   " << arg.key();
40✔
245
      } else {
246
        ss << "   " << arg.short_key() << ", " << arg.key();
96✔
247
      }
248

249
      for (size_t i = 0; i < arg.min_values(); ++i) {
50✔
250
        ss << " <" << arg.metavar() << ">";
18✔
251
      }
252

253
      if (arg.max_values() == std::numeric_limits<size_t>::max()) {
44✔
254
        ss << " [" << arg.metavar() << ", ...]";
6✔
255
      } else {
256
        for (size_t i = arg.min_values(); i < arg.max_values(); ++i) {
87✔
257
          ss << " [" << arg.metavar() << "]";
9✔
258
        }
259
      }
260

261
      indentation = std::max<size_t>(indentation, ss.str().size() + 3);
220✔
262
      signatures.emplace_back(ss.str());
132✔
263
    } else {
44✔
264
      signatures.emplace_back();
38✔
265
    }
266
  }
267

268
  cli_formatter fmt;
12✔
269
  fmt.set_indent(indentation);
12✔
270
  fmt.set_indent_first_line(false);
12✔
271
  fmt.set_column_width(m_terminal_width - indentation);
12✔
272

273
  for (size_t i = 0; i < m_args.size(); i++) {
188✔
274
    const auto& entry = m_args.at(i);
164✔
275
    if (entry.argument) {
164✔
276
      if (!entry.argument->is_hidden()) {
138✔
277
        const auto& arg = *entry.argument;
88✔
278
        const auto& signature = signatures.at(i);
88✔
279

280
        cerr << signature;
44✔
281

282
        const std::string help = arg.help();
44✔
283
        if (!help.empty()) {
88✔
284
          // Format into columns and indent lines (except the first line)
285
          cerr << std::string(indentation - signature.length(), ' ')
168✔
286
               << fmt.format(help);
168✔
287
        }
288

289
        cerr << "\n";
88✔
290
      }
44✔
291
    } else {
292
      cerr << entry.header << "\n";
154✔
293
    }
294
  }
295
}
24✔
296

297
void
298
parser::print_licenses() const
1✔
299
{
300
  auto cerr = log::cerr();
1✔
301

302
  for (const auto& block : split_lines(m_licenses)) {
15✔
303
    if (block.empty()) {
6✔
304
      cerr << "\n";
3✔
305
    } else {
306
      size_t indentation = block.find_first_not_of(' ');
3✔
307
      if (indentation == std::string::npos) {
3✔
308
        indentation = 0;
×
309
      }
310

311
      size_t ljust = 0;
3✔
312
      if (!block.empty() && block.at(indentation) == '*') {
9✔
313
        ljust = 2;
314
      } else if (block.size() > indentation + 1 &&
6✔
315
                 block.at(indentation + 1) == '.') {
6✔
316
        ljust = 3;
317
      }
318

319
      const auto width = 80 - indentation;
3✔
320
      for (const auto& line : wrap_text(block, width, ljust)) {
24✔
321
        cerr << std::string(indentation, ' ') << line << "\n";
18✔
322
      }
3✔
323
    }
324
  }
1✔
325
}
2✔
326

327
void
328
parser::set_terminal_width(unsigned w)
6✔
329
{
330
  m_terminal_width = w;
6✔
331
}
6✔
332

333
void
334
parser::update_argument_map()
21✔
335
{
336
  m_keys.clear();
21✔
337

338
  for (auto& it : m_args) {
680✔
339
    if (it.argument) {
298✔
340
      for (const auto& key : it.argument->keys()) {
814✔
341
        const auto result = m_keys.emplace(key, it.argument);
128✔
342
        AR_REQUIRE(result.second, shell_escape(key));
128✔
343
      }
86✔
344
    }
345
  }
346

347
  bool any_errors = false;
21✔
348
  for (const auto& it : m_args) {
680✔
349
    if (it.argument) {
298✔
350
      for (const auto& key : it.argument->conflicts_with()) {
524✔
351
        if (!m_keys.count(key)) {
4✔
352
          any_errors = true;
×
353
          log::error() << it.argument->key() << " conflicts with "
×
354
                       << "unknown command-line option " << key;
×
355
        }
356
      }
357

358
      for (const auto& key : it.argument->depends_on()) {
524✔
359
        if (!m_keys.count(key)) {
4✔
360
          any_errors = true;
×
361
          log::error() << it.argument->key() << " requires "
×
362
                       << "unknown command-line option " << key;
×
363
        }
364
      }
365
    }
366
  }
367

368
  AR_REQUIRE(!any_errors, "bugs in argument parsing");
21✔
369
}
21✔
370

371
argument_ptr
372
parser::find_argument(const std::string& key)
25✔
373
{
374
  auto it = m_keys.find(key);
25✔
375
  if (it != m_keys.end()) {
75✔
376
    return it->second;
38✔
377
  }
378

379
  if (key.size() > 1 && key.front() == '-' && key.back() != '-') {
23✔
380
    string_vec candidates;
5✔
381
    const size_t max_distance = 1 + key.size() / 4;
5✔
382

383
    for (const auto& arg : m_args) {
164✔
384
      if (arg.argument) {
72✔
385
        for (const auto& name : arg.argument->keys()) {
229✔
386

387
          if (is_similar_argument(key, name, max_distance)) {
31✔
388
            candidates.push_back(name);
7✔
389
          }
390
        }
21✔
391
      }
392
    }
393

394
    auto error = log::error();
5✔
395
    error << "Unknown argument '" << key << "'";
15✔
396

397
    if (!candidates.empty()) {
10✔
398
      std::sort(candidates.begin(), candidates.end());
12✔
399

400
      error << ". Did you mean " << candidates.front();
12✔
401
      for (size_t i = 1; i < candidates.size() - 1; ++i) {
10✔
402
        error << ", " << candidates.at(i);
4✔
403
      }
404

405
      if (candidates.size() > 1) {
8✔
406
        error << " or " << candidates.back();
6✔
407
      }
408

409
      error << "?";
4✔
410
    }
411
  } else {
5✔
412
    log::error() << "Unexpected positional argument '" << key << "'";
5✔
413
  }
414

415
  return {};
31✔
416
}
417

418
///////////////////////////////////////////////////////////////////////////////
419

420
argument::argument(const std::string& key, std::string metavar)
144✔
421
  : m_key_long(key)
144✔
422
  , m_metavar(std::move(metavar))
144✔
423
  , m_sink(std::make_unique<bool_sink>(&m_default_sink))
1,440✔
424
{
425
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
432✔
426
}
144✔
427

428
argument&
429
argument::help(const std::string& text)
99✔
430
{
431
  m_help = text;
99✔
432

433
  return *this;
99✔
434
}
435

436
string_vec
437
argument::keys() const
110✔
438
{
439
  string_vec keys = m_deprecated_keys;
110✔
440
  keys.push_back(m_key_long);
110✔
441

442
  if (!m_key_short.empty()) {
220✔
443
    keys.push_back(m_key_short);
53✔
444
  }
445

446
  return keys;
110✔
447
}
×
448

449
std::string
450
argument::help() const
51✔
451
{
452
  // Append string representation of current (default) value
453
  std::ostringstream ss(m_help);
51✔
454
  ss << m_help;
51✔
455

456
  const auto choices = m_sink->choices();
102✔
457
  if (!choices.empty()) {
102✔
458
    ss << ". Possible values are " << join_text(choices, ", ", ", and ");
15✔
459
  }
460

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

465
  return ss.str();
102✔
466
}
51✔
467

468
size_t
469
argument::min_values() const
92✔
470
{
471
  return m_sink->min_values();
276✔
472
}
473

474
size_t
475
argument::max_values() const
89✔
476
{
477
  return m_sink->max_values();
267✔
478
}
479

480
std::string
481
argument::value() const
3✔
482
{
483
  return m_sink->value();
6✔
484
}
485

486
std::string
487
argument::default_value() const
2✔
488
{
489
  return m_sink->default_value();
4✔
490
}
491

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

498
  return static_cast<A&>(*ptr);
46✔
499
}
500

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

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

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

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

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

531
argument&
532
argument::abbreviation(char key)
1✔
533
{
534
  m_key_short.clear();
1✔
535
  m_key_short.push_back('-');
536
  m_key_short.push_back(key);
537

538
  return *this;
10✔
539
}
540

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

2✔
547
  return *this;
548
}
549

550
argument&
5✔
551
argument::deprecated()
552
{
5✔
553
  m_deprecated = true;
554

555
  return hidden();
556
}
5✔
557

558
argument&
5✔
559
argument::hidden()
560
{
561
  m_hidden = true;
562

59✔
563
  return *this;
564
}
59✔
565

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

572
  return *this;
3✔
573
}
574

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

581
  return *this;
2✔
582
}
583

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

4✔
592
  out << "Command-line argument " << key << " takes" << relation << " " << limit
593
      << " value";
4✔
594

595
  if (limit != 1) {
596
    out << "s";
597
  }
4✔
598

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

606
size_t
4✔
607
argument::parse(string_vec_citer start, const string_vec_citer& end)
608
{
12✔
609
  AR_REQUIRE(start != end);
4✔
610

611
  const bool deprecated_alias = is_deprecated_alias(*start);
4✔
612
  AR_REQUIRE(deprecated_alias || *start == m_key_long ||
613
             (!m_key_short.empty() && *start == m_key_short));
614

615
  if (m_deprecated) {
1✔
616
    log::warn() << "Option " << *start << " is deprecated and will "
617
                << "be removed in the future.";
618
  } else if (deprecated_alias) {
619
    log::warn() << "Option " << *start << " has been renamed to " << key()
620
                << ". Support for the old name will be removed in the future.";
1✔
621
  }
622

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

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

33✔
638
  const auto min_values = m_sink->min_values();
639
  const auto max_values = m_sink->max_values();
66✔
640

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

644
  if (n_values != min_values && min_values == max_values) {
645
    n_args_error(*start, min_values, "", n_values);
32✔
646

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

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

5✔
655
    return parsing_failed;
2✔
656
  }
657

658
  size_t result = parsing_failed;
32✔
659
  std::string error_message;
32✔
660

94✔
661
  try {
61✔
662
    result = m_sink->consume(start + 1, end_of_values);
663
  } catch (const std::invalid_argument& error) {
5✔
664
    error_message = error.what();
665
  }
666

667
  if (result == parsing_failed) {
668
    auto error = log::error();
64✔
669

64✔
670
    error << "Invalid command-line argument " << *start;
671
    for (auto it = start + 1; it != end_of_values; ++it) {
64✔
672
      error << " " << shell_escape(*it);
32✔
673
    }
674

32✔
675
    if (!error_message.empty()) {
2✔
676
      error << ": " << error_message;
677
    }
678

31✔
679
    return result;
×
680
  } else if (result == invalid_choice) {
681
    auto error = log::error();
682

31✔
683
    error << "Invalid command-line argument " << *start;
×
684
    for (auto it = start + 1; it != end_of_values; ++it) {
685
      error << " " << shell_escape(*it);
686
    }
687

688
    error << ". Valid values for " << *start << " are "
31✔
689
          << join_text(m_sink->choices(), ", ", ", and ");
31✔
690
  }
691

31✔
692
  m_times_set++;
93✔
693
  return result + 1;
1✔
694
}
1✔
695

×
696
bool
697
argument::is_deprecated_alias(const std::string& key) const
31✔
698
{
1✔
699
  return std::find(m_deprecated_keys.begin(), m_deprecated_keys.end(), key) !=
700
         m_deprecated_keys.end();
3✔
701
}
6✔
702

5✔
703
///////////////////////////////////////////////////////////////////////////////
704
// sink
705

2✔
706
sink::sink(size_t n_values)
2✔
707
  : sink(n_values, n_values)
708
{
709
}
1✔
710

31✔
711
sink::sink(size_t min_values, size_t max_values)
×
712
  : m_min_values(min_values)
713
  , m_max_values(max_values)
×
714
{
×
715
}
×
716

717
std::string
718
sink::default_value() const
×
719
{
×
720
  AR_FAIL("sink::default_value not implemented");
721
}
722

30✔
723
std::string
30✔
724
sink::preprocess(std::string value) const
32✔
725
{
726
  if (m_preprocess) {
727
    m_preprocess(value);
38✔
728
  }
729

152✔
730
  return value;
114✔
731
}
732

733
///////////////////////////////////////////////////////////////////////////////
734
// bool_sink
735

736
bool_sink::bool_sink(bool* ptr)
206✔
737
  : sink(0)
×
738
  , m_sink(ptr ? ptr : &m_fallback_sink)
739
{
740
  *m_sink = false;
741
}
222✔
742

222✔
743
std::string
444✔
744
bool_sink::value() const
745
{
746
  return *m_sink ? "on" : "off";
747
}
748

×
749
size_t
750
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
751
{
752
  AR_REQUIRE(start == end);
753
  *m_sink = true;
754

25✔
755
  return 0;
756
}
25✔
757

×
758
///////////////////////////////////////////////////////////////////////////////
759
// uint_sink
760

25✔
761
uint_sink::uint_sink(unsigned* ptr)
762
  : sink(1)
763
  , m_sink(ptr)
764
{
765
  AR_REQUIRE(ptr);
766

150✔
767
  *m_sink = m_default;
768
}
300✔
769

770
uint_sink&
150✔
771
uint_sink::with_default(unsigned value)
5✔
772
{
773
  set_has_default();
774
  *m_sink = m_default = value;
4✔
775

776
  return *this;
10✔
777
}
778

779
std::string
780
uint_sink::value() const
24✔
781
{
782
  return std::to_string(*m_sink);
52✔
783
}
23✔
784

785
std::string
23✔
786
uint_sink::default_value() const
787
{
788
  AR_REQUIRE(has_default());
789
  return std::to_string(m_default);
790
}
791

23✔
792
size_t
793
uint_sink::consume(string_vec_citer start, const string_vec_citer& end)
46✔
794
{
795
  AR_REQUIRE(end - start == 1);
27✔
796

797
  *m_sink = str_to_unsigned(preprocess(*start));
22✔
798

23✔
799
  return 1;
800
}
801

5✔
802
///////////////////////////////////////////////////////////////////////////////
803
// double_sink
5✔
804

5✔
805
double_sink::double_sink(double* ptr)
806
  : sink(1)
5✔
807
  , m_sink(ptr)
808
{
809
  AR_REQUIRE(ptr);
810

3✔
811
  *m_sink = m_default;
812
}
3✔
813

814
double_sink&
815
double_sink::with_default(double value)
816
{
2✔
817
  set_has_default();
818
  *m_sink = m_default = value;
2✔
819

2✔
820
  return *this;
821
}
822

823
namespace {
13✔
824

825
std::string
34✔
826
double_to_string(double value)
827
{
55✔
828
  auto s = std::to_string(value);
829
  s.erase(s.find_last_not_of('0') + 1, std::string::npos);
6✔
830
  s.erase(s.find_last_not_of('.') + 1, std::string::npos);
831

832
  return s;
833
}
834

835
} // namespace
12✔
836

837
std::string
24✔
838
double_sink::default_value() const
839
{
16✔
840
  AR_REQUIRE(has_default());
841
  return double_to_string(m_default);
11✔
842
}
12✔
843

844
std::string
845
double_sink::value() const
2✔
846
{
847
  return double_to_string(*m_sink);
2✔
848
}
2✔
849

850
size_t
2✔
851
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
852
{
853
  AR_REQUIRE(end - start == 1);
854

855
  double value = 0;
856
  std::istringstream stream(*start);
3✔
857
  if (!(stream >> value)) {
858
    throw std::invalid_argument("not a valid number");
3✔
859
  }
3✔
860

3✔
861
  char trailing = 0;
862
  if (stream >> trailing) {
3✔
863
    throw std::invalid_argument("number contains trailing text");
×
864
  }
865

866
  *m_sink = value;
867
  return 1;
868
}
×
869

870
///////////////////////////////////////////////////////////////////////////////
×
871
// str_sink
×
872

873
str_sink::str_sink(std::string* ptr)
874
  : sink(1)
875
  , m_sink(ptr ? ptr : &m_fallback_sink)
3✔
876
{
877
  AR_REQUIRE(m_sink);
3✔
878

879
  *m_sink = m_default;
880
}
881

7✔
882
str_sink&
883
str_sink::with_default(const char* value)
22✔
884
{
885
  set_has_default();
5✔
886
  *m_sink = m_default = value;
10✔
887

15✔
888
  return *this;
1✔
889
}
890

891
str_sink&
4✔
892
str_sink::with_default(const std::string& value)
8✔
893
{
1✔
894
  set_has_default();
895
  *m_sink = m_default = value;
896

3✔
897
  return *this;
3✔
898
}
5✔
899

900
str_sink&
901
str_sink::with_choices(const string_vec& choices)
902
{
903
  m_choices = choices;
21✔
904

905
  return *this;
84✔
906
}
907

21✔
908
std::string
909
str_sink::value() const
21✔
910
{
21✔
911
  return *m_sink;
912
}
913

3✔
914
std::string
915
str_sink::default_value() const
3✔
916
{
6✔
917
  AR_REQUIRE(has_default());
918
  return m_default;
3✔
919
}
920

921
size_t
922
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
2✔
923
{
924
  AR_REQUIRE(end - start == 1);
2✔
925

4✔
926
  if (m_choices.empty()) {
927
    *m_sink = preprocess(*start);
2✔
928
    return 1;
929
  } else {
930
    const auto value = preprocess(*start);
931
    const auto choice = to_lower(value);
8✔
932
    for (const auto& it : m_choices) {
933
      if (choice == to_lower(it)) {
8✔
934
        *m_sink = it;
935
        return 1;
8✔
936
      }
937
    }
938

939
    return invalid_choice;
3✔
940
  }
941
}
3✔
942

943
vec_sink::vec_sink(string_vec* ptr)
944
  : sink(1, std::numeric_limits<size_t>::max())
945
  , m_sink(ptr)
×
946
{
947
  AR_REQUIRE(ptr);
×
948

×
949
  m_sink->clear();
950
}
951

952
vec_sink&
10✔
953
vec_sink::with_min_values(size_t n)
954
{
28✔
955
  AR_REQUIRE(n <= max_values());
956
  set_min_values(n);
16✔
957

20✔
958
  return *this;
5✔
959
}
960

12✔
961
vec_sink&
6✔
962
vec_sink::with_max_values(size_t n)
42✔
963
{
36✔
964
  AR_REQUIRE(n >= min_values());
2✔
965
  set_max_values(n);
2✔
966

967
  return *this;
968
}
969

1✔
970
size_t
6✔
971
vec_sink::consume(string_vec_citer start, const string_vec_citer& end)
972
{
973
  AR_REQUIRE(static_cast<size_t>(end - start) >= min_values());
16✔
974
  AR_REQUIRE(static_cast<size_t>(end - start) <= max_values());
975
  for (auto it = start; it != end; ++it) {
32✔
976
    m_sink->emplace_back(preprocess(*it));
977
  }
20✔
978

979
  return static_cast<size_t>(end - start);
15✔
980
}
16✔
981

982
std::string
983
vec_sink::value() const
5✔
984
{
985
  std::string output;
10✔
986

5✔
987
  for (const auto& s : *m_sink) {
988
    if (!output.empty()) {
5✔
989
      output.push_back(';');
990
    }
991

992
    output.append(shell_escape(s));
3✔
993
  }
994

6✔
995
  return output;
3✔
996
}
997

3✔
998
} // namespace argparse
999

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