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

MikkelSchubert / adapterremoval / #46

27 Nov 2024 03:10PM UTC coverage: 27.245% (+1.0%) from 26.244%
#46

push

travis-ci

MikkelSchubert
fix convenience executable make target

2609 of 9576 relevant lines covered (27.25%)

4268.73 hits per line

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

92.09
/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()
203✔
61
{
62
  add_header("OPTIONS:");
58✔
63

64
  // Built-in arguments
65
  add("--help").abbreviation('h').help("Display this message.");
116✔
66
  add("--version").abbreviation('v').help("Print the version string.");
116✔
67
  add("--licenses").help("Print licenses for this software.");
116✔
68
  add_separator();
29✔
69
}
29✔
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)
118✔
174
{
175
  AR_REQUIRE(starts_with(name, "--"), name);
354✔
176
  auto ptr = std::make_shared<argument>(name, metavar);
118✔
177
  m_args.push_back({ std::string(), ptr });
472✔
178

179
  return *ptr;
236✔
180
}
118✔
181

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

188
void
189
parser::add_header(const std::string& header)
29✔
190
{
191
  add_separator();
29✔
192
  m_args.push_back({ header, argument_ptr() });
58✔
193
}
29✔
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)
144✔
409
  : m_key_long(key)
144✔
410
  , m_metavar(std::move(metavar))
144✔
411
  , m_sink(std::make_unique<bool_sink>(&m_default_sink))
1,296✔
412
{
413
  AR_REQUIRE(!key.empty() && key.at(0) == '-', shell_escape(key));
288✔
414
}
144✔
415

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

421
  return *this;
99✔
422
}
423

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

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

434
  return keys;
110✔
435
}
×
436

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

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

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

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

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

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

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

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

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

486
  return static_cast<A&>(*ptr);
23✔
487
}
488

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

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

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

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

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

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

528
  return *this;
10✔
529
}
530

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

537
  return *this;
538
}
5✔
539

540
argument&
5✔
541
argument::deprecated()
542
{
543
  m_deprecated = true;
544

5✔
545
  return hidden();
546
}
5✔
547

548
argument&
549
argument::hidden()
550
{
59✔
551
  m_hidden = true;
552

553
  return *this;
59✔
554
}
59✔
555

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

562
  return *this;
3✔
563
}
564

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

571
  return *this;
2✔
572
}
573

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

4✔
582
  out << "Command-line argument " << key << " takes" << relation << " " << limit
583
      << " value";
4✔
584

585
  if (limit != 1) {
586
    out << "s";
587
  }
4✔
588

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

596
size_t
4✔
597
argument::parse(string_vec_citer start, const string_vec_citer& end)
598
{
8✔
599
  AR_REQUIRE(start != end);
4✔
600

601
  const bool deprecated_alias = is_deprecated_alias(*start);
4✔
602
  AR_REQUIRE(deprecated_alias || *start == m_key_long ||
603
             (!m_key_short.empty() && *start == m_key_short));
604

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

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

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

66✔
630
  const auto min_values = m_sink->min_values();
631
  const auto max_values = m_sink->max_values();
66✔
632

77✔
633
  AR_REQUIRE(start < end_of_values);
634
  auto n_values = static_cast<size_t>(end_of_values - start - 1);
635

32✔
636
  if (n_values != min_values && min_values == max_values) {
6✔
637
    n_args_error(*start, min_values, "", n_values);
2✔
638

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

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

647
    return parsing_failed;
648
  }
32✔
649

94✔
650
  size_t result = parsing_failed;
42✔
651
  std::string error_message;
652

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

659
  if (result == parsing_failed) {
660
    auto error = log::error();
64✔
661

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

32✔
667
    if (!error_message.empty()) {
2✔
668
      error << ": " << error_message;
669
    }
670

31✔
671
    return result;
×
672
  } else if (result == invalid_choice) {
673
    auto error = log::error();
674

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

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

31✔
684
  m_times_set++;
93✔
685
  return result + 1;
1✔
686
}
1✔
687

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

6✔
695
///////////////////////////////////////////////////////////////////////////////
696
// sink
697

2✔
698
sink::sink(size_t n_values)
2✔
699
  : sink(n_values, n_values)
700
{
701
}
1✔
702

31✔
703
sink::sink(size_t min_values, size_t max_values)
×
704
  : m_min_values(min_values)
705
  , m_max_values(max_values)
×
706
{
×
707
}
×
708

709
std::string
710
sink::default_value() const
×
711
{
×
712
  AR_FAIL("sink::default_value not implemented");
713
}
714

30✔
715
std::string
30✔
716
sink::preprocess(std::string value) const
32✔
717
{
718
  if (m_preprocess) {
719
    m_preprocess(value);
38✔
720
  }
721

152✔
722
  return value;
114✔
723
}
724

725
///////////////////////////////////////////////////////////////////////////////
726
// bool_sink
727

728
bool_sink::bool_sink(bool* ptr)
219✔
729
  : sink(0)
219✔
730
  , m_sink(ptr ? ptr : &m_fallback_sink)
731
{
219✔
732
  *m_sink = false;
733
}
235✔
734

235✔
735
std::string
470✔
736
bool_sink::value() const
737
{
235✔
738
  return *m_sink ? "on" : "off";
739
}
740

×
741
size_t
742
bool_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
743
{
744
  AR_REQUIRE(start == end);
745
  *m_sink = true;
746

35✔
747
  return 0;
748
}
35✔
749

×
750
///////////////////////////////////////////////////////////////////////////////
751
// u32_sink
752

35✔
753
u32_sink::u32_sink(uint32_t* ptr)
754
  : sink(1)
755
  , m_sink(ptr)
756
  , m_minimum(std::numeric_limits<decltype(m_minimum)>::lowest())
757
  , m_maximum(std::numeric_limits<decltype(m_maximum)>::max())
758
{
150✔
759
  AR_REQUIRE(ptr);
760

150✔
761
  *m_sink = m_default;
762
}
150✔
763

150✔
764
u32_sink&
765
u32_sink::with_default(uint32_t value)
766
{
4✔
767
  AR_REQUIRE(value >= m_minimum && value <= m_maximum);
768
  set_has_default();
10✔
769
  *m_sink = m_default = value;
770

771
  return *this;
772
}
24✔
773

774
std::string
55✔
775
u32_sink::value() const
23✔
776
{
777
  return std::to_string(*m_sink);
23✔
778
}
779

780
std::string
781
u32_sink::default_value() const
782
{
783
  AR_REQUIRE(has_default());
35✔
784
  return std::to_string(m_default);
785
}
35✔
786

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

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

803
size_t
804
u32_sink::consume(string_vec_citer start, const string_vec_citer& end)
805
{
3✔
806
  AR_REQUIRE(end - start == 1);
807

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

817
  *m_sink = value;
818
  return 1;
8✔
819
}
820

15✔
821
///////////////////////////////////////////////////////////////////////////////
7✔
822
// double_sink
7✔
823

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

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

3✔
841
  return *this;
3✔
842
}
13✔
843

3✔
844
namespace {
3✔
845

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

853
  return s;
854
}
12✔
855

856
} // namespace
12✔
857

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

865
double_sink&
2✔
866
double_sink::with_minimum(double value)
867
{
2✔
868
  AR_REQUIRE(m_default >= value && value >= m_minimum);
2✔
869
  m_minimum = value;
2✔
870

871
  return *this;
2✔
872
}
873

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

3✔
882
std::string
883
double_sink::value() const
3✔
884
{
×
885
  return double_to_string(*m_sink);
886
}
887

888
size_t
889
double_sink::consume(string_vec_citer start, const string_vec_citer& end)
×
890
{
891
  AR_REQUIRE(end - start == 1);
×
892

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

×
902
  *m_sink = value;
903
  return 1;
904
}
905

×
906
///////////////////////////////////////////////////////////////////////////////
907
// str_sink
×
908

×
909
str_sink::str_sink(std::string* ptr)
×
910
  : sink(1)
911
  , m_sink(ptr ? ptr : &m_fallback_sink)
912
{
913
  AR_REQUIRE(m_sink);
3✔
914

915
  *m_sink = m_default;
3✔
916
}
917

918
str_sink&
919
str_sink::with_default(std::string_view value)
7✔
920
{
921
  set_has_default();
28✔
922
  *m_sink = m_default = value;
923

15✔
924
  return *this;
3✔
925
}
×
926

×
927
str_sink&
3✔
928
str_sink::with_choices(const string_vec& choices)
×
929
{
×
930
  m_choices = choices;
931

932
  return *this;
3✔
933
}
3✔
934

935
str_sink&
936
str_sink::with_implicit_argument(std::string_view value)
937
{
938
  m_has_implicit_argument = true;
939
  m_implicit_argument = value;
22✔
940
  set_min_values(0);
941

88✔
942
  return *this;
943
}
22✔
944

945
std::string
22✔
946
str_sink::value() const
22✔
947
{
948
  return *m_sink;
949
}
5✔
950

951
std::string
5✔
952
str_sink::default_value() const
10✔
953
{
954
  AR_REQUIRE(has_default());
5✔
955
  return m_default;
956
}
957

958
size_t
8✔
959
str_sink::consume(string_vec_citer start, const string_vec_citer& end)
960
{
8✔
961
  AR_REQUIRE(end - start <= 1);
962

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

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

987
    return invalid_choice;
988
  }
989
}
12✔
990

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

9✔
997
  m_sink->clear();
1✔
998
}
1✔
999

1000
vec_sink&
18✔
1001
vec_sink::with_min_values(size_t n)
9✔
1002
{
1003
  AR_REQUIRE(n <= max_values());
1004
  set_min_values(n);
20✔
1005

14✔
1006
  return *this;
7✔
1007
}
1008

6✔
1009
vec_sink&
6✔
1010
vec_sink::with_max_values(size_t n)
42✔
1011
{
27✔
1012
  AR_REQUIRE(n >= min_values());
2✔
1013
  set_max_values(n);
2✔
1014

1015
  return *this;
1016
}
1017

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

1027
  return static_cast<size_t>(end - start);
15✔
1028
}
16✔
1029

1030
std::string
1031
vec_sink::value() const
5✔
1032
{
1033
  std::string output;
10✔
1034

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

1040
    output.append(shell_escape(s));
3✔
1041
  }
1042

6✔
1043
  return output;
3✔
1044
}
1045

3✔
1046
} // namespace argparse
1047

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