SeqAn3  3.2.0
The Modern C++ library for sequence analysis.
format_help.hpp
Go to the documentation of this file.
1 // -----------------------------------------------------------------------------------------------------
2 // Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
3 // Copyright (c) 2016-2022, 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 
24 namespace seqan3::detail
25 {
26 
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} {};
61 
62 protected:
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},
92  defaultScreenWidth{80},
93  maximalScreenWidth{120},
94  minimalScreenWidth{40},
95  leftPadding{4},
96  centerPadding{2},
97  rightPadding{2},
98  leftColumnWidth{4},
99  rightColumnWidth{0}
100  {
101  // Guess terminal screen width and set into layout.
102  screenWidth = (terminal_width > 0) ? terminal_width : defaultScreenWidth;
103  screenWidth = std::max(screenWidth, minimalScreenWidth);
104  screenWidth = std::min(screenWidth, maximalScreenWidth);
105  screenWidth -= rightPadding;
106 
107  rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
108  rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
109  }
110 
112  console_layout_struct() : console_layout_struct{get_terminal_width()}
113  {}
114  };
115 
117  void print_header()
118  {
120 
121  std::cout << meta.app_name;
122  if (!empty(meta.short_description))
123  std::cout << " - " << meta.short_description;
124 
125  std::cout << "\n";
126  unsigned len =
127  text_width(meta.app_name) + (empty(meta.short_description) ? 0 : 3) + text_width(meta.short_description);
128  std::fill_n(out, len, '=');
129  std::cout << '\n';
130  }
131 
135  void print_section(std::string const & title)
136  {
138  std::cout << '\n' << to_text("\\fB");
139  std::transform(title.begin(),
140  title.end(),
141  out,
142  [](unsigned char c)
143  {
144  return std::toupper(c);
145  });
146  std::cout << to_text("\\fP") << '\n';
147  prev_was_paragraph = false;
148  }
149 
153  void print_subsection(std::string const & title)
154  {
156  std::cout << '\n';
157  std::fill_n(out, layout.leftPadding / 2, ' ');
158  std::cout << in_bold(title) << '\n';
159  prev_was_paragraph = false;
160  }
161 
167  void print_line(std::string const & text, bool const line_is_paragraph)
168  {
169  if (prev_was_paragraph)
170  std::cout << '\n';
171 
173  std::fill_n(out, layout.leftPadding, ' ');
174  print_text(text, layout.leftPadding);
175  prev_was_paragraph = line_is_paragraph;
176  }
177 
192  void print_list_item(std::string const & term, std::string const & desc)
193  {
194  if (prev_was_paragraph)
195  std::cout << '\n';
196 
198 
199  // Print term.
200  std::fill_n(out, layout.leftPadding, ' ');
201  std::cout << to_text(term);
202  unsigned pos = layout.leftPadding + term.size();
203  if (pos + layout.centerPadding > layout.rightColumnTab)
204  {
205  std::cout << '\n';
206  pos = 0;
207  }
208  std::fill_n(out, layout.rightColumnTab - pos, ' ');
209  print_text(desc, layout.rightColumnTab);
210 
211  prev_was_paragraph = false;
212  }
213 
215  void print_footer()
216  {
217  // no footer
218  }
219 
223  std::string to_text(std::string const & str)
224  {
225  std::string result;
226 
227  for (auto it = str.begin(); it != str.end(); ++it)
228  {
229  if (*it == '\\')
230  {
231  // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
232  ++it;
233  assert(it != str.end());
234  if (*it == '-')
235  {
236  result.push_back(*it);
237  }
238  else if (*it == 'f')
239  {
240  ++it;
241  assert(it != str.end());
242  if (*it == 'I')
243  {
244  if (is_terminal())
245  result.append("\033[4m");
246  }
247  else if (*it == 'B')
248  {
249  if (is_terminal())
250  result.append("\033[1m");
251  }
252  else if (*it == 'P')
253  {
254  if (is_terminal())
255  result.append("\033[0m");
256  }
257  else
258  {
259  result.append("\\f");
260  result.push_back(*it);
261  }
262  }
263  else
264  {
265  result.push_back('\\');
266  result.push_back(*it);
267  }
268  }
269  else
270  {
271  result.push_back(*it);
272  }
273  }
274 
275  return result;
276  }
277 
282  unsigned text_width(std::string const & text)
283  {
284  unsigned result = 0;
285 
286  for (unsigned i = 0; i < text.size(); ++i)
287  {
288  if (text[i] != '\\')
289  {
290  result += 1;
291  continue;
292  }
293 
294  if (i + 1 == text.size())
295  {
296  result += 1; // Will print "\\".
297  continue;
298  }
299 
300  if (text[i + 1] == '\\' || text[i + 1] == '-')
301  {
302  i += 1;
303  result += 1;
304  continue; // Will print '\\' or '-'.
305  }
306 
307  if (i + 2 == text.size())
308  {
309  i += 1;
310  result += 2; // Will print two chars.
311  continue;
312  }
313 
314  if (text[i + 1] == 'f')
315  {
316  if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
317  i += 2; // Skip f and {B, I, P}.
318  else
319  result += 1;
320  }
321  }
322 
323  return result;
324  }
325 
330  void print_text(std::string const & text, unsigned const tab)
331  {
332  unsigned pos = tab;
334 
335  // Tokenize the text.
336  std::istringstream iss(text.c_str());
338  std::ranges::copy(std::istream_iterator<std::string>(iss),
340  std::back_inserter(tokens));
341 
342  // Print the text.
343  assert(pos <= tab);
344  std::fill_n(out, tab - pos, ' '); // go to tab
345 
346  pos = tab;
347  typedef std::vector<std::string>::const_iterator TConstIter;
348  for (TConstIter it = tokens.begin(); it != tokens.end(); ++it)
349  {
350  if (it == tokens.begin())
351  {
352  std::cout << to_text(*it);
353  pos += text_width(*it);
354  if (pos > layout.screenWidth)
355  {
356  std::cout << '\n';
357  std::fill_n(out, tab, ' ');
358  pos = tab;
359  }
360  }
361  else
362  {
363  if (pos + 1 + text_width(*it) > layout.screenWidth)
364  {
365  // Would go over screen with next, print current word on next line.
366  std::cout << '\n';
367  fill_n(out, tab, ' ');
368  std::cout << to_text(*it);
369  pos = tab + text_width(*it);
370  }
371  else
372  {
373  std::cout << ' ';
374  std::cout << to_text(*it);
375  pos += text_width(*it) + 1;
376  }
377  }
378  }
379  if (!empty(tokens))
380  std::cout << '\n';
381  }
382 
387  std::string in_bold(std::string const & str)
388  {
389  return to_text("\\fB") + str + to_text("\\fP");
390  }
391 
393  bool prev_was_paragraph{false};
394 
396  friend struct ::seqan3::detail::test_accessor;
397 
399  console_layout_struct layout{};
400 };
401 
413 class format_short_help : public format_help
414 {
415 public:
419  void parse(argument_parser_meta_data const & parser_meta)
420  {
421  meta = parser_meta;
422 
423  print_header();
424 
425  if (!parser_meta.synopsis.empty())
426  print_synopsis();
427 
428  print_line("Try -h or --help for more information.\n", true);
429 
430  std::exit(EXIT_SUCCESS);
431  }
432 };
433 
445 class format_version : public format_help
446 {
447 public:
451  void parse(argument_parser_meta_data & parser_meta)
452  {
453  meta = parser_meta;
454 
455  print_header();
456  print_version();
457 
458  std::exit(EXIT_SUCCESS); // program should not continue from here
459  }
460 };
461 
473 class format_copyright : public format_help
474 {
475 public:
479  void parse(argument_parser_meta_data const & parser_meta)
480  {
481  meta = parser_meta;
482  debug_stream_type stream{std::cout};
483  std::string seqan_license{
484  R"(Copyright (c) 2006-2022, Knut Reinert & Freie Universität Berlin
485 Copyright (c) 2016-2022, Knut Reinert & MPI für molekulare Genetik
486 All rights reserved.
487 
488 Redistribution and use in source and binary forms, with or without
489 modification, are permitted provided that the following conditions are met:
490 
491  * Redistributions of source code must retain the above copyright
492  notice, this list of conditions and the following disclaimer.
493  * Redistributions in binary form must reproduce the above copyright
494  notice, this list of conditions and the following disclaimer in the
495  documentation and/or other materials provided with the distribution.
496  * Neither the name of Knut Reinert or the FU Berlin nor the names of
497  its contributors may be used to endorse or promote products derived
498  from this software without specific prior written permission.
499 
500 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
501 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
502 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
503 ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
504 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
505 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
506 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
507 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
508 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
509 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
510 DAMAGE.)"};
511 
512  stream << std::string(80, '=') << "\n"
513  << in_bold("Copyright information for " + meta.app_name + ":\n") << std::string(80, '-') << '\n';
514 
515  if (!empty(meta.long_copyright))
516  {
517  stream << to_text("\\fP") << meta.long_copyright << "\n";
518  }
519  else if (!empty(meta.short_copyright))
520  {
521  stream << in_bold(meta.app_name + " full copyright information not available. "
522  + "Displaying short copyright information instead:\n")
523  << meta.short_copyright << "\n";
524  }
525  else
526  {
527  stream << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
528  }
529 
530  stream << std::string(80, '=') << '\n'
531  << in_bold("This program contains SeqAn code licensed under the following terms:\n")
532  << std::string(80, '-') << '\n'
533  << seqan_license << '\n';
534 
535  std::exit(EXIT_SUCCESS);
536  }
537 };
538 
539 } // namespace seqan3::detail
T back_inserter(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:255
T max(T... args)
T min(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)