Sharg 1.1.0
The argument parser for bio-c++ tools.
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Modules Pages Concepts
Loading...
Searching...
No Matches
format_base.hpp
Go to the documentation of this file.
1// --------------------------------------------------------------------------------------------------------
2// Copyright (c) 2006-2023, Knut Reinert & Freie Universität Berlin
3// Copyright (c) 2016-2023, 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/sharg-parser/blob/main/LICENSE.md
6// --------------------------------------------------------------------------------------------------------
7
14#pragma once
15
16#include <sharg/auxiliary.hpp>
17#include <sharg/config.hpp>
20#include <sharg/validators.hpp>
21
22#if __has_include(<seqan3/version.hpp>)
23# include <seqan3/version.hpp>
24#endif
25
26namespace sharg::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
45 if constexpr (std::is_same_v<type, int8_t>)
46 return "signed 8 bit integer";
47 else if constexpr (std::is_same_v<type, uint8_t>)
48 return "unsigned 8 bit integer";
49 else if constexpr (std::is_same_v<type, int16_t>)
50 return "signed 16 bit integer";
51 else if constexpr (std::is_same_v<type, uint16_t>)
52 return "unsigned 16 bit integer";
53 else if constexpr (std::is_same_v<type, int32_t>)
54 return "signed 32 bit integer";
55 else if constexpr (std::is_same_v<type, uint32_t>)
56 return "unsigned 32 bit integer";
57 else if constexpr (std::is_same_v<type, int64_t>)
58 return "signed 64 bit integer";
59 else if constexpr (std::is_same_v<type, uint64_t>)
60 return "unsigned 64 bit integer";
61 else if constexpr (std::is_same_v<type, double>)
62 return "double";
63 else if constexpr (std::is_same_v<type, float>)
64 return "float";
65 else if constexpr (std::is_same_v<type, bool>)
66 return "bool";
67 else if constexpr (std::is_same_v<type, char>)
68 return "char";
69 else if constexpr (std::is_same_v<type, std::string>)
70 return "std::string";
71 else if constexpr (std::is_same_v<type, std::filesystem::path>)
72 return "std::filesystem::path";
73 else
74 return sharg::detail::type_name_as_string<value_type>;
75 }
76
81 template <detail::is_container_option container_type>
82 static std::string get_type_name_as_string(container_type const & )
83 {
84 typename container_type::value_type tmp{};
85 return get_type_name_as_string(tmp);
86 }
87
93 template <typename option_value_type>
94 static std::string option_type_and_list_info(option_value_type const & value)
95 {
96 return ("(\\fI" + get_type_name_as_string(value) + "\\fP)");
97 }
98
105 template <detail::is_container_option container_type>
106 static std::string option_type_and_list_info(container_type const & container)
107 {
108 return ("(\\fIList\\fP of \\fI" + get_type_name_as_string(container) + "\\fP)");
109 }
110
118 static std::string prep_id_for_help(char const short_id, std::string const & long_id)
119 {
120 // Build list item term.
121 std::string term;
122 if (short_id != '\0')
123 term = "\\fB-" + std::string(1, short_id) + "\\fP";
124
125 if (short_id != '\0' && !long_id.empty())
126 term.append(", ");
127
128 if (!long_id.empty())
129 term.append("\\fB--" + long_id + "\\fP");
130
131 return term;
132 }
133
140 std::string escape_special_xml_chars(std::string const & original)
141 {
142 std::string escaped;
143 escaped.reserve(original.size()); // will be at least as long
144
145 for (auto c : original)
146 {
147 if (c == '"')
148 escaped.append("&quot;");
149 else if (c == '\'')
150 escaped.append("&apos;");
151 else if (c == '&')
152 escaped.append("&amp;");
153 else if (c == '<')
154 escaped.append("&lt;");
155 else if (c == '>')
156 escaped.append("&gt;");
157 else
158 escaped.push_back(c);
159 }
160
161 return escaped;
162 }
163
170 static std::string expand_multiple_flags(std::string const & flag_cluster)
171 {
172 std::string tmp;
173 auto it{flag_cluster.begin()};
174
175 if (flag_cluster[0] == '-')
176 ++it;
177
178 for (; it != flag_cluster.end() - 1; ++it)
179 tmp.append({'-', *it, ',', ' '});
180
181 tmp.erase(tmp.find_last_of(',')); // remove last ', '
182 tmp.append({'a', 'n', 'd', ' ', '-', flag_cluster[flag_cluster.size() - 1]});
183
184 return tmp;
185 }
186};
187
193template <typename derived_type>
194class format_help_base : public format_base
195{
196private:
200 format_help_base() = default;
201 format_help_base(format_help_base const & pf) = default;
202 format_help_base & operator=(format_help_base const & pf) = default;
203 format_help_base(format_help_base &&) = default;
204 format_help_base & operator=(format_help_base &&) = default;
205 ~format_help_base() = default;
206
212 format_help_base(std::vector<std::string> const & names,
213 update_notifications const version_updates,
214 bool const advanced) :
215 version_check_dev_decision{version_updates},
216 command_names{names},
217 show_advanced_options{advanced}
218 {}
220
221public:
225 template <typename option_type, typename config_type>
226 void add_option(option_type & value, config_type const & config)
227 {
228 std::string id = prep_id_for_help(config.short_id, config.long_id) + " " + option_type_and_list_info(value);
229 std::string info{config.description};
230 if (config.default_message.empty())
231 info += ((config.required) ? std::string{" "} : detail::to_string(" Default: ", value, ". "));
232 else
233 info += detail::to_string(" Default: ", config.default_message, ". ");
234
235 info += config.validator.get_help_page_message();
236
237 store_help_page_element(
238 [this, id, info]()
239 {
240 derived_t().print_list_item(id, info);
241 },
242 config);
243 }
244
248 template <typename config_type>
249 void add_flag(bool & SHARG_DOXYGEN_ONLY(value), config_type const & config)
250 {
251 store_help_page_element(
252 [this, id = prep_id_for_help(config.short_id, config.long_id), description = config.description]()
253 {
254 derived_t().print_list_item(id, description);
255 },
256 config);
257 }
258
262 template <typename option_type, typename config_type>
263 void add_positional_option(option_type & value, config_type const & config)
264 {
265 positional_option_calls.push_back(
266 [this, &value, description = config.description, validator = config.validator]()
267 {
268 ++positional_option_count;
269 derived_t().print_list_item(detail::to_string("\\fBARGUMENT-",
270 positional_option_count,
271 "\\fP ",
272 option_type_and_list_info(value)),
273 description +
274 // a list at the end may be empty and thus have a default value
275 ((detail::is_container_option<option_type>)
276 ? detail::to_string(" Default: ", value, ". ")
277 : std::string{" "})
278 + validator.get_help_page_message());
279 });
280 // clang-format on
281 }
282
286 void parse(parser_meta_data & parser_meta)
287 {
288 meta = parser_meta;
289
290 derived_t().print_header();
291
292 if (!meta.synopsis.empty())
293 {
294 derived_t().print_section("Synopsis");
295 derived_t().print_synopsis();
296 }
297
298 if (!meta.description.empty())
299 {
300 derived_t().print_section("Description");
301 for (auto desc : meta.description)
302 print_line(desc);
303 }
304
305 if (!command_names.empty())
306 {
307 derived_t().print_section("Subcommands");
308 derived_t().print_line("This program must be invoked with one of the following subcommands:", false);
309 for (std::string const & name : command_names)
310 derived_t().print_line("- \\fB" + name + "\\fP", false);
311 derived_t().print_line("See the respective help page for further details (e.g. by calling " + meta.app_name
312 + " " + command_names[0] + " -h).",
313 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.",
317 true);
318 }
319
320 // add positional options if specified
321 if (!positional_option_calls.empty())
322 derived_t().print_section("Positional Arguments");
323
324 // each call will evaluate the function derived_t().print_list_item()
325 for (auto f : positional_option_calls)
326 f();
327
328 // There are always options because of the common options
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 // print Common options after developer options
336 derived_t().print_subsection("Common options");
337 derived_t().print_list_item("\\fB-h\\fP, \\fB--help\\fP", "Prints the help page.");
338 derived_t().print_list_item("\\fB-hh\\fP, \\fB--advanced-help\\fP",
339 "Prints the help page including advanced options.");
340 derived_t().print_list_item("\\fB--version\\fP", "Prints the version information.");
341 derived_t().print_list_item("\\fB--copyright\\fP", "Prints the copyright/license information.");
342 derived_t().print_list_item("\\fB--export-help\\fP (std::string)",
343 "Export the help page information. Value must be one of [html, man, ctd, cwl].");
344 if (version_check_dev_decision == update_notifications::on)
345 derived_t().print_list_item("\\fB--version-check\\fP (bool)",
346 "Whether to check for the newest app version. Default: true.");
347
348 if (!meta.examples.empty())
349 {
350 derived_t().print_section("Examples");
351 for (auto example : meta.examples)
352 print_line(example);
353 }
354
355 print_version();
356
357 print_legal();
358
359 derived_t().print_footer();
360 }
361
365 void add_section(std::string const & title, bool const advanced_only)
366 {
367 store_help_page_element(
368 [this, title]()
369 {
370 derived_t().print_section(title);
371 },
372 advanced_only,
373 false /* never hidden */);
374 }
375
379 void add_subsection(std::string const & title, bool const advanced_only)
380 {
381 store_help_page_element(
382 [this, title]()
383 {
384 derived_t().print_subsection(title);
385 },
386 advanced_only,
387 false /* never hidden */);
388 }
389
393 void add_line(std::string const & text, bool is_paragraph, bool const advanced_only)
394 {
395 store_help_page_element(
396 [this, text, is_paragraph]()
397 {
398 derived_t().print_line(text, is_paragraph);
399 },
400 advanced_only,
401 false /* never hidden */);
402 }
403
407 void add_list_item(std::string const & key, std::string const & desc, bool const advanced_only)
408 {
409 store_help_page_element(
410 [this, key, desc]()
411 {
412 derived_t().print_list_item(key, desc);
413 },
414 advanced_only,
415 false /* never hidden */);
416 }
417
432 parser_meta_data meta;
433
435 friend derived_type;
436
437protected:
439 update_notifications version_check_dev_decision{};
440
442 derived_type & derived_t()
443 {
444 return static_cast<derived_type &>(*this);
445 }
446
448 void print_synopsis()
449 {
450 for (unsigned i = 0; i < meta.synopsis.size(); ++i)
451 {
452 std::string text = "\\fB";
453 text.append(meta.synopsis[i]);
454 text.insert(text.find_first_of(" \t"), "\\fP");
455
456 derived_t().print_line(text, false);
457 }
458 }
459
463 void print_line(std::string const & text)
464 {
465 derived_t().print_line(text, true);
466 }
467
469 void print_version()
470 {
471 std::string const version_str{sharg::sharg_version_cstring};
472
473 // Print version, date and url.
474 derived_t().print_section("Version");
475 derived_t().print_line(derived_t().in_bold("Last update: ") + meta.date, false);
476 derived_t().print_line(derived_t().in_bold(meta.app_name + " version: ") + meta.version, false);
477 derived_t().print_line(derived_t().in_bold("Sharg version: ") + version_str, false);
478
479#ifdef SEQAN3_VERSION_CSTRING
480 std::string const seqan3_version_str{seqan3::seqan3_version_cstring};
481 derived_t().print_line(derived_t().in_bold("SeqAn version: ") + seqan3_version_str, false);
482#endif
483
484 if (!empty(meta.url))
485 {
486 derived_t().print_section("Url");
487 derived_t().print_line(meta.url, false);
488 }
489 }
490
492 void print_legal()
493 {
494 // Print legal stuff
495 if ((!empty(meta.short_copyright)) || (!empty(meta.long_copyright)) || (!empty(meta.citation))
496 || (!empty(meta.author)) || (!empty(meta.email)))
497 {
498 derived_t().print_section("Legal");
499
500 if (!empty(meta.short_copyright))
501 {
502 derived_t().print_line(derived_t().in_bold(meta.app_name + " Copyright: ") + meta.short_copyright,
503 false);
504 }
505
506 if (!empty(meta.author))
507 {
508 derived_t().print_line(derived_t().in_bold("Author: ") + meta.author, false);
509 }
510
511 if (!empty(meta.email))
512 {
513 derived_t().print_line(derived_t().in_bold("Contact: ") + meta.email, false);
514 }
515
516 derived_t().print_line(derived_t().in_bold("SeqAn Copyright: ")
517 + "2006-2023 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.",
518 false);
519
520 if (!empty(meta.citation))
521 {
522 derived_t().print_line(derived_t().in_bold("In your academic works please cite: ") + meta.citation,
523 false);
524 }
525
526 if (!empty(meta.long_copyright))
527 {
528 derived_t().print_line("For full copyright and/or warranty information see "
529 + derived_t().in_bold("--copyright") + ".",
530 false);
531 }
532 }
533 }
534
536 std::vector<std::function<void()>> parser_set_up_calls;
538 std::vector<std::function<void()>> positional_option_calls; // singled out to be printed on top
540 unsigned positional_option_count{0};
542 std::vector<std::string> command_names{};
544 bool show_advanced_options{true};
545
546private:
558 void store_help_page_element(std::function<void()> printer, bool const advanced, bool const hidden)
559 {
560 if (!(hidden) && (!(advanced) || show_advanced_options))
561 parser_set_up_calls.push_back(std::move(printer));
562 }
563
574 void store_help_page_element(std::function<void()> printer, config<auto> const & config)
575 {
576 if (!(config.hidden) && (!(config.advanced) || show_advanced_options))
577 parser_set_up_calls.push_back(std::move(printer));
578 }
579};
580
581} // namespace sharg::detail
T append(T... args)
Provides auxiliary information.
T begin(T... args)
Provides sharg::config class.
Provides the concept sharg::detail::is_container_option.
T empty(T... args)
T end(T... args)
T erase(T... args)
T find_first_of(T... args)
T find_last_of(T... args)
update_notifications
Indicates whether application allows automatic update notifications by the sharg::parser.
Definition: auxiliary.hpp:29
@ on
Automatic update notifications should be enabled.
T insert(T... args)
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.
constexpr char const * sharg_version_cstring
The full version as null terminated string.
Definition: version.hpp:66