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 
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 public:
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 
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}, 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;
332  typedef std::vector<std::string>::const_iterator TConstIter;
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 
398 class format_short_help : public format_help
399 {
400 public:
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 
430 class format_version : public format_help
431 {
432 public:
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 
458 class format_copyright : public format_help
459 {
460 public:
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{
469 R"(Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
470 Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
471 All rights reserved.
472 
473 Redistribution and use in source and binary forms, with or without
474 modification, 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 
485 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
486 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
487 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
488 ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
489 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
490 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
491 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
492 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
493 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
494 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
495 DAMAGE.)"};
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)