SeqAn3  3.0.1
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:
237  template <typename option_type, typename validator_type>
238  void add_option(option_type & value,
239  char const short_id,
240  std::string const & long_id,
241  std::string const & desc,
242  option_spec const & spec,
243  validator_type && validator)
244  {
246 
247  parser_set_up_calls.push_back([this, &value, short_id, long_id, desc, spec, msg] ()
248  {
249  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED) || show_advanced_options))
250  derived_t().print_list_item(prep_id_for_help(short_id, long_id) +
251  " " + option_type_and_list_info(value),
252  desc +
253  ((spec & option_spec::REQUIRED)
254  ? std::string{" "}
255  : detail::to_string(" Default: ", value, ". ")) +
256  msg);
257  });
258  }
259 
267  void add_flag(bool & /*value*/,
268  char const short_id,
269  std::string const & long_id,
270  std::string const & desc,
271  option_spec const & spec)
272  {
273  parser_set_up_calls.push_back([this, short_id, long_id, desc, spec] ()
274  {
275  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED) || show_advanced_options))
276  derived_t().print_list_item(prep_id_for_help(short_id, long_id), desc);
277  });
278  }
279 
289  template <typename option_type, typename validator_type>
290  void add_positional_option(option_type & value,
291  std::string const & desc,
292  validator_type & validator)
293  {
295 
296  positional_option_calls.push_back([this, &value, desc, msg] ()
297  {
298  ++positional_option_count;
299  derived_t().print_list_item(detail::to_string("\\fBARGUMENT-", positional_option_count, "\\fP ",
300  option_type_and_list_info(value)),
301  desc +
302  // a list at the end may be empty and thus have a default value
304  ? detail::to_string(" Default: ", value, ". ")
305  : std::string{" "}) +
306  msg);
307  });
308  }
309 
313  void parse(argument_parser_meta_data & parser_meta)
314  {
315  meta = parser_meta;
316 
317  derived_t().print_header();
318 
319  if (!meta.synopsis.empty())
320  {
321  derived_t().print_section("Synopsis");
322  derived_t().print_synopsis();
323  }
324 
325  if (!meta.description.empty())
326  {
327  derived_t().print_section("Description");
328  for (auto desc : meta.description)
329  print_line(desc);
330  }
331 
332  if (!command_names.empty())
333  {
334  derived_t().print_section("Subcommands");
335  derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
336  for (std::string const & name : command_names)
337  derived_t().print_line("- \\fB" + name + "\\fP", false);
338  derived_t().print_line("See the respective help page for further details (e.g. by calling " +
339  meta.app_name + " " + command_names[0] + " -h).", true);
340  derived_t().print_line("The following options below belong to the top-level parser and need to be "
341  "specified \\fBbefore\\fP the subcommand key word. Every argument after the "
342  "subcommand key word is passed on to the corresponding sub-parser.", true);
343  }
344 
345  // add positional options if specified
346  if (!positional_option_calls.empty())
347  derived_t().print_section("Positional Arguments");
348 
349  // each call will evaluate the function derived_t().print_list_item()
350  for (auto f : positional_option_calls)
351  f();
352 
353  // add options and flags if specified
354  if (!parser_set_up_calls.empty())
355  derived_t().print_section("Options");
356 
357  // each call will evaluate the function derived_t().print_list_item()
358  for (auto f : parser_set_up_calls)
359  f();
360 
361  if (!meta.examples.empty())
362  {
363  derived_t().print_section("Examples");
364  for (auto example : meta.examples)
365  print_line(example);
366  }
367 
368  derived_t().print_footer();
369 
370  std::exit(EXIT_SUCCESS); // program should not continue from here
371  }
372 
376  void add_section(std::string const & title)
377  {
378  parser_set_up_calls.push_back([this, title] ()
379  {
380  derived_t().print_section(title);
381  });
382  }
383 
387  void add_subsection(std::string const & title)
388  {
389  parser_set_up_calls.push_back([this, title] ()
390  {
391  derived_t().print_subsection(title);
392  });
393  }
394 
399  void add_line(std::string const & text, bool line_is_paragraph)
400  {
401  parser_set_up_calls.push_back([this, text, line_is_paragraph] ()
402  {
403  derived_t().print_line(text, line_is_paragraph);
404  });
405  }
406 
411  void add_list_item(std::string const & key, std::string const & desc)
412  {
413  parser_set_up_calls.push_back([this, key, desc] ()
414  {
415  derived_t().print_list_item(key, desc);
416  });
417  }
418 
433  argument_parser_meta_data meta;
434 
436  friend derived_type;
437 
438 protected:
440  derived_type & derived_t()
441  {
442  return static_cast<derived_type &>(*this);
443  }
444 
446  void print_synopsis()
447  {
448  for (unsigned i = 0; i < meta.synopsis.size(); ++i)
449  {
450  std::string text = "\\fB";
451  text.append(meta.synopsis[i]);
452  text.insert(text.find_first_of(" \t"), "\\fP");
453 
454  derived_t().print_line(text, false);
455  }
456  }
457 
461  void print_line(std::string const & text)
462  {
463  derived_t().print_line(text, true);
464  }
465 
467  std::vector<std::function<void()>> parser_set_up_calls;
469  std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
471  unsigned positional_option_count{0};
473  std::vector<std::string> command_names{};
475  bool show_advanced_options{true};
476 };
477 
478 } // namespace seqan3::detail
sstream
seqan3::option_spec
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:231
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 seqan3::filesystem (indep...
iostream
std::filesystem::path
std::string::push_back
T push_back(T... args)
same_as
The concept std::same_as<T, U> is satisfied if and only if T and U denote the same type.
validators.hpp
Provides some standard validators for (positional) options.
seqan3::REQUIRED
Definition: auxiliary.hpp:234
std::string::find_last_of
T find_last_of(T... args)
validator::get_help_page_message
std::string get_help_page_message() const
Returns a message that can be appended to the (positional) options help page info.
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.
validator
The concept for option validators passed to add_option/positional_option.
std::string::empty
T empty(T... args)
seqan3::HIDDEN
Definition: auxiliary.hpp:243
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
Definition: auxiliary.hpp:239
std::exit
T exit(T... args)
string