SeqAn3 3.4.0-rc.1
The Modern C++ library for sequence analysis.
Loading...
Searching...
No Matches
format_base.hpp
Go to the documentation of this file.
1// SPDX-FileCopyrightText: 2006-2024 Knut Reinert & Freie Universität Berlin
2// SPDX-FileCopyrightText: 2016-2024 Knut Reinert & MPI für molekulare Genetik
3// SPDX-License-Identifier: BSD-3-Clause
4
11#pragma once
12
13#include <filesystem>
14#include <iostream>
15#include <limits>
16#include <sstream>
17#include <string>
18
24#include <seqan3/version.hpp>
25
26namespace seqan3::detail
27{
28
33class format_base
34{
35protected:
40 template <typename value_type>
41 static std::string get_type_name_as_string(value_type const & )
42 {
43 using type = std::decay_t<value_type>;
44 using types = type_list<int8_t,
45 uint8_t,
46 int16_t,
47 uint16_t,
48 int32_t,
49 uint32_t,
50 int64_t,
51 uint64_t,
52 double,
53 float,
54 bool,
55 char,
58 std::vector<std::string> names{"signed 8 bit integer",
59 "unsigned 8 bit integer",
60 "signed 16 bit integer",
61 "unsigned 16 bit integer",
62 "signed 32 bit integer",
63 "unsigned 32 bit integer",
64 "signed 64 bit integer",
65 "unsigned 64 bit integer",
66 "double",
67 "float",
68 "bool",
69 "char",
70 "std::string",
71 "std::filesystem::path"};
72
73 if constexpr (list_traits::contains<type, types>)
74 return names[list_traits::find<type, types>];
75 else
76 return detail::type_name_as_string<value_type>;
77 }
78
83 template <detail::is_container_option container_type>
84 static std::string get_type_name_as_string(container_type const & )
85 {
86 typename container_type::value_type tmp{};
87 return get_type_name_as_string(tmp);
88 }
89
95 template <typename option_value_type>
96 static std::string option_type_and_list_info(option_value_type const & value)
97 {
98 return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
99 }
100
107 template <detail::is_container_option container_type>
108 static std::string option_type_and_list_info(container_type const & container)
109 {
110 return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP)");
111 }
112
120 static std::string prep_id_for_help(char const short_id, std::string const & long_id)
121 {
122 // Build list item term.
123 std::string term;
124 if (short_id != '\0')
125 term = "\\fB-" + std::string(1, short_id) + "\\fP";
126
127 if (short_id != '\0' && !long_id.empty())
128 term.append(", ");
129
130 if (!long_id.empty())
131 term.append("\\fB--" + long_id + "\\fP");
132
133 return term;
134 }
135
142 std::string escape_special_xml_chars(std::string const & original)
143 {
144 std::string escaped;
145 escaped.reserve(original.size()); // will be at least as long
146
147 for (auto c : original)
148 {
149 if (c == '"')
150 escaped.append("&quot;");
151 else if (c == '\'')
152 escaped.append("&apos;");
153 else if (c == '&')
154 escaped.append("&amp;");
155 else if (c == '<')
156 escaped.append("&lt;");
157 else if (c == '>')
158 escaped.append("&gt;");
159 else
160 escaped.push_back(c);
161 }
162
163 return escaped;
164 }
165
172 static std::string expand_multiple_flags(std::string const & flag_cluster)
173 {
174 std::string tmp;
175 auto it{flag_cluster.begin()};
176
177 if (flag_cluster[0] == '-')
178 ++it;
179
180 for (; it != flag_cluster.end() - 1; ++it)
181 tmp.append({'-', *it, ',', ' '});
182
183 tmp.erase(tmp.find_last_of(',')); // remove last ', '
184 tmp.append({'a', 'n', 'd', ' ', '-', flag_cluster[flag_cluster.size() - 1]});
185
186 return tmp;
187 }
188};
189
195template <typename derived_type>
196class format_help_base : public format_base
197{
198private:
202 format_help_base() = default;
203 format_help_base(format_help_base const & pf) = default;
204 format_help_base & operator=(format_help_base const & pf) = default;
205 format_help_base(format_help_base &&) = default;
206 format_help_base & operator=(format_help_base &&) = default;
207 ~format_help_base() = default;
208
213 format_help_base(std::vector<std::string> const & names, bool const advanced) :
214 command_names{names},
215 show_advanced_options{advanced}
216 {}
218
219public:
223 template <typename option_type, typename validator_type>
224 void add_option(option_type & value,
225 char const short_id,
226 std::string const & long_id,
227 std::string const & desc,
228 option_spec const spec,
229 validator_type && option_validator)
230 {
231 std::string id = prep_id_for_help(short_id, long_id) + " " + option_type_and_list_info(value);
232 std::string info{desc};
233 info += ((spec & option_spec::required) ? std::string{" "} : detail::to_string(" Default: ", value, ". "));
234 info += option_validator.get_help_page_message();
235 store_help_page_element(
236 [this, id, info]()
237 {
238 derived_t().print_list_item(id, info);
239 },
240 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(
254 [this, id, desc]()
255 {
256 derived_t().print_list_item(id, desc);
257 },
258 spec);
259 }
260
264 template <typename option_type, typename validator_type>
265 void add_positional_option(option_type & value, std::string const & desc, validator_type & option_validator)
266 {
267 std::string msg = option_validator.get_help_page_message();
268
269 positional_option_calls.push_back(
270 [this, &value, desc, msg]()
271 {
272 ++positional_option_count;
273 derived_t().print_list_item(detail::to_string("\\fBARGUMENT-",
274 positional_option_count,
275 "\\fP ",
276 option_type_and_list_info(value)),
277 desc +
278 // a list at the end may be empty and thus have a default value
279 ((detail::is_container_option<option_type>)
280 ? detail::to_string(" Default: ", value, ". ")
281 : std::string{" "})
282 + msg);
283 });
284 }
285
289 void parse(argument_parser_meta_data & parser_meta)
290 {
291 meta = parser_meta;
292
293 derived_t().print_header();
294
295 if (!meta.synopsis.empty())
296 {
297 derived_t().print_section("Synopsis");
298 derived_t().print_synopsis();
299 }
300
301 if (!meta.description.empty())
302 {
303 derived_t().print_section("Description");
304 for (auto desc : meta.description)
305 print_line(desc);
306 }
307
308 if (!command_names.empty())
309 {
310 derived_t().print_section("Subcommands");
311 derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
312 for (std::string const & name : command_names)
313 derived_t().print_line("- \\fB" + name + "\\fP", false);
314 derived_t().print_line("See the respective help page for further details (e.g. by calling " + meta.app_name
315 + " " + command_names[0] + " -h).",
316 true);
317 derived_t().print_line("The following options below belong to the top-level parser and need to be "
318 "specified \\fBbefore\\fP the subcommand key word. Every argument after the "
319 "subcommand key word is passed on to the corresponding sub-parser.",
320 true);
321 }
322
323 // add positional options if specified
324 if (!positional_option_calls.empty())
325 derived_t().print_section("Positional Arguments");
326
327 // each call will evaluate the function derived_t().print_list_item()
328 for (auto f : positional_option_calls)
329 f();
330
331 // add options and flags if specified
332 if (!parser_set_up_calls.empty())
333 derived_t().print_section("Options");
334
335 // each call will evaluate the function derived_t().print_list_item()
336 for (auto f : parser_set_up_calls)
337 f();
338
339 if (!meta.examples.empty())
340 {
341 derived_t().print_section("Examples");
342 for (auto example : meta.examples)
343 print_line(example);
344 }
345
346 print_version();
347
348 print_legal();
349
350 derived_t().print_footer();
351
352 std::exit(EXIT_SUCCESS); // program should not continue from here
353 }
354
358 void add_section(std::string const & title, option_spec const spec)
359 {
360 store_help_page_element(
361 [this, title]()
362 {
363 derived_t().print_section(title);
364 },
365 spec);
366 }
367
371 void add_subsection(std::string const & title, option_spec const spec)
372 {
373 store_help_page_element(
374 [this, title]()
375 {
376 derived_t().print_subsection(title);
377 },
378 spec);
379 }
380
384 void add_line(std::string const & text, bool is_paragraph, option_spec const spec)
385 {
386 store_help_page_element(
387 [this, text, is_paragraph]()
388 {
389 derived_t().print_line(text, is_paragraph);
390 },
391 spec);
392 }
393
397 void add_list_item(std::string const & key, std::string const & desc, option_spec const spec)
398 {
399 store_help_page_element(
400 [this, key, desc]()
401 {
402 derived_t().print_list_item(key, desc);
403 },
404 spec);
405 }
406
421 argument_parser_meta_data meta;
422
424 friend derived_type;
425
426protected:
428 derived_type & derived_t()
429 {
430 return static_cast<derived_type &>(*this);
431 }
432
434 void print_synopsis()
435 {
436 for (unsigned i = 0; i < meta.synopsis.size(); ++i)
437 {
438 std::string text = "\\fB";
439 text.append(meta.synopsis[i]);
440 text.insert(text.find_first_of(" \t"), "\\fP");
441
442 derived_t().print_line(text, false);
443 }
444 }
445
449 void print_line(std::string const & text)
450 {
451 derived_t().print_line(text, true);
452 }
453
455 void print_version()
456 {
457 std::string const version_str{seqan3_version_cstring};
458
459 // Print version, date and url.
460 derived_t().print_section("Version");
461 derived_t().print_line(derived_t().in_bold("Last update: ") + meta.date, false);
462 derived_t().print_line(derived_t().in_bold(meta.app_name + " version: ") + meta.version, false);
463 derived_t().print_line(derived_t().in_bold("SeqAn version: ") + version_str, false);
464
465 if (!empty(meta.url))
466 {
467 derived_t().print_section("Url");
468 derived_t().print_line(meta.url, false);
469 }
470 }
471
473 void print_legal()
474 {
475 // Print legal stuff
476 if ((!empty(meta.short_copyright)) || (!empty(meta.long_copyright)) || (!empty(meta.citation))
477 || (!empty(meta.author)) || (!empty(meta.email)))
478 {
479 derived_t().print_section("Legal");
480
481 if (!empty(meta.short_copyright))
482 {
483 derived_t().print_line(derived_t().in_bold(meta.app_name + " Copyright: ") + meta.short_copyright,
484 false);
485 }
486
487 if (!empty(meta.author))
488 {
489 derived_t().print_line(derived_t().in_bold("Author: ") + meta.author, false);
490 }
491
492 if (!empty(meta.email))
493 {
494 derived_t().print_line(derived_t().in_bold("Contact: ") + meta.email, false);
495 }
496
497 derived_t().print_line(derived_t().in_bold("SeqAn Copyright: ")
498 + "2006-2023 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.",
499 false);
500
501 if (!empty(meta.citation))
502 {
503 derived_t().print_line(derived_t().in_bold("In your academic works please cite: ") + meta.citation,
504 false);
505 }
506
507 if (!empty(meta.long_copyright))
508 {
509 derived_t().print_line("For full copyright and/or warranty information see "
510 + derived_t().in_bold("--copyright") + ".",
511 false);
512 }
513 }
514 }
515
517 std::vector<std::function<void()>> parser_set_up_calls;
519 std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
521 unsigned positional_option_count{0};
523 std::vector<std::string> command_names{};
525 bool show_advanced_options{true};
526
527private:
538 void store_help_page_element(std::function<void()> printer, option_spec const spec)
539 {
540 if (!(spec & option_spec::hidden) && (!(spec & option_spec::advanced) || show_advanced_options))
541 parser_set_up_calls.push_back(std::move(printer));
542 }
543};
544
545} // 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:245
@ advanced
Definition auxiliary.hpp:252
@ hidden
Definition auxiliary.hpp:256
@ required
Definition auxiliary.hpp:247
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:64
SeqAn specific customisations in the standard namespace.
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.
Hide me