SeqAn3
argument_parser.hpp
Go to the documentation of this file.
1 // ============================================================================
2 // SeqAn - The Library for Sequence Analysis
3 // ============================================================================
4 //
5 // Copyright (c) 2006-2018, Knut Reinert & Freie Universitaet Berlin
6 // Copyright (c) 2016-2018, Knut Reinert & MPI Molekulare Genetik
7 // All rights reserved.
8 //
9 // Redistribution and use in source and binary forms, with or without
10 // modification, are permitted provided that the following conditions are met:
11 //
12 // * Redistributions of source code must retain the above copyright
13 // notice, this list of conditions and the following disclaimer.
14 // * Redistributions in binary form must reproduce the above copyright
15 // notice, this list of conditions and the following disclaimer in the
16 // documentation and/or other materials provided with the distribution.
17 // * Neither the name of Knut Reinert or the FU Berlin nor the names of
18 // its contributors may be used to endorse or promote products derived
19 // from this software without specific prior written permission.
20 //
21 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 // ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
25 // FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26 // DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27 // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28 // CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29 // LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30 // OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
31 // DAMAGE.
32 //
33 // ============================================================================
34 
40 #pragma once
41 
42 #include <experimental/filesystem>
43 #include <future>
44 #include <iostream>
45 #include <set>
46 #include <sstream>
47 #include <string>
48 #include <variant>
49 #include <vector>
50 
51 // #include <seqan3/argument_parser/detail/format_ctd.hpp>
57 
58 namespace seqan3
59 {
60 
155 {
156 public:
162  argument_parser() = delete;
163  argument_parser(argument_parser const &) = default;
164  argument_parser & operator=(argument_parser const &) = default;
165  argument_parser(argument_parser &&) = default;
166  argument_parser & operator=(argument_parser &&) = default;
167 
174  argument_parser(std::string const app_name, int const argc, char const * const * const argv)
175  {
176  info.app_name = std::move(app_name);
177  init(argc, argv);
178  }
179 
181  ~argument_parser() = default;
183 
207  template <typename option_type, validator_concept validator_type = detail::default_validator<option_type>>
211  std::is_same_v<typename validator_type::value_type, option_type>
213  void add_option(option_type & value,
214  char const short_id,
215  std::string const & long_id,
216  std::string const & desc,
217  option_spec const & spec = option_spec::DEFAULT,
218  validator_type validator = validator_type{}) // copy to bind rvalues
219  {
220  if (id_exists(short_id))
221  throw parser_design_error("Option Identifier '" + std::string(1, short_id) + "' was already used before.");
222  if (id_exists(long_id))
223  throw parser_design_error("Option Identifier '" + long_id + "' was already used before.");
224  if (short_id == '\0' && long_id.empty())
225  throw parser_design_error("Option Identifiers cannot both be empty.");
226 
227  // copy variables into the lambda because the calls are pushed to a stack
228  // and the references would go out of scope.
229  std::visit([=, &value] (auto & f) { f.add_option(value, short_id, long_id, desc, spec, validator); }, format);
230  }
231 
242  void add_flag(bool & value,
243  char const short_id,
244  std::string const & long_id,
245  std::string const & desc,
246  option_spec const & spec = option_spec::DEFAULT)
247  {
248  if (id_exists(short_id))
249  throw parser_design_error("Option Identifier '" + std::string(1, short_id) + "' was already used before.");
250  if (id_exists(long_id))
251  throw parser_design_error("Option Identifier '" + long_id + "' was already used before.");
252  if (short_id == '\0' && long_id.empty())
253  throw parser_design_error("Option Identifiers cannot both be empty.");
254 
255  // copy variables into the lambda because the calls are pushed to a stack
256  // and the references would go out of scope.
257  std::visit([=, &value] (auto & f) { f.add_flag(value, short_id, long_id, desc, spec); }, format);
258  }
259 
280  template <typename option_type, validator_concept validator_type = detail::default_validator<option_type>>
284  std::is_same_v<typename validator_type::value_type, option_type>
286  void add_positional_option(option_type & value,
287  std::string const & desc,
288  validator_type validator = validator_type{}) // copy to bind rvalues
289  {
290  // copy variables into the lambda because the calls are pushed to a stack
291  // and the references would go out of scope.
292  std::visit([=, &value] (auto & f) { f.add_positional_option(value, desc, validator); }, format);
293  }
295 
373  void parse()
374  {
375  if (parse_was_called)
376  throw parser_design_error("The function parse() must only be called once!");
377 
378  std::visit([this] (auto & f) { f.parse(info); }, format);
379  parse_was_called = true;
380  }
381 
384 
389  void add_section(std::string const & title)
390  {
391  std::visit([&] (auto & f) { f.add_section(title); }, format);
392  }
393 
398  void add_subsection(std::string const & title)
399  {
400  std::visit([&] (auto & f) { f.add_subsection(title); }, format);
401  }
402 
409  void add_line(std::string const & text, bool line_is_paragraph)
410  {
411  std::visit([&] (auto & f) { f.add_line(text, line_is_paragraph); }, format);
412  }
413 
430  void add_list_item(std::string const & key, std::string const & desc)
431  {
432  std::visit([&] (auto & f) { f.add_list_item(key, desc); }, format);
433  }
435 
485 
486 // TODO (smehringer)
487 // #ifdef SEQAN_VERSION_CHECK_OPT_IN
488 // bool enabled_version_check{false};
489 // #else // Make version update opt out.
490 // bool enabled_version_check{true};
491 // #endif // SEQAN_VERSION_CHECK_OPT_IN
492 // std::future<bool> appVersionCheckFuture;
493 
494 private:
496  bool parse_was_called{false};
497 
526  void init(int const argc, char const * const * const argv)
527  {
528  if (argc <= 1) // no arguments provided
529  {
530  format = detail::format_short_help();
531  return;
532  }
533 
534  for(int i = 1; i < argc; ++i) // start at 1 to skip binary name
535  {
536  std::string arg{argv[i]};
537 
538  if (arg == "-h" || arg == "--help")
539  {
540  format = detail::format_help(false);
541  return;
542  }
543  else if (arg == "-hh" || arg == "--advanced-help")
544  {
545  format = detail::format_help(true);
546  return;
547  }
548  else if (arg == "--version")
549  {
550  format = detail::format_version();
551  return;
552  }
553  else if (arg == "--export-help")
554  {
555  std::string export_format{argv[i+1]};
556 
557  if (export_format == "html")
558  format = detail::format_html();
559  else if (export_format == "man")
560  format = detail::format_man();
561  // TODO (smehringer) use when CTD support is available
562  // else if (export_format == "ctd")
563  // format = detail::format_ctd();
564  else
565  throw validation_failed("Validation Failed. "
566  "Value of --export-help must be one of [html, man, ctd]");
567  return;
568  }
569  }
570 
571  format = detail::format_parse(argc, argv);
572  }
573 
579  bool id_exists(std::string const & long_id)
580  {
581  return(!(used_option_ids.insert(long_id)).second);
582  }
583 
589  bool id_exists(char const short_id)
590  {
591  return(!(used_option_ids.insert(std::string(1, short_id))).second);
592  }
593 
598  std::variant<detail::format_parse,
599  detail::format_help,
600  detail::format_short_help,
601  detail::format_version,
602  detail::format_html,
603  detail::format_man/*,
604  detail::format_ctd*/> format{detail::format_help(0)};
605 
607  std::set<std::string> used_option_ids;
608 };
609 
610 } // namespace seqan3
Argument parser exception thrown when an argument could not be casted to the according type...
Definition: exceptions.hpp:144
argument_parser(std::string const app_name, int const argc, char const *const *const argv)
Initializes an argument_parser object from the command line arguments.
Definition: argument_parser.hpp:174
void add_subsection(std::string const &title)
Adds an help page subsection to the seqan3::argument_parser.
Definition: argument_parser.hpp:398
void add_section(std::string const &title)
Adds an help page section to the seqan3::argument_parser.
Definition: argument_parser.hpp:389
The SeqAn command line parser.
Definition: argument_parser.hpp:154
void add_line(std::string const &text, bool line_is_paragraph)
Adds an help page text line to the seqan3::argument_parser.
Definition: argument_parser.hpp:409
The main SeqAn3 namespace.
Definition: aligned_sequence_concept.hpp:58
Concept for input streams.
Contains the format_man struct and its helper functions.
Argument parser exception that is thrown whenever there is an design error directed at the developer ...
Definition: exceptions.hpp:163
Stores all parser related meta information of the seqan3::argument_parser.
Definition: auxiliary.hpp:90
void add_positional_option(option_type &value, std::string const &desc, validator_type validator=validator_type{})
Adds a positional option to the seqan3::argument_parser.
Definition: argument_parser.hpp:286
std::string app_name
The application name that will be displayed on the help page.
Definition: auxiliary.hpp:93
void add_list_item(std::string const &key, std::string const &desc)
Adds an help page list item (key-value) to the seqan3::argument_parser.
Definition: argument_parser.hpp:430
argument_parser_meta_data info
Aggregates all parser related meta data (see seqan3::argument_parser_meta_data struct).
Definition: argument_parser.hpp:484
Stream concepts.
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:60
~argument_parser()=default
The destructor.
Contains the format_html struct and its helper functions.
Contains the format_parse class.
void add_option(option_type &value, char const short_id, std::string const &long_id, std::string const &desc, option_spec const &spec=option_spec::DEFAULT, validator_type validator=validator_type{})
Adds an option to the seqan3::argument_parser.
Definition: argument_parser.hpp:213
void add_flag(bool &value, char const short_id, std::string const &long_id, std::string const &desc, option_spec const &spec=option_spec::DEFAULT)
Adds a flag to the seqan3::argument_parser.
Definition: argument_parser.hpp:242
void parse()
Initiates the actual command line parsing.
Definition: argument_parser.hpp:373
Contains the format_help struct that print the help page to the command line and the two child format...
The default were no checking or special displaying is happening.
Definition: auxiliary.hpp:62