SeqAn3  3.0.2
The Modern C++ library for sequence analysis.
format_base.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2020, Knut Reinert & MPI für molekulare Genetik
4 // This file may be used, modified and/or redistributed under the terms of the 3-clause BSD-License
5 // shipped with this file and also available at: https://github.com/seqan/seqan3/blob/master/LICENSE.md
6 // -----------------------------------------------------------------------------------------------------
7 
14 #pragma once
15 
16 #include <iostream>
17 #include <limits>
18 #include <sstream>
19 #include <string>
20 
26 #include <seqan3/std/filesystem>
27 
28 namespace seqan3::detail
29 {
30 
34 class format_base
35 {
36 protected:
41  template <typename value_type>
42  static std::string get_type_name_as_string(value_type const & )
43  {
44  using type = std::decay_t<value_type>;
45  using types = type_list<int8_t,
46  uint8_t,
47  int16_t,
48  uint16_t,
49  int32_t,
50  uint32_t,
51  int64_t,
52  uint64_t,
53  double,
54  float,
55  bool,
56  char,
59  std::vector<std::string> names{"signed 8 bit integer",
60  "unsigned 8 bit integer",
61  "signed 16 bit integer",
62  "unsigned 16 bit integer",
63  "signed 32 bit integer",
64  "unsigned 32 bit integer",
65  "signed 64 bit integer",
66  "unsigned 64 bit integer",
67  "double",
68  "float",
69  "bool",
70  "char",
71  "std::string",
72  "std::filesystem::path"};
73 
74  if constexpr (list_traits::contains<type, types>)
75  return names[list_traits::find<type, types>];
76  else
77  return detail::type_name_as_string<value_type>;
78  }
79 
84  template <sequence_container container_type>
86  requires (!std::is_same_v<container_type, std::string>)
88  static std::string get_type_name_as_string(container_type const & )
89  {
90  typename container_type::value_type tmp{};
91  return get_type_name_as_string(tmp);
92  }
93 
99  template <typename option_value_type>
100  static std::string option_type_and_list_info(option_value_type const & value)
101  {
102  return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
103  }
104 
111  template <typename container_type>
113  requires sequence_container<container_type> && (!std::is_same_v<container_type, std::string>)
115  static std::string option_type_and_list_info(container_type const & container)
116  {
117  return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP's)");
118  }
119 
127  static std::string prep_id_for_help(char const short_id, std::string const & long_id)
128  {
129  // Build list item term.
130  std::string term;
131  if (short_id != '\0')
132  term = "\\fB-" + std::string(1, short_id) + "\\fP";
133 
134  if (short_id != '\0' && !long_id.empty())
135  term.append(", ");
136 
137  if (!long_id.empty())
138  term.append("\\fB--" + long_id + "\\fP");
139 
140  return term;
141  }
142 
149  std::string escape_special_xml_chars(std::string const & original)
150  {
151  std::string escaped;
152  escaped.reserve(original.size()); // will be at least as long
153 
154  for (auto c : original)
155  {
156  if (c == '"')
157  escaped.append("&quot;");
158  else if (c == '\'')
159  escaped.append("&apos;");
160  else if (c == '&')
161  escaped.append("&amp;");
162  else if (c == '<')
163  escaped.append("&lt;");
164  else if (c == '>')
165  escaped.append("&gt;");
166  else
167  escaped.push_back(c);
168  }
169 
170  return escaped;
171  }
172 
179  static std::string expand_multiple_flags(std::string const & flag_cluster)
180  {
181  std::string tmp;
182  auto it{flag_cluster.begin()};
183 
184  if (flag_cluster[0] == '-')
185  ++it;
186 
187  for (; it != flag_cluster.end() - 1; ++it)
188  tmp.append("-" + std::string(1, *it) + ", ");
189 
190  tmp.erase(tmp.find_last_of(',')); // remove last ', '
191  tmp.append(" and -" + std::string(1, flag_cluster[flag_cluster.size() - 1]));
192 
193  return tmp;
194  }
195 };
196 
201 template <typename derived_type>
202 class format_help_base : public format_base
203 {
204 private:
208  format_help_base() = default;
209  format_help_base(format_help_base const & pf) = default;
210  format_help_base & operator=(format_help_base const & pf) = default;
211  format_help_base(format_help_base &&) = default;
212  format_help_base & operator=(format_help_base &&) = default;
213  ~format_help_base() = default;
214 
219  format_help_base(std::vector<std::string> const & names, bool const advanced) :
220  command_names{names}, show_advanced_options{advanced}
221  {}
223 
224 public:
228  template <typename option_type, typename validator_type>
229  void add_option(option_type & value,
230  char const short_id,
231  std::string const & long_id,
232  std::string const & desc,
233  option_spec const spec,
234  validator_type && option_validator)
235  {
236  std::string id = prep_id_for_help(short_id, long_id) + " " + option_type_and_list_info(value);
237  std::string info{desc};
238  info += ((spec & option_spec::REQUIRED) ? std::string{" "} : detail::to_string(" Default: ", value, ". "));
239  info += option_validator.get_help_page_message();
240  store_help_page_element([this, id, info] () { derived_t().print_list_item(id, info); }, spec);
241  }
242 
246  void add_flag(bool & SEQAN3_DOXYGEN_ONLY(value),
247  char const short_id,
248  std::string const & long_id,
249  std::string const & desc,
250  option_spec const spec)
251  {
252  std::string id = prep_id_for_help(short_id, long_id);
253  store_help_page_element([this, id, desc] () { derived_t().print_list_item(id, desc); }, spec);
254  }
255 
259  template <typename option_type, typename validator_type>
260  void add_positional_option(option_type & value,
261  std::string const & desc,
262  validator_type & option_validator)
263  {
264  std::string msg = option_validator.get_help_page_message();
265 
266  positional_option_calls.push_back([this, &value, desc, msg] ()
267  {
268  ++positional_option_count;
269  derived_t().print_list_item(detail::to_string("\\fBARGUMENT-", positional_option_count, "\\fP ",
270  option_type_and_list_info(value)),
271  desc +
272  // a list at the end may be empty and thus have a default value
273  ((sequence_container<option_type> && !std::same_as<option_type, std::string>)
274  ? detail::to_string(" Default: ", value, ". ")
275  : std::string{" "}) +
276  msg);
277  });
278  }
279 
283  void parse(argument_parser_meta_data & parser_meta)
284  {
285  meta = parser_meta;
286 
287  derived_t().print_header();
288 
289  if (!meta.synopsis.empty())
290  {
291  derived_t().print_section("Synopsis");
292  derived_t().print_synopsis();
293  }
294 
295  if (!meta.description.empty())
296  {
297  derived_t().print_section("Description");
298  for (auto desc : meta.description)
299  print_line(desc);
300  }
301 
302  if (!command_names.empty())
303  {
304  derived_t().print_section("Subcommands");
305  derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
306  for (std::string const & name : command_names)
307  derived_t().print_line("- \\fB" + name + "\\fP", false);
308  derived_t().print_line("See the respective help page for further details (e.g. by calling " +
309  meta.app_name + " " + command_names[0] + " -h).", true);
310  derived_t().print_line("The following options below belong to the top-level parser and need to be "
311  "specified \\fBbefore\\fP the subcommand key word. Every argument after the "
312  "subcommand key word is passed on to the corresponding sub-parser.", true);
313  }
314 
315  // add positional options if specified
316  if (!positional_option_calls.empty())
317  derived_t().print_section("Positional Arguments");
318 
319  // each call will evaluate the function derived_t().print_list_item()
320  for (auto f : positional_option_calls)
321  f();
322 
323  // add options and flags if specified
324  if (!parser_set_up_calls.empty())
325  derived_t().print_section("Options");
326 
327  // each call will evaluate the function derived_t().print_list_item()
328  for (auto f : parser_set_up_calls)
329  f();
330 
331  if (!meta.examples.empty())
332  {
333  derived_t().print_section("Examples");
334  for (auto example : meta.examples)
335  print_line(example);
336  }
337 
338  derived_t().print_footer();
339 
340  std::exit(EXIT_SUCCESS); // program should not continue from here
341  }
342 
346  void add_section(std::string const & title, option_spec const spec)
347  {
348  store_help_page_element([this, title] () { derived_t().print_section(title); }, spec);
349  }
350 
354  void add_subsection(std::string const & title, option_spec const spec)
355  {
356  store_help_page_element([this, title] () { derived_t().print_subsection(title); }, spec);
357  }
358 
362  void add_line(std::string const & text, bool is_paragraph, option_spec const spec)
363  {
364  store_help_page_element([this, text, is_paragraph] () { derived_t().print_line(text, is_paragraph); }, spec);
365  }
366 
370  void add_list_item(std::string const & key, std::string const & desc, option_spec const spec)
371  {
372  store_help_page_element([this, key, desc] () { derived_t().print_list_item(key, desc); }, spec);
373  }
374 
389  argument_parser_meta_data meta;
390 
392  friend derived_type;
393 
394 protected:
396  derived_type & derived_t()
397  {
398  return static_cast<derived_type &>(*this);
399  }
400 
402  void print_synopsis()
403  {
404  for (unsigned i = 0; i < meta.synopsis.size(); ++i)
405  {
406  std::string text = "\\fB";
407  text.append(meta.synopsis[i]);
408  text.insert(text.find_first_of(" \t"), "\\fP");
409 
410  derived_t().print_line(text, false);
411  }
412  }
413 
417  void print_line(std::string const & text)
418  {
419  derived_t().print_line(text, true);
420  }
421 
423  std::vector<std::function<void()>> parser_set_up_calls;
425  std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
427  unsigned positional_option_count{0};
429  std::vector<std::string> command_names{};
431  bool show_advanced_options{true};
432 
433 private:
444  void store_help_page_element(std::function<void()> printer, option_spec const spec)
445  {
446  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED) || show_advanced_options))
447  parser_set_up_calls.push_back(std::move(printer));
448  }
449 };
450 
451 } // namespace seqan3::detail
sstream
seqan3::option_spec
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:233
exceptions.hpp
Provides parser related exceptions.
std::string
sequence_container
A more refined container concept than seqan3::container.
seqan3::type_list
meta::list< types... > type_list
Type that contains multiple types, an alias for meta::list.
Definition: type_list.hpp:31
std::string::reserve
T reserve(T... args)
std::vector< std::string >
std::string::size
T size(T... args)
std::function
filesystem
This header includes C++17 filesystem support and imports it into namespace std::filesystem (independ...
iostream
std::filesystem::path
std::string::push_back
T push_back(T... args)
validators.hpp
Provides some standard validators for (positional) options.
seqan3::views::move
auto const move
A view that turns lvalue-references into rvalue-references.
Definition: move.hpp:68
seqan3::REQUIRED
@ REQUIRED
Definition: auxiliary.hpp:235
std::string::find_last_of
T find_last_of(T... args)
std::string::erase
T erase(T... args)
std::string::append
T append(T... args)
std::decay_t
limits
std::string::begin
T begin(T... args)
container
The (most general) container concept as defined by the standard library.
std::string::insert
T insert(T... args)
type_inspection.hpp
Provides traits to inspect some information of a type, for example its name.
std::string::empty
T empty(T... args)
seqan3::HIDDEN
@ HIDDEN
Definition: auxiliary.hpp:244
std::string::find_first_of
T find_first_of(T... args)
auxiliary.hpp
Provides auxiliary information.
std::string::end
T end(T... args)
traits.hpp
Provides traits for seqan3::type_list.
seqan3::ADVANCED
@ ADVANCED
Definition: auxiliary.hpp:240
std::exit
T exit(T... args)
string