SeqAn3
The Modern C++ library for sequence analysis.
format_base.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2019, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2019, 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 
21 #include <meta/meta.hpp>
22 
27 #include <seqan3/std/filesystem>
28 
29 namespace seqan3::detail
30 {
31 
35 class format_base
36 {
37 protected:
42  template <typename value_type>
43  static std::string get_type_name_as_string(value_type const & )
44  {
45  using type = std::decay_t<value_type>;
46  using types = meta::list<int8_t,
47  uint8_t,
48  int16_t,
49  uint16_t,
50  int32_t,
51  uint32_t,
52  int64_t,
53  uint64_t,
54  double,
55  float,
56  bool,
57  char,
60  std::vector<std::string> names{"signed 8 bit integer",
61  "unsigned 8 bit integer",
62  "signed 16 bit integer",
63  "unsigned 16 bit integer",
64  "signed 32 bit integer",
65  "unsigned 32 bit integer",
66  "signed 64 bit integer",
67  "unsigned 64 bit integer",
68  "double",
69  "float",
70  "bool",
71  "char",
72  "std::string",
73  "std::filesystem::path"};
74 
75  if constexpr (meta::in<types, type>::value)
76  return names[meta::find_index<types, type>::value];
77  else
78  return detail::get_display_name_v<value_type>.str();
79  }
80 
85  template <SequenceContainer container_type>
87  requires !std::is_same_v<container_type, std::string>
89  static std::string get_type_name_as_string(container_type const & )
90  {
91  typename container_type::value_type tmp;
92  return get_type_name_as_string(tmp);
93  }
94 
100  template <typename option_value_type>
101  static std::string option_type_and_list_info(option_value_type const & value)
102  {
103  return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
104  }
105 
112  template <typename container_type>
114  requires SequenceContainer<container_type> && !std::is_same_v<container_type, std::string>
116  static std::string option_type_and_list_info(container_type const & container)
117  {
118  return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP's)");
119  }
120 
128  static std::string prep_id_for_help(char const short_id, std::string const & long_id)
129  {
130  // Build list item term.
131  std::string term;
132  if (short_id != '\0')
133  term = "\\fB-" + std::string(1, short_id) + "\\fP";
134 
135  if (short_id != '\0' && !long_id.empty())
136  term.append(", ");
137 
138  if (!long_id.empty())
139  term.append("\\fB--" + long_id + "\\fP");
140 
141  return term;
142  }
143 
150  std::string escape_special_xml_chars(std::string const & original)
151  {
152  std::string escaped;
153  escaped.reserve(original.size()); // will be at least as long
154 
155  for (auto c : original)
156  {
157  if (c == '"')
158  escaped.append("&quot;");
159  else if (c == '\'')
160  escaped.append("&apos;");
161  else if (c == '&')
162  escaped.append("&amp;");
163  else if (c == '<')
164  escaped.append("&lt;");
165  else if (c == '>')
166  escaped.append("&gt;");
167  else
168  escaped.push_back(c);
169  }
170 
171  return escaped;
172  }
173 
180  static std::string expand_multiple_flags(std::string const & flag_cluster)
181  {
182  std::string tmp;
183  auto it{flag_cluster.begin()};
184 
185  if (flag_cluster[0] == '-')
186  ++it;
187 
188  for (; it != flag_cluster.end() - 1; ++it)
189  tmp.append("-" + std::string(1, *it) + ", ");
190 
191  tmp.erase(tmp.find_last_of(',')); // remove last ', '
192  tmp.append(" and -" + std::string(1, flag_cluster[flag_cluster.size() - 1]));
193 
194  return tmp;
195  }
196 };
197 
202 template <typename derived_type>
203 class format_help_base : public format_base
204 {
205 private:
209  format_help_base() = default;
210  format_help_base(format_help_base const & pf) = default;
211  format_help_base & operator=(format_help_base const & pf) = default;
212  format_help_base(format_help_base &&) = default;
213  format_help_base & operator=(format_help_base &&) = default;
214  ~format_help_base() = default;
215 
219  format_help_base(bool const advanced) :
220  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  {
245  parser_set_up_calls.push_back([this, &value, short_id, long_id, desc, spec, validator] ()
246  {
247  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED) || show_advanced_options))
248  derived_t().print_list_item(prep_id_for_help(short_id, long_id) +
249  " " + option_type_and_list_info(value),
250  desc + detail::to_string(" Default: ", value, ". ") +
251  validator.get_help_page_message());
252  });
253  }
254 
262  void add_flag(bool & /*value*/,
263  char const short_id,
264  std::string const & long_id,
265  std::string const & desc,
266  option_spec const & spec)
267  {
268  parser_set_up_calls.push_back([this, short_id, long_id, desc, spec] ()
269  {
270  if (!(spec & option_spec::HIDDEN) && (!(spec & option_spec::ADVANCED) || show_advanced_options))
271  derived_t().print_list_item(prep_id_for_help(short_id, long_id), desc);
272  });
273  }
274 
284  template <typename option_type, typename validator_type>
285  void add_positional_option(option_type & value,
286  std::string const & desc,
287  validator_type & validator)
288  {
289  positional_option_calls.push_back([this, &value, desc, validator] ()
290  {
291  ++positional_option_count;
292  derived_t().print_list_item(detail::to_string("\\fBARGUMENT-", positional_option_count, "\\fP ",
293  option_type_and_list_info(value)),
294  desc + detail::to_string(" Default: ", value, ". ") +
295  validator.get_help_page_message());
296  });
297  }
298 
302  void parse(argument_parser_meta_data & parser_meta)
303  {
304  meta = parser_meta;
305 
306  derived_t().print_header();
307 
308  if (!meta.synopsis.empty())
309  {
310  derived_t().print_section("Synopsis");
311  derived_t().print_synopsis();
312  }
313 
314  if (!meta.description.empty())
315  {
316  derived_t().print_section("Description");
317  for (auto desc : meta.description)
318  print_line(desc);
319  }
320 
321  // add positional options if specified
322  if (!positional_option_calls.empty())
323  derived_t().print_section("Positional Arguments");
324 
325  // each call will evaluate the function derived_t().print_list_item()
326  for (auto f : positional_option_calls)
327  f();
328 
329  // add options and flags if specified
330  if (!parser_set_up_calls.empty())
331  derived_t().print_section("Options");
332 
333  // each call will evaluate the function derived_t().print_list_item()
334  for (auto f : parser_set_up_calls)
335  f();
336 
337  if (!meta.examples.empty())
338  {
339  derived_t().print_section("Examples");
340  for (auto example : meta.examples)
341  print_line(example);
342  }
343 
344  derived_t().print_footer();
345 
346  std::exit(EXIT_SUCCESS); // program should not continue from here
347  }
348 
352  void add_section(std::string const & title)
353  {
354  parser_set_up_calls.push_back([this, title] ()
355  {
356  derived_t().print_section(title);
357  });
358  }
359 
363  void add_subsection(std::string const & title)
364  {
365  parser_set_up_calls.push_back([this, title] ()
366  {
367  derived_t().print_subsection(title);
368  });
369  }
370 
375  void add_line(std::string const & text, bool line_is_paragraph)
376  {
377  parser_set_up_calls.push_back([this, text, line_is_paragraph] ()
378  {
379  derived_t().print_line(text, line_is_paragraph);
380  });
381  }
382 
387  void add_list_item(std::string const & key, std::string const & desc)
388  {
389  parser_set_up_calls.push_back([this, key, desc] ()
390  {
391  derived_t().print_list_item(key, desc);
392  });
393  }
394 
409  argument_parser_meta_data meta;
410 
412  friend derived_type;
413 
414 protected:
416  derived_type & derived_t()
417  {
418  return static_cast<derived_type &>(*this);
419  }
420 
422  void print_synopsis()
423  {
424  for (unsigned i = 0; i < meta.synopsis.size(); ++i)
425  {
426  std::string text = "\\fB";
427  text.append(meta.synopsis[i]);
428  text.insert(text.find_first_of(" \t"), "\\fP");
429 
430  derived_t().print_line(text, false);
431  }
432  }
433 
437  void print_line(std::string const & text)
438  {
439  derived_t().print_line(text, true);
440  }
441 
443  std::vector<std::function<void()>> parser_set_up_calls;
445  std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
447  unsigned positional_option_count{0};
449  bool show_advanced_options{true};
450 };
451 
452 } // namespace seqan3::detail
Provides some standard validators for (positional) options.
T empty(T... args)
Definition: auxiliary.hpp:46
Provides parser related exceptions.
T end(T... args)
T find_last_of(T... args)
T push_back(T... args)
T exit(T... args)
T append(T... args)
T erase(T... args)
T find_first_of(T... args)
T insert(T... args)
Definition: aligned_sequence_concept.hpp:35
Definition: auxiliary.hpp:42
T size(T... args)
T begin(T... args)
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:34
Static reflection for arbitrary types.
Provides auxiliary information.
T reserve(T... args)
This header includes C++17 filesystem support and imports it into namespace seqan3::filesystem (indep...