SeqAn3 3.1.0
The Modern C++ library for sequence analysis.
format_help.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
15#pragma once
16
17#include <cassert>
18#include <iostream>
19
23
24namespace seqan3::detail
25{
26
39class format_help : public format_help_base<format_help>
40{
42 using base_type = format_help_base<format_help>;
43
45 friend base_type;
46public:
50 format_help() = default;
51 format_help(format_help const & pf) = default;
52 format_help & operator=(format_help const &) = default;
53 format_help(format_help &&) = default;
54 format_help & operator=(format_help &&) = default;
55 ~format_help() = default;
56
58 format_help(std::vector<std::string> const & names, bool const advanced = false) : base_type{names, advanced}
59 {};
61
62protected:
65 struct console_layout_struct
66 {
68 uint32_t screenWidth;
70 uint32_t defaultScreenWidth;
72 uint32_t maximalScreenWidth;
74 uint32_t minimalScreenWidth;
76 uint32_t leftPadding;
78 uint32_t centerPadding;
80 uint32_t rightPadding;
82 uint32_t leftColumnWidth;
84 uint32_t rightColumnWidth;
86 uint32_t rightColumnTab;
87
90 console_layout_struct(uint32_t const terminal_width) :
91 screenWidth{0}, defaultScreenWidth{80}, maximalScreenWidth{120}, minimalScreenWidth{40},
92 leftPadding{4}, centerPadding{2}, rightPadding{2}, leftColumnWidth{4}, rightColumnWidth{0}
93 {
94 // Guess terminal screen width and set into layout.
95 screenWidth = (terminal_width > 0) ? terminal_width : defaultScreenWidth;
96 screenWidth = std::max(screenWidth, minimalScreenWidth);
97 screenWidth = std::min(screenWidth, maximalScreenWidth);
98 screenWidth -= rightPadding;
99
100 rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
101 rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
102 }
103
105 console_layout_struct() : console_layout_struct{get_terminal_width()} {}
106 };
107
109 void print_header()
110 {
112
113 std::cout << meta.app_name;
114 if (!empty(meta.short_description))
115 std::cout << " - " << meta.short_description;
116
117 std::cout << "\n";
118 unsigned len = text_width(meta.app_name) + (empty(meta.short_description) ? 0 : 3) +
119 text_width(meta.short_description);
120 std::fill_n(out, len, '=');
121 std::cout << '\n';
122 }
123
127 void print_section(std::string const & title)
128 {
130 std::cout << '\n' << to_text("\\fB");
131 std::transform(title.begin(), title.end(), out, [] (unsigned char c) { return std::toupper(c); });
132 std::cout << to_text("\\fP") << '\n';
133 prev_was_paragraph = false;
134 }
135
139 void print_subsection(std::string const & title)
140 {
142 std::cout << '\n';
143 std::fill_n(out, layout.leftPadding / 2, ' ');
144 std::cout << in_bold(title) << '\n';
145 prev_was_paragraph = false;
146 }
147
153 void print_line(std::string const & text, bool const line_is_paragraph)
154 {
155 if (prev_was_paragraph)
156 std::cout << '\n';
157
159 std::fill_n(out, layout.leftPadding, ' ');
160 print_text(text, layout.leftPadding);
161 prev_was_paragraph = line_is_paragraph;
162 }
163
178 void print_list_item(std::string const & term, std::string const & desc)
179 {
180 if (prev_was_paragraph)
181 std::cout << '\n';
182
184
185 // Print term.
186 std::fill_n(out, layout.leftPadding, ' ');
187 std::cout << to_text(term);
188 unsigned pos = layout.leftPadding + term.size();
189 if (pos + layout.centerPadding > layout.rightColumnTab)
190 {
191 std::cout << '\n';
192 pos = 0;
193 }
194 std::fill_n(out, layout.rightColumnTab - pos, ' ');
195 print_text(desc, layout.rightColumnTab);
196
197 prev_was_paragraph = false;
198 }
199
201 void print_footer()
202 {
203 // no footer
204 }
205
209 std::string to_text(std::string const & str)
210 {
211 std::string result;
212
213 for (auto it = str.begin(); it != str.end(); ++it)
214 {
215 if (*it == '\\')
216 {
217 // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
218 ++it;
219 assert(it != str.end());
220 if (*it == '-')
221 {
222 result.push_back(*it);
223 }
224 else if (*it == 'f')
225 {
226 ++it;
227 assert(it != str.end());
228 if (*it == 'I')
229 {
230 if (is_terminal())
231 result.append("\033[4m");
232 }
233 else if (*it == 'B')
234 {
235 if (is_terminal())
236 result.append("\033[1m");
237 }
238 else if (*it == 'P')
239 {
240 if (is_terminal())
241 result.append("\033[0m");
242 }
243 else
244 {
245 result.append("\\f");
246 result.push_back(*it);
247 }
248 }
249 else
250 {
251 result.push_back('\\');
252 result.push_back(*it);
253 }
254 }
255 else
256 {
257 result.push_back(*it);
258 }
259 }
260
261 return result;
262 }
263
268 unsigned text_width(std::string const & text)
269 {
270 unsigned result = 0;
271
272 for (unsigned i = 0; i < text.size(); ++i)
273 {
274 if (text[i] != '\\')
275 {
276 result += 1;
277 continue;
278 }
279
280 if (i + 1 == text.size())
281 {
282 result += 1; // Will print "\\".
283 continue;
284 }
285
286 if (text[i + 1] == '\\' || text[i + 1] == '-')
287 {
288 i += 1;
289 result += 1;
290 continue; // Will print '\\' or '-'.
291 }
292
293 if (i + 2 == text.size())
294 {
295 i += 1;
296 result += 2; // Will print two chars.
297 continue;
298 }
299
300 if (text[i + 1] == 'f')
301 {
302 if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
303 i += 2; // Skip f and {B, I, P}.
304 else
305 result += 1;
306 }
307 }
308
309 return result;
310 }
311
316 void print_text(std::string const & text, unsigned const tab)
317 {
318 unsigned pos = tab;
320
321 // Tokenize the text.
322 std::istringstream iss(text.c_str());
325 std::cpp20::back_inserter(tokens));
326
327 // Print the text.
328 assert(pos <= tab);
329 std::fill_n(out, tab - pos, ' '); // go to tab
330
331 pos = tab;
333 for (TConstIter it = tokens.begin(); it != tokens.end(); ++it)
334 {
335 if (it == tokens.begin())
336 {
337 std::cout << to_text(*it);
338 pos += text_width(*it);
339 if (pos > layout.screenWidth)
340 {
341 std::cout << '\n';
342 std::fill_n(out, tab, ' ');
343 pos = tab;
344 }
345 }
346 else
347 {
348 if (pos + 1 + text_width(*it) > layout.screenWidth)
349 {
350 // Would go over screen with next, print current word on next line.
351 std::cout << '\n';
352 fill_n(out, tab, ' ');
353 std::cout << to_text(*it);
354 pos = tab + text_width(*it);
355 }
356 else
357 {
358 std::cout << ' ';
359 std::cout << to_text(*it);
360 pos += text_width(*it) + 1;
361 }
362 }
363 }
364 if (!empty(tokens))
365 std::cout << '\n';
366 }
367
372 std::string in_bold(std::string const & str)
373 {
374 return to_text("\\fB") + str + to_text("\\fP");
375 }
376
378 bool prev_was_paragraph{false};
379
381 friend struct ::seqan3::detail::test_accessor;
382
384 console_layout_struct layout{};
385};
386
398class format_short_help : public format_help
399{
400public:
404 void parse(argument_parser_meta_data const & parser_meta)
405 {
406 meta = parser_meta;
407
408 print_header();
409
410 if (!parser_meta.synopsis.empty())
411 print_synopsis();
412
413 print_line("Try -h or --help for more information.\n", true);
414
415 std::exit(EXIT_SUCCESS);
416 }
417};
418
430class format_version : public format_help
431{
432public:
436 void parse(argument_parser_meta_data & parser_meta)
437 {
438 meta = parser_meta;
439
440 print_header();
441 print_version();
442
443 std::exit(EXIT_SUCCESS); // program should not continue from here
444 }
445};
446
458class format_copyright : public format_help
459{
460public:
464 void parse(argument_parser_meta_data const & parser_meta)
465 {
466 meta = parser_meta;
467 debug_stream_type stream{std::cout};
468 std::string seqan_license{
469R"(Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
470Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
471All rights reserved.
472
473Redistribution and use in source and binary forms, with or without
474modification, are permitted provided that the following conditions are met:
475
476 * Redistributions of source code must retain the above copyright
477 notice, this list of conditions and the following disclaimer.
478 * Redistributions in binary form must reproduce the above copyright
479 notice, this list of conditions and the following disclaimer in the
480 documentation and/or other materials provided with the distribution.
481 * Neither the name of Knut Reinert or the FU Berlin nor the names of
482 its contributors may be used to endorse or promote products derived
483 from this software without specific prior written permission.
484
485THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
486AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
487IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
488ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
489FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
490DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
491SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
492CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
493LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
494OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
495DAMAGE.)"};
496
497 stream << std::string(80, '=') << "\n"
498 << in_bold("Copyright information for " + meta.app_name + ":\n")
499 << std::string(80, '-') << '\n';
500
501 if (!empty(meta.long_copyright))
502 {
503 stream << to_text("\\fP") << meta.long_copyright << "\n";
504 }
505 else if (!empty(meta.short_copyright))
506 {
507 stream << in_bold(meta.app_name + " full copyright information not available. " +
508 "Displaying short copyright information instead:\n" )
509 << meta.short_copyright << "\n";
510 }
511 else
512 {
513 stream << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
514 }
515
516 stream << std::string(80, '=') << '\n'
517 << in_bold("This program contains SeqAn code licensed under the following terms:\n")
518 << std::string(80, '-') << '\n' << seqan_license << '\n';
519
520 std::exit(EXIT_SUCCESS);
521 }
522};
523
524} // namespace seqan3::detail
T append(T... args)
T begin(T... args)
T c_str(T... args)
T empty(T... args)
T end(T... args)
T exit(T... args)
T fill_n(T... args)
Provides the format_base struct containing all helper functions that are needed in all formats.
@ advanced
Definition: auxiliary.hpp:256
T max(T... args)
T min(T... args)
T push_back(T... args)
T size(T... args)
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
Forward declares seqan3::detail::test_accessor.
T transform(T... args)