SeqAn3 3.2.0
The Modern C++ library for sequence analysis.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
format_base.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2022, 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 <filesystem>
17#include <iostream>
18#include <limits>
19#include <sstream>
20#include <string>
21
27#include <seqan3/version.hpp>
28
29namespace seqan3::detail
30{
31
36class format_base
37{
38protected:
43 template <typename value_type>
44 static std::string get_type_name_as_string(value_type const & )
45 {
46 using type = std::decay_t<value_type>;
47 using types = type_list<int8_t,
48 uint8_t,
49 int16_t,
50 uint16_t,
51 int32_t,
52 uint32_t,
53 int64_t,
54 uint64_t,
55 double,
56 float,
57 bool,
58 char,
61 std::vector<std::string> names{"signed 8 bit integer",
62 "unsigned 8 bit integer",
63 "signed 16 bit integer",
64 "unsigned 16 bit integer",
65 "signed 32 bit integer",
66 "unsigned 32 bit integer",
67 "signed 64 bit integer",
68 "unsigned 64 bit integer",
69 "double",
70 "float",
71 "bool",
72 "char",
73 "std::string",
74 "std::filesystem::path"};
75
76 if constexpr (list_traits::contains<type, types>)
77 return names[list_traits::find<type, types>];
78 else
79 return detail::type_name_as_string<value_type>;
80 }
81
86 template <detail::is_container_option container_type>
87 static std::string get_type_name_as_string(container_type const & )
88 {
89 typename container_type::value_type tmp{};
90 return get_type_name_as_string(tmp);
91 }
92
98 template <typename option_value_type>
99 static std::string option_type_and_list_info(option_value_type const & value)
100 {
101 return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
102 }
103
110 template <detail::is_container_option container_type>
111 static std::string option_type_and_list_info(container_type const & container)
112 {
113 return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP)");
114 }
115
123 static std::string prep_id_for_help(char const short_id, std::string const & long_id)
124 {
125 // Build list item term.
126 std::string term;
127 if (short_id != '\0')
128 term = "\\fB-" + std::string(1, short_id) + "\\fP";
129
130 if (short_id != '\0' && !long_id.empty())
131 term.append(", ");
132
133 if (!long_id.empty())
134 term.append("\\fB--" + long_id + "\\fP");
135
136 return term;
137 }
138
145 std::string escape_special_xml_chars(std::string const & original)
146 {
147 std::string escaped;
148 escaped.reserve(original.size()); // will be at least as long
149
150 for (auto c : original)
151 {
152 if (c == '"')
153 escaped.append("&quot;");
154 else if (c == '\'')
155 escaped.append("&apos;");
156 else if (c == '&')
157 escaped.append("&amp;");
158 else if (c == '<')
159 escaped.append("&lt;");
160 else if (c == '>')
161 escaped.append("&gt;");
162 else
163 escaped.push_back(c);
164 }
165
166 return escaped;
167 }
168
175 static std::string expand_multiple_flags(std::string const & flag_cluster)
176 {
177 std::string tmp;
178 auto it{flag_cluster.begin()};
179
180 if (flag_cluster[0] == '-')
181 ++it;
182
183 for (; it != flag_cluster.end() - 1; ++it)
184 tmp.append({'-', *it, ',', ' '});
185
186 tmp.erase(tmp.find_last_of(',')); // remove last ', '
187 tmp.append({'a', 'n', 'd', ' ', '-', flag_cluster[flag_cluster.size() - 1]});
188
189 return tmp;
190 }
191};
192
198template <typename derived_type>
199class format_help_base : public format_base
200{
201private:
205 format_help_base() = default;
206 format_help_base(format_help_base const & pf) = default;
207 format_help_base & operator=(format_help_base const & pf) = default;
208 format_help_base(format_help_base &&) = default;
209 format_help_base & operator=(format_help_base &&) = default;
210 ~format_help_base() = default;
211
216 format_help_base(std::vector<std::string> const & names, bool const advanced) :
217 command_names{names},
218 show_advanced_options{advanced}
219 {}
221
222public:
226 template <typename option_type, typename validator_type>
227 void add_option(option_type & value,
228 char const short_id,
229 std::string const & long_id,
230 std::string const & desc,
231 option_spec const spec,
232 validator_type && option_validator)
233 {
234 std::string id = prep_id_for_help(short_id, long_id) + " " + option_type_and_list_info(value);
235 std::string info{desc};
236 info += ((spec & option_spec::required) ? std::string{" "} : detail::to_string(" Default: ", value, ". "));
237 info += option_validator.get_help_page_message();
238 store_help_page_element(
239 [this, id, info]()
240 {
241 derived_t().print_list_item(id, info);
242 },
243 spec);
244 }
245
249 void add_flag(bool & SEQAN3_DOXYGEN_ONLY(value),
250 char const short_id,
251 std::string const & long_id,
252 std::string const & desc,
253 option_spec const spec)
254 {
255 std::string id = prep_id_for_help(short_id, long_id);
256 store_help_page_element(
257 [this, id, desc]()
258 {
259 derived_t().print_list_item(id, desc);
260 },
261 spec);
262 }
263
267 template <typename option_type, typename validator_type>
268 void add_positional_option(option_type & value, std::string const & desc, validator_type & option_validator)
269 {
270 std::string msg = option_validator.get_help_page_message();
271
272 positional_option_calls.push_back(
273 [this, &value, desc, msg]()
274 {
275 ++positional_option_count;
276 derived_t().print_list_item(detail::to_string("\\fBARGUMENT-",
277 positional_option_count,
278 "\\fP ",
279 option_type_and_list_info(value)),
280 desc +
281 // a list at the end may be empty and thus have a default value
282 ((detail::is_container_option<option_type>)
283 ? detail::to_string(" Default: ", value, ". ")
284 : std::string{" "})
285 + msg);
286 });
287 }
288
292 void parse(argument_parser_meta_data & parser_meta)
293 {
294 meta = parser_meta;
295
296 derived_t().print_header();
297
298 if (!meta.synopsis.empty())
299 {
300 derived_t().print_section("Synopsis");
301 derived_t().print_synopsis();
302 }
303
304 if (!meta.description.empty())
305 {
306 derived_t().print_section("Description");
307 for (auto desc : meta.description)
308 print_line(desc);
309 }
310
311 if (!command_names.empty())
312 {
313 derived_t().print_section("Subcommands");
314 derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
315 for (std::string const & name : command_names)
316 derived_t().print_line("- \\fB" + name + "\\fP", false);
317 derived_t().print_line("See the respective help page for further details (e.g. by calling " + meta.app_name
318 + " " + command_names[0] + " -h).",
319 true);
320 derived_t().print_line("The following options below belong to the top-level parser and need to be "
321 "specified \\fBbefore\\fP the subcommand key word. Every argument after the "
322 "subcommand key word is passed on to the corresponding sub-parser.",
323 true);
324 }
325
326 // add positional options if specified
327 if (!positional_option_calls.empty())
328 derived_t().print_section("Positional Arguments");
329
330 // each call will evaluate the function derived_t().print_list_item()
331 for (auto f : positional_option_calls)
332 f();
333
334 // add options and flags if specified
335 if (!parser_set_up_calls.empty())
336 derived_t().print_section("Options");
337
338 // each call will evaluate the function derived_t().print_list_item()
339 for (auto f : parser_set_up_calls)
340 f();
341
342 if (!meta.examples.empty())
343 {
344 derived_t().print_section("Examples");
345 for (auto example : meta.examples)
346 print_line(example);
347 }
348
349 print_version();
350
351 print_legal();
352
353 derived_t().print_footer();
354
355 std::exit(EXIT_SUCCESS); // program should not continue from here
356 }
357
361 void add_section(std::string const & title, option_spec const spec)
362 {
363 store_help_page_element(
364 [this, title]()
365 {
366 derived_t().print_section(title);
367 },
368 spec);
369 }
370
374 void add_subsection(std::string const & title, option_spec const spec)
375 {
376 store_help_page_element(
377 [this, title]()
378 {
379 derived_t().print_subsection(title);
380 },
381 spec);
382 }
383
387 void add_line(std::string const & text, bool is_paragraph, option_spec const spec)
388 {
389 store_help_page_element(
390 [this, text, is_paragraph]()
391 {
392 derived_t().print_line(text, is_paragraph);
393 },
394 spec);
395 }
396
400 void add_list_item(std::string const & key, std::string const & desc, option_spec const spec)
401 {
402 store_help_page_element(
403 [this, key, desc]()
404 {
405 derived_t().print_list_item(key, desc);
406 },
407 spec);
408 }
409
424 argument_parser_meta_data meta;
425
427 friend derived_type;
428
429protected:
431 derived_type & derived_t()
432 {
433 return static_cast<derived_type &>(*this);
434 }
435
437 void print_synopsis()
438 {
439 for (unsigned i = 0; i < meta.synopsis.size(); ++i)
440 {
441 std::string text = "\\fB";
442 text.append(meta.synopsis[i]);
443 text.insert(text.find_first_of(" \t"), "\\fP");
444
445 derived_t().print_line(text, false);
446 }
447 }
448
452 void print_line(std::string const & text)
453 {
454 derived_t().print_line(text, true);
455 }
456
458 void print_version()
459 {
460 std::string const version_str{seqan3_version_cstring};
461
462 // Print version, date and url.
463 derived_t().print_section("Version");
464 derived_t().print_line(derived_t().in_bold("Last update: ") + meta.date, false);
465 derived_t().print_line(derived_t().in_bold(meta.app_name + " version: ") + meta.version, false);
466 derived_t().print_line(derived_t().in_bold("SeqAn version: ") + version_str, false);
467
468 if (!empty(meta.url))
469 {
470 derived_t().print_section("Url");
471 derived_t().print_line(meta.url, false);
472 }
473 }
474
476 void print_legal()
477 {
478 // Print legal stuff
479 if ((!empty(meta.short_copyright)) || (!empty(meta.long_copyright)) || (!empty(meta.citation))
480 || (!empty(meta.author)) || (!empty(meta.email)))
481 {
482 derived_t().print_section("Legal");
483
484 if (!empty(meta.short_copyright))
485 {
486 derived_t().print_line(derived_t().in_bold(meta.app_name + " Copyright: ") + meta.short_copyright,
487 false);
488 }
489
490 if (!empty(meta.author))
491 {
492 derived_t().print_line(derived_t().in_bold("Author: ") + meta.author, false);
493 }
494
495 if (!empty(meta.email))
496 {
497 derived_t().print_line(derived_t().in_bold("Contact: ") + meta.email, false);
498 }
499
500 derived_t().print_line(derived_t().in_bold("SeqAn Copyright: ")
501 + "2006-2022 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.",
502 false);
503
504 if (!empty(meta.citation))
505 {
506 derived_t().print_line(derived_t().in_bold("In your academic works please cite: ") + meta.citation,
507 false);
508 }
509
510 if (!empty(meta.long_copyright))
511 {
512 derived_t().print_line("For full copyright and/or warranty information see "
513 + derived_t().in_bold("--copyright") + ".",
514 false);
515 }
516 }
517 }
518
520 std::vector<std::function<void()>> parser_set_up_calls;
522 std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
524 unsigned positional_option_count{0};
526 std::vector<std::string> command_names{};
528 bool show_advanced_options{true};
529
530private:
541 void store_help_page_element(std::function<void()> printer, option_spec const spec)
542 {
543 if (!(spec & option_spec::hidden) && (!(spec & option_spec::advanced) || show_advanced_options))
544 parser_set_up_calls.push_back(std::move(printer));
545 }
546};
547
548} // namespace seqan3::detail
T append(T... args)
Provides the concept seqan3::detail::is_container_option.
Provides auxiliary information.
T begin(T... args)
T empty(T... args)
T end(T... args)
T erase(T... args)
Provides parser related exceptions.
T exit(T... args)
T find_first_of(T... args)
T find_last_of(T... args)
option_spec
Used to further specify argument_parser options/flags.
Definition: auxiliary.hpp:248
@ advanced
Definition: auxiliary.hpp:255
@ hidden
Definition: auxiliary.hpp:259
@ required
Definition: auxiliary.hpp:250
T insert(T... args)
The (most general) container concept as defined by the standard library.
constexpr char const * seqan3_version_cstring
The full version as null terminated string.
Definition: version.hpp:67
T parse(T... args)
T push_back(T... args)
T reserve(T... args)
T size(T... args)
Provides traits to inspect some information of a type, for example its name.
Provides some standard validators for (positional) options.
Provides SeqAn version macros and global variables.