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