SeqAn3 3.1.0
The Modern C++ library for sequence analysis.
format_base.hpp
Go to the documentation of this file.
1// -----------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2021, 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 <seqan3/std/filesystem>
17#include <iostream>
18#include <limits>
19#include <sstream>
20#include <string>
21
28#include <seqan3/version.hpp>
29
30namespace seqan3::detail
31{
32
37class format_base
38{
39protected:
44 template <typename value_type>
45 static std::string get_type_name_as_string(value_type const & )
46 {
47 using type = std::decay_t<value_type>;
48 using types = type_list<int8_t,
49 uint8_t,
50 int16_t,
51 uint16_t,
52 int32_t,
53 uint32_t,
54 int64_t,
55 uint64_t,
56 double,
57 float,
58 bool,
59 char,
62 std::vector<std::string> names{"signed 8 bit integer",
63 "unsigned 8 bit integer",
64 "signed 16 bit integer",
65 "unsigned 16 bit integer",
66 "signed 32 bit integer",
67 "unsigned 32 bit integer",
68 "signed 64 bit integer",
69 "unsigned 64 bit integer",
70 "double",
71 "float",
72 "bool",
73 "char",
74 "std::string",
75 "std::filesystem::path"};
76
77 if constexpr (list_traits::contains<type, types>)
78 return names[list_traits::find<type, types>];
79 else
80 return detail::type_name_as_string<value_type>;
81 }
82
87 template <sequence_container container_type>
89 requires (!std::is_same_v<container_type, std::string>)
91 static std::string get_type_name_as_string(container_type const & )
92 {
93 typename container_type::value_type tmp{};
94 return get_type_name_as_string(tmp);
95 }
96
102 template <typename option_value_type>
103 static std::string option_type_and_list_info(option_value_type const & value)
104 {
105 return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
106 }
107
114 template <typename container_type>
116 requires sequence_container<container_type> && (!std::is_same_v<container_type, std::string>)
118 static std::string option_type_and_list_info(container_type const & container)
119 {
120 return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP)");
121 }
122
130 static std::string prep_id_for_help(char const short_id, std::string const & long_id)
131 {
132 // Build list item term.
133 std::string term;
134 if (short_id != '\0')
135 term = "\\fB-" + std::string(1, short_id) + "\\fP";
136
137 if (short_id != '\0' && !long_id.empty())
138 term.append(", ");
139
140 if (!long_id.empty())
141 term.append("\\fB--" + long_id + "\\fP");
142
143 return term;
144 }
145
152 std::string escape_special_xml_chars(std::string const & original)
153 {
154 std::string escaped;
155 escaped.reserve(original.size()); // will be at least as long
156
157 for (auto c : original)
158 {
159 if (c == '"')
160 escaped.append("&quot;");
161 else if (c == '\'')
162 escaped.append("&apos;");
163 else if (c == '&')
164 escaped.append("&amp;");
165 else if (c == '<')
166 escaped.append("&lt;");
167 else if (c == '>')
168 escaped.append("&gt;");
169 else
170 escaped.push_back(c);
171 }
172
173 return escaped;
174 }
175
182 static std::string expand_multiple_flags(std::string const & flag_cluster)
183 {
184 std::string tmp;
185 auto it{flag_cluster.begin()};
186
187 if (flag_cluster[0] == '-')
188 ++it;
189
190 for (; it != flag_cluster.end() - 1; ++it)
191 tmp.append("-" + std::string(1, *it) + ", ");
192
193 tmp.erase(tmp.find_last_of(',')); // remove last ', '
194 tmp.append(" and -" + std::string(1, flag_cluster[flag_cluster.size() - 1]));
195
196 return tmp;
197 }
198};
199
205template <typename derived_type>
206class format_help_base : public format_base
207{
208private:
212 format_help_base() = default;
213 format_help_base(format_help_base const & pf) = default;
214 format_help_base & operator=(format_help_base const & pf) = default;
215 format_help_base(format_help_base &&) = default;
216 format_help_base & operator=(format_help_base &&) = default;
217 ~format_help_base() = default;
218
223 format_help_base(std::vector<std::string> const & names, bool const advanced) :
224 command_names{names}, show_advanced_options{advanced}
225 {}
227
228public:
232 template <typename option_type, typename validator_type>
233 void add_option(option_type & value,
234 char const short_id,
235 std::string const & long_id,
236 std::string const & desc,
237 option_spec const spec,
238 validator_type && option_validator)
239 {
240 std::string id = prep_id_for_help(short_id, long_id) + " " + option_type_and_list_info(value);
241 std::string info{desc};
242 info += ((spec & option_spec::required) ? std::string{" "} : detail::to_string(" Default: ", value, ". "));
243 info += option_validator.get_help_page_message();
244 store_help_page_element([this, id, info] () { derived_t().print_list_item(id, info); }, spec);
245 }
246
250 void add_flag(bool & SEQAN3_DOXYGEN_ONLY(value),
251 char const short_id,
252 std::string const & long_id,
253 std::string const & desc,
254 option_spec const spec)
255 {
256 std::string id = prep_id_for_help(short_id, long_id);
257 store_help_page_element([this, id, desc] () { derived_t().print_list_item(id, desc); }, spec);
258 }
259
263 template <typename option_type, typename validator_type>
264 void add_positional_option(option_type & value,
265 std::string const & desc,
266 validator_type & option_validator)
267 {
268 std::string msg = option_validator.get_help_page_message();
269
270 positional_option_calls.push_back([this, &value, desc, msg] ()
271 {
272 ++positional_option_count;
273 derived_t().print_list_item(detail::to_string("\\fBARGUMENT-", positional_option_count, "\\fP ",
274 option_type_and_list_info(value)),
275 desc +
276 // a list at the end may be empty and thus have a default value
277 ((sequence_container<option_type> && !std::same_as<option_type, std::string>)
278 ? detail::to_string(" Default: ", value, ". ")
279 : std::string{" "}) +
280 msg);
281 });
282 }
283
287 void parse(argument_parser_meta_data & parser_meta)
288 {
289 meta = parser_meta;
290
291 derived_t().print_header();
292
293 if (!meta.synopsis.empty())
294 {
295 derived_t().print_section("Synopsis");
296 derived_t().print_synopsis();
297 }
298
299 if (!meta.description.empty())
300 {
301 derived_t().print_section("Description");
302 for (auto desc : meta.description)
303 print_line(desc);
304 }
305
306 if (!command_names.empty())
307 {
308 derived_t().print_section("Subcommands");
309 derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
310 for (std::string const & name : command_names)
311 derived_t().print_line("- \\fB" + name + "\\fP", false);
312 derived_t().print_line("See the respective help page for further details (e.g. by calling " +
313 meta.app_name + " " + command_names[0] + " -h).", true);
314 derived_t().print_line("The following options below belong to the top-level parser and need to be "
315 "specified \\fBbefore\\fP the subcommand key word. Every argument after the "
316 "subcommand key word is passed on to the corresponding sub-parser.", true);
317 }
318
319 // add positional options if specified
320 if (!positional_option_calls.empty())
321 derived_t().print_section("Positional Arguments");
322
323 // each call will evaluate the function derived_t().print_list_item()
324 for (auto f : positional_option_calls)
325 f();
326
327 // add options and flags if specified
328 if (!parser_set_up_calls.empty())
329 derived_t().print_section("Options");
330
331 // each call will evaluate the function derived_t().print_list_item()
332 for (auto f : parser_set_up_calls)
333 f();
334
335 if (!meta.examples.empty())
336 {
337 derived_t().print_section("Examples");
338 for (auto example : meta.examples)
339 print_line(example);
340 }
341
342 print_version();
343
344 print_legal();
345
346 derived_t().print_footer();
347
348 std::exit(EXIT_SUCCESS); // program should not continue from here
349 }
350
354 void add_section(std::string const & title, option_spec const spec)
355 {
356 store_help_page_element([this, title] () { derived_t().print_section(title); }, spec);
357 }
358
362 void add_subsection(std::string const & title, option_spec const spec)
363 {
364 store_help_page_element([this, title] () { derived_t().print_subsection(title); }, spec);
365 }
366
370 void add_line(std::string const & text, bool is_paragraph, option_spec const spec)
371 {
372 store_help_page_element([this, text, is_paragraph] () { derived_t().print_line(text, is_paragraph); }, spec);
373 }
374
378 void add_list_item(std::string const & key, std::string const & desc, option_spec const spec)
379 {
380 store_help_page_element([this, key, desc] () { derived_t().print_list_item(key, desc); }, spec);
381 }
382
397 argument_parser_meta_data meta;
398
400 friend derived_type;
401
402protected:
404 derived_type & derived_t()
405 {
406 return static_cast<derived_type &>(*this);
407 }
408
410 void print_synopsis()
411 {
412 for (unsigned i = 0; i < meta.synopsis.size(); ++i)
413 {
414 std::string text = "\\fB";
415 text.append(meta.synopsis[i]);
416 text.insert(text.find_first_of(" \t"), "\\fP");
417
418 derived_t().print_line(text, false);
419 }
420 }
421
425 void print_line(std::string const & text)
426 {
427 derived_t().print_line(text, true);
428 }
429
431 void print_version()
432 {
433 std::string const version_str{seqan3_version_cstring};
434
435 // Print version, date and url.
436 derived_t().print_section("Version");
437 derived_t().print_line(derived_t().in_bold("Last update: ") + meta.date, false);
438 derived_t().print_line(derived_t().in_bold(meta.app_name + " version: ") + meta.version, false);
439 derived_t().print_line(derived_t().in_bold("SeqAn version: ") + version_str, false);
440
441 if (!empty(meta.url))
442 {
443 derived_t().print_section("Url");
444 derived_t().print_line(meta.url, false);
445 }
446 }
447
449 void print_legal()
450 {
451 // Print legal stuff
452 if ((!empty(meta.short_copyright)) ||
453 (!empty(meta.long_copyright)) ||
454 (!empty(meta.citation)) ||
455 (!empty(meta.author)) ||
456 (!empty(meta.email)))
457 {
458 derived_t().print_section("Legal");
459
460 if (!empty(meta.short_copyright))
461 {
462 derived_t().print_line(derived_t().in_bold(meta.app_name + " Copyright: ") + meta.short_copyright,
463 false);
464 }
465
466 if (!empty(meta.author))
467 {
468 derived_t().print_line(derived_t().in_bold("Author: ") + meta.author, false);
469 }
470
471 if (!empty(meta.email))
472 {
473 derived_t().print_line(derived_t().in_bold("Contact: ") + meta.email, false);
474 }
475
476 derived_t().print_line(derived_t().in_bold("SeqAn Copyright: ") +
477 "2006-2021 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.", false);
478
479 if (!empty(meta.citation))
480 {
481 derived_t().print_line(derived_t().in_bold("In your academic works please cite: ") + meta.citation,
482 false);
483 }
484
485 if (!empty(meta.long_copyright))
486 {
487 derived_t().print_line("For full copyright and/or warranty information see " +
488 derived_t().in_bold("--copyright") + ".",
489 false);
490 }
491 }
492 }
493
495 std::vector<std::function<void()>> parser_set_up_calls;
497 std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
499 unsigned positional_option_count{0};
501 std::vector<std::string> command_names{};
503 bool show_advanced_options{true};
504
505private:
516 void store_help_page_element(std::function<void()> printer, option_spec const spec)
517 {
518 if (!(spec & option_spec::hidden) && (!(spec & option_spec::advanced) || show_advanced_options))
519 parser_set_up_calls.push_back(std::move(printer));
520 }
521};
522
523} // namespace seqan3::detail
T append(T... args)
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)
The <filesystem> header from C++17's standard library.
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:249
@ advanced
Definition: auxiliary.hpp:256
@ hidden
Definition: auxiliary.hpp:260
@ required
Definition: auxiliary.hpp:251
T insert(T... args)
The (most general) container concept as defined by the standard library.
A more refined container concept than seqan3::container.
constexpr char const * seqan3_version_cstring
The full version as null terminated string.
Definition: version.hpp:73
T push_back(T... args)
T reserve(T... args)
T size(T... args)
Provides traits for seqan3::type_list.
Provides traits to inspect some information of a type, for example its name.
Adaptations of concepts from the standard library.
Provides some standard validators for (positional) options.
Provides SeqAn version macros and global variables.