SeqAn3  3.0.1
The Modern C++ library for sequence analysis.
format_help.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2020, 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 
24 #include <seqan3/version.hpp>
25 
26 namespace seqan3::detail
27 {
28 
39 class format_help : public format_help_base<format_help>
40 {
42  using base_type = format_help_base<format_help>;
43 
45  friend base_type;
46 
47 public:
51  format_help() = default;
52  format_help(format_help const & pf) = default;
53  format_help & operator=(format_help const &) = default;
54  format_help(format_help &&) = default;
55  format_help & operator=(format_help &&) = default;
56  ~format_help() = default;
57 
59  format_help(std::vector<std::string> const & names, bool const advanced = false) : base_type{names, advanced}
60  {};
62 
63 protected:
66  struct console_layout_struct
67  {
69  unsigned screenWidth;
71  unsigned defaultScreenWidth;
73  unsigned maximalScreenWidth;
75  unsigned minimalScreenWidth;
77  unsigned leftPadding;
79  unsigned centerPadding;
81  unsigned rightPadding;
83  unsigned leftColumnWidth;
85  unsigned rightColumnWidth;
87  unsigned rightColumnTab;
88 
90  console_layout_struct() :
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  unsigned cols = get_terminal_width();
96  screenWidth = (cols > 0) ? cols : defaultScreenWidth;
97  screenWidth = std::max(screenWidth, minimalScreenWidth);
98  screenWidth = std::min(screenWidth, maximalScreenWidth);
99  screenWidth -= rightPadding;
100 
101  rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
102  rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
103  }
104  };
105 
107  void print_header()
108  {
110 
111  std::cout << meta.app_name;
112  if (!empty(meta.short_description))
113  std::cout << " - " << meta.short_description;
114 
115  std::cout << "\n";
116  unsigned len = text_width(meta.app_name) + (empty(meta.short_description) ? 0 : 3) +
117  text_width(meta.short_description);
118  std::fill_n(out, len, '=');
119  std::cout << '\n';
120  }
121 
125  void print_section(std::string const & title)
126  {
128  std::cout << '\n' << to_text("\\fB");
129  std::transform(title.begin(), title.end(), out, [] (unsigned char c) { return std::toupper(c); });
130  std::cout << to_text("\\fP") << '\n';
131  prev_was_paragraph = false;
132  }
133 
137  void print_subsection(std::string const & title)
138  {
140  std::cout << '\n' << to_text("\\fB");
141  std::fill_n(out, layout.leftPadding / 2, ' ');
142  std::cout << title << to_text("\\fP") << '\n';
143  prev_was_paragraph = false;
144  }
145 
151  void print_line(std::string const & text, bool const line_is_paragraph)
152  {
153  if (prev_was_paragraph)
154  std::cout << '\n';
155 
157  std::fill_n(out, layout.leftPadding, ' ');
158  print_text(text, layout.leftPadding);
159  prev_was_paragraph = line_is_paragraph;
160  }
161 
176  void print_list_item(std::string const & term, std::string const & desc)
177  {
178  if (prev_was_paragraph)
179  std::cout << '\n';
180 
182 
183  // Print term.
184  std::fill_n(out, layout.leftPadding, ' ');
185  std::cout << to_text(term);
186  unsigned pos = layout.leftPadding + term.size();
187  if (pos + layout.centerPadding > layout.rightColumnTab)
188  {
189  std::cout << '\n';
190  pos = 0;
191  }
192  std::fill_n(out, layout.rightColumnTab - pos, ' ');
193  print_text(desc, layout.rightColumnTab);
194 
195  prev_was_paragraph = false;
196  }
197 
199  void print_version()
200  {
202 
203  // Print version, date and url.
204  std::cout << "\n" << to_text("\\fB") << "VERSION" << to_text("\\fP") << "\n";
205  std::fill_n(out, layout.leftPadding, ' ');
206  std::cout << to_text("\\fB") << "Last update: " << to_text("\\fP") << meta.date << "\n";
207  std::fill_n(out, layout.leftPadding, ' ');
208  std::cout << to_text("\\fB") << meta.app_name << " version: " << to_text("\\fP") << meta.version << "\n";
209  std::fill_n(out, layout.leftPadding, ' ');
210  std::cout << to_text("\\fB") << "SeqAn version: " << to_text("\\fP") << SEQAN3_VERSION_MAJOR << '.'
212 
213  if (!empty(meta.url))
214  {
215  std::cout << "\n" << to_text("\\fB") << "URL" << to_text("\\fP") << "\n";
216  std::fill_n(out, layout.leftPadding, ' ');
217  std::cout << meta.url << "\n";
218  }
219  std::cout << "\n";
220  }
221 
223  void print_footer()
224  {
225  print_version();
226 
228 
229  // Print legal stuff
230  if ((!empty(meta.short_copyright)) || (!empty(meta.long_copyright)) || (!empty(meta.citation)))
231  {
232  std::cout << "\n" << to_text("\\fB") << "LEGAL" << to_text("\\fP") << "\n";
233 
234  if (!empty(meta.short_copyright))
235  {
236  std::fill_n(out, layout.leftPadding, ' ');
237  std::cout << to_text("\\fB") << meta.app_name << " Copyright: "
238  << to_text("\\fP") << meta.short_copyright << "\n";
239  }
240  std::fill_n(out, layout.leftPadding, ' ');
241  std::cout << to_text("\\fB") << "SeqAn Copyright: " << to_text("\\fP")
242  << "2006-2015 Knut Reinert, FU-Berlin; released under the 3-clause BSDL.\n";
243  if (!empty(meta.citation))
244  {
245  std::fill_n(out, layout.leftPadding, ' ');
246  std::cout << to_text("\\fB") << "In your academic works please cite: " << to_text("\\fP")
247  << meta.citation << "\n";
248  }
249  if (!empty(meta.long_copyright))
250  {
251  std::fill_n(out, layout.leftPadding, ' ');
252  std::cout << "For full copyright and/or warranty information see " << to_text("\\fB")
253  << "--copyright" << to_text("\\fP") << ".\n";
254  }
255  }
256  }
257 
261  std::string to_text(std::string const & str)
262  {
263  std::string result;
264 
265  for (auto it = str.begin(); it != str.end(); ++it)
266  {
267  if (*it == '\\')
268  {
269  // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
270  ++it;
271  assert(it != str.end());
272  if (*it == '-')
273  {
274  result.push_back(*it);
275  }
276  else if (*it == 'f')
277  {
278  ++it;
279  assert(it != str.end());
280  if (*it == 'I')
281  {
282  if (is_terminal())
283  result.append("\033[4m");
284  }
285  else if (*it == 'B')
286  {
287  if (is_terminal())
288  result.append("\033[1m");
289  }
290  else if (*it == 'P')
291  {
292  if (is_terminal())
293  result.append("\033[0m");
294  }
295  else
296  {
297  result.append("\\f");
298  result.push_back(*it);
299  }
300  }
301  else
302  {
303  result.push_back('\\');
304  result.push_back(*it);
305  }
306  }
307  else
308  {
309  result.push_back(*it);
310  }
311  }
312 
313  return result;
314  }
315 
320  unsigned text_width(std::string const & text)
321  {
322  unsigned result = 0;
323 
324  for (unsigned i = 0; i < text.size(); ++i)
325  {
326  if (text[i] != '\\')
327  {
328  result += 1;
329  continue;
330  }
331 
332  if (i + 1 == text.size())
333  {
334  result += 1; // Will print "\\".
335  continue;
336  }
337 
338  if (text[i + 1] == '\\' || text[i + 1] == '-')
339  {
340  i += 1;
341  result += 1;
342  continue; // Will print '\\' or '-'.
343  }
344 
345  if (i + 2 == text.size())
346  {
347  i += 1;
348  result += 2; // Will print two chars.
349  continue;
350  }
351 
352  if (text[i + 1] == 'f')
353  {
354  if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
355  i += 2; // Skip f and {B, I, P}.
356  else
357  result += 1;
358  }
359  }
360 
361  return result;
362  }
363 
368  void print_text(std::string const & text, unsigned const tab)
369  {
370  unsigned pos = tab;
372 
373  // Tokenize the text.
374  std::istringstream iss(text.c_str());
377  std::ranges::back_inserter(tokens));
378 
379  // Print the text.
380  assert(pos <= tab);
381  std::fill_n(out, tab - pos, ' '); // go to tab
382 
383  pos = tab;
384  typedef std::vector<std::string>::const_iterator TConstIter;
385  for (TConstIter it = tokens.begin(); it != tokens.end(); ++it)
386  {
387  if (it == tokens.begin())
388  {
389  std::cout << to_text(*it);
390  pos += text_width(*it);
391  if (pos > layout.screenWidth)
392  {
393  std::cout << '\n';
394  std::fill_n(out, tab, ' ');
395  pos = tab;
396  }
397  }
398  else
399  {
400  if (pos + 1 + text_width(*it) > layout.screenWidth)
401  {
402  // Would go over screen with next, print current word on next line.
403  std::cout << '\n';
404  fill_n(out, tab, ' ');
405  std::cout << to_text(*it);
406  pos = tab + text_width(*it);
407  }
408  else
409  {
410  std::cout << ' ';
411  std::cout << to_text(*it);
412  pos += text_width(*it) + 1;
413  }
414  }
415  }
416  if (!empty(tokens))
417  std::cout << '\n';
418  }
419 
421  bool prev_was_paragraph{false};
423  console_layout_struct layout{};
424 };
425 
435 class format_short_help : public format_help
436 {
437 public:
441  void parse(argument_parser_meta_data const & parser_meta)
442  {
443  meta = parser_meta;
444 
445  print_header();
446 
447  if (!parser_meta.synopsis.empty())
448  print_synopsis();
449 
450  print_line("Try -h or --help for more information.\n", true);
451 
452  std::exit(EXIT_SUCCESS);
453  }
454 };
455 
465 class format_version : public format_help
466 {
467 public:
471  void parse(argument_parser_meta_data & parser_meta)
472  {
473  meta = parser_meta;
474 
475  print_header();
476  print_version();
477 
478  std::exit(EXIT_SUCCESS); // program should not continue from here
479  }
480 };
481 
491 class format_copyright : public format_help
492 {
493 public:
497  void parse(argument_parser_meta_data const & parser_meta)
498  {
499  meta = parser_meta;
500  debug_stream_type stream{std::cout};
501  std::string seqan_license{
502 R"(Copyright (c) 2006-2020, Knut Reinert & Freie Universität Berlin
503 Copyright (c) 2016-2020, Knut Reinert & MPI für molekulare Genetik
504 All rights reserved.
505 
506 Redistribution and use in source and binary forms, with or without
507 modification, are permitted provided that the following conditions are met:
508 
509  * Redistributions of source code must retain the above copyright
510  notice, this list of conditions and the following disclaimer.
511  * Redistributions in binary form must reproduce the above copyright
512  notice, this list of conditions and the following disclaimer in the
513  documentation and/or other materials provided with the distribution.
514  * Neither the name of Knut Reinert or the FU Berlin nor the names of
515  its contributors may be used to endorse or promote products derived
516  from this software without specific prior written permission.
517 
518 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
519 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
520 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
521 ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
522 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
523 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
524 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
525 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
526 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
527 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
528 DAMAGE.)"};
529 
530  stream << views::repeat_n('=', 80) << to_text("\n\\fB") << "Copyright information for "
531  << meta.app_name << ":\n" << to_text("\\fP") << views::repeat_n('-', 80) << '\n';
532 
533  if (!empty(meta.long_copyright))
534  {
535  stream << to_text("\\fP") << meta.long_copyright << "\n";
536  }
537  else if (!empty(meta.short_copyright))
538  {
539  stream << to_text("\\fP") << meta.app_name << " full copyright information not available. Displaying"
540  << " short copyright information instead:\n" << to_text("\\fP") << meta.short_copyright << "\n";
541  }
542  else
543  {
544  stream << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
545  }
546 
547  stream << views::repeat_n('=', 80) << to_text("\n\\fB")
548  << "This program contains SeqAn code licensed under the following terms:\n" << to_text("\\fP")
549  << views::repeat_n('-', 80) << '\n' << seqan_license << '\n';
550 
551  std::exit(EXIT_SUCCESS);
552  }
553 };
554 
555 } // namespace seqan3::detail
std::string
std::vector< std::string >
std::string::size
T size(T... args)
SEQAN3_VERSION_MINOR
#define SEQAN3_VERSION_MINOR
The minor version as MACRO.
Definition: version.hpp:22
std::istringstream
version.hpp
Provides SeqAn version macros and global variables.
debug_stream_type.hpp
Provides seqan3::debug_stream and related types.
iostream
terminal.hpp
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
std::cout
repeat_n.hpp
Provides seqan3::views::repeat_n.
std::string::c_str
T c_str(T... args)
std::transform
T transform(T... args)
std::min
T min(T... args)
format_base.hpp
Provides the format_base struct containing all helper functions that are needed in all formats.
std::ostream_iterator
SEQAN3_VERSION_MAJOR
#define SEQAN3_VERSION_MAJOR
The major version as MACRO.
Definition: version.hpp:20
std::istream_iterator
std::string::begin
T begin(T... args)
cassert
std::empty
T empty(T... args)
SEQAN3_VERSION_PATCH
#define SEQAN3_VERSION_PATCH
The patch version as MACRO.
Definition: version.hpp:24
seqan3::views::repeat_n
constexpr auto repeat_n
A view factory that repeats a given value n times.
Definition: repeat_n.hpp:94
std::string::end
T end(T... args)
std::max
T max(T... args)
std::fill_n
T fill_n(T... args)
std::exit
T exit(T... args)