SeqAn3  3.1.0-rc.1
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 
37 class format_help : public format_help_base<format_help>
38 {
40  using base_type = format_help_base<format_help>;
41 
43  friend base_type;
44 public:
48  format_help() = default;
49  format_help(format_help const & pf) = default;
50  format_help & operator=(format_help const &) = default;
51  format_help(format_help &&) = default;
52  format_help & operator=(format_help &&) = default;
53  ~format_help() = default;
54 
56  format_help(std::vector<std::string> const & names, bool const advanced = false) : base_type{names, advanced}
57  {};
59 
60 protected:
63  struct console_layout_struct
64  {
66  uint32_t screenWidth;
68  uint32_t defaultScreenWidth;
70  uint32_t maximalScreenWidth;
72  uint32_t minimalScreenWidth;
74  uint32_t leftPadding;
76  uint32_t centerPadding;
78  uint32_t rightPadding;
80  uint32_t leftColumnWidth;
82  uint32_t rightColumnWidth;
84  uint32_t rightColumnTab;
85 
88  console_layout_struct(uint32_t const terminal_width) :
89  screenWidth{0}, defaultScreenWidth{80}, maximalScreenWidth{120}, minimalScreenWidth{40},
90  leftPadding{4}, centerPadding{2}, rightPadding{2}, leftColumnWidth{4}, rightColumnWidth{0}
91  {
92  // Guess terminal screen width and set into layout.
93  screenWidth = (terminal_width > 0) ? terminal_width : defaultScreenWidth;
94  screenWidth = std::max(screenWidth, minimalScreenWidth);
95  screenWidth = std::min(screenWidth, maximalScreenWidth);
96  screenWidth -= rightPadding;
97 
98  rightColumnWidth = screenWidth - leftPadding - leftColumnWidth - centerPadding - rightPadding;
99  rightColumnTab = leftPadding + leftColumnWidth + centerPadding;
100  }
101 
103  console_layout_struct() : console_layout_struct{get_terminal_width()} {}
104  };
105 
107  void print_header()
108  {
109  std::ostream_iterator<char> out(std::cout);
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  {
127  std::ostream_iterator<char> out(std::cout);
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  {
139  std::ostream_iterator<char> out(std::cout);
140  std::cout << '\n';
141  std::fill_n(out, layout.leftPadding / 2, ' ');
142  std::cout << in_bold(title) << '\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 
156  std::ostream_iterator<char> out(std::cout);
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 
181  std::ostream_iterator<char> out(std::cout);
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_footer()
200  {
201  // no footer
202  }
203 
207  std::string to_text(std::string const & str)
208  {
209  std::string result;
210 
211  for (auto it = str.begin(); it != str.end(); ++it)
212  {
213  if (*it == '\\')
214  {
215  // Handle escape sequence, we interpret only "\-", "\fI", and "\fB".
216  ++it;
217  assert(it != str.end());
218  if (*it == '-')
219  {
220  result.push_back(*it);
221  }
222  else if (*it == 'f')
223  {
224  ++it;
225  assert(it != str.end());
226  if (*it == 'I')
227  {
228  if (is_terminal())
229  result.append("\033[4m");
230  }
231  else if (*it == 'B')
232  {
233  if (is_terminal())
234  result.append("\033[1m");
235  }
236  else if (*it == 'P')
237  {
238  if (is_terminal())
239  result.append("\033[0m");
240  }
241  else
242  {
243  result.append("\\f");
244  result.push_back(*it);
245  }
246  }
247  else
248  {
249  result.push_back('\\');
250  result.push_back(*it);
251  }
252  }
253  else
254  {
255  result.push_back(*it);
256  }
257  }
258 
259  return result;
260  }
261 
266  unsigned text_width(std::string const & text)
267  {
268  unsigned result = 0;
269 
270  for (unsigned i = 0; i < text.size(); ++i)
271  {
272  if (text[i] != '\\')
273  {
274  result += 1;
275  continue;
276  }
277 
278  if (i + 1 == text.size())
279  {
280  result += 1; // Will print "\\".
281  continue;
282  }
283 
284  if (text[i + 1] == '\\' || text[i + 1] == '-')
285  {
286  i += 1;
287  result += 1;
288  continue; // Will print '\\' or '-'.
289  }
290 
291  if (i + 2 == text.size())
292  {
293  i += 1;
294  result += 2; // Will print two chars.
295  continue;
296  }
297 
298  if (text[i + 1] == 'f')
299  {
300  if (text[i + 2] == 'B' || text[i + 2] == 'I' || text[i + 2] == 'P')
301  i += 2; // Skip f and {B, I, P}.
302  else
303  result += 1;
304  }
305  }
306 
307  return result;
308  }
309 
314  void print_text(std::string const & text, unsigned const tab)
315  {
316  unsigned pos = tab;
317  std::ostream_iterator<char> out(std::cout);
318 
319  // Tokenize the text.
320  std::istringstream iss(text.c_str());
321  std::vector<std::string> tokens;
322  std::ranges::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(),
323  std::cpp20::back_inserter(tokens));
324 
325  // Print the text.
326  assert(pos <= tab);
327  std::fill_n(out, tab - pos, ' '); // go to tab
328 
329  pos = tab;
330  typedef std::vector<std::string>::const_iterator TConstIter;
331  for (TConstIter it = tokens.begin(); it != tokens.end(); ++it)
332  {
333  if (it == tokens.begin())
334  {
335  std::cout << to_text(*it);
336  pos += text_width(*it);
337  if (pos > layout.screenWidth)
338  {
339  std::cout << '\n';
340  std::fill_n(out, tab, ' ');
341  pos = tab;
342  }
343  }
344  else
345  {
346  if (pos + 1 + text_width(*it) > layout.screenWidth)
347  {
348  // Would go over screen with next, print current word on next line.
349  std::cout << '\n';
350  fill_n(out, tab, ' ');
351  std::cout << to_text(*it);
352  pos = tab + text_width(*it);
353  }
354  else
355  {
356  std::cout << ' ';
357  std::cout << to_text(*it);
358  pos += text_width(*it) + 1;
359  }
360  }
361  }
362  if (!empty(tokens))
363  std::cout << '\n';
364  }
365 
370  std::string in_bold(std::string const & str)
371  {
372  return to_text("\\fB") + str + to_text("\\fP");
373  }
374 
376  bool prev_was_paragraph{false};
377 
379  friend struct ::seqan3::detail::test_accessor;
380 
382  console_layout_struct layout{};
383 };
384 
394 class format_short_help : public format_help
395 {
396 public:
400  void parse(argument_parser_meta_data const & parser_meta)
401  {
402  meta = parser_meta;
403 
404  print_header();
405 
406  if (!parser_meta.synopsis.empty())
407  print_synopsis();
408 
409  print_line("Try -h or --help for more information.\n", true);
410 
411  std::exit(EXIT_SUCCESS);
412  }
413 };
414 
424 class format_version : public format_help
425 {
426 public:
430  void parse(argument_parser_meta_data & parser_meta)
431  {
432  meta = parser_meta;
433 
434  print_header();
435  print_version();
436 
437  std::exit(EXIT_SUCCESS); // program should not continue from here
438  }
439 };
440 
450 class format_copyright : public format_help
451 {
452 public:
456  void parse(argument_parser_meta_data const & parser_meta)
457  {
458  meta = parser_meta;
459  debug_stream_type stream{std::cout};
460  std::string seqan_license{
461 R"(Copyright (c) 2006-2021, Knut Reinert & Freie Universität Berlin
462 Copyright (c) 2016-2021, Knut Reinert & MPI für molekulare Genetik
463 All rights reserved.
464 
465 Redistribution and use in source and binary forms, with or without
466 modification, are permitted provided that the following conditions are met:
467 
468  * Redistributions of source code must retain the above copyright
469  notice, this list of conditions and the following disclaimer.
470  * Redistributions in binary form must reproduce the above copyright
471  notice, this list of conditions and the following disclaimer in the
472  documentation and/or other materials provided with the distribution.
473  * Neither the name of Knut Reinert or the FU Berlin nor the names of
474  its contributors may be used to endorse or promote products derived
475  from this software without specific prior written permission.
476 
477 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
478 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
479 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
480 ARE DISCLAIMED. IN NO EVENT SHALL KNUT REINERT OR THE FU BERLIN BE LIABLE
481 FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
482 DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
483 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
484 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
485 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
486 OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
487 DAMAGE.)"};
488 
489  stream << std::string(80, '=') << "\n"
490  << in_bold("Copyright information for " + meta.app_name + ":\n")
491  << std::string(80, '-') << '\n';
492 
493  if (!empty(meta.long_copyright))
494  {
495  stream << to_text("\\fP") << meta.long_copyright << "\n";
496  }
497  else if (!empty(meta.short_copyright))
498  {
499  stream << in_bold(meta.app_name + " full copyright information not available. " +
500  "Displaying short copyright information instead:\n" )
501  << meta.short_copyright << "\n";
502  }
503  else
504  {
505  stream << to_text("\\fP") << meta.app_name << " copyright information not available.\n";
506  }
507 
508  stream << std::string(80, '=') << '\n'
509  << in_bold("This program contains SeqAn code licensed under the following terms:\n")
510  << std::string(80, '-') << '\n' << seqan_license << '\n';
511 
512  std::exit(EXIT_SUCCESS);
513  }
514 };
515 
516 } // namespace seqan3::detail
Provides the format_base struct containing all helper functions that are needed in all formats.
@ advanced
Definition: auxiliary.hpp:245
decltype(detail::transform< trait_t >(list_t{})) transform
Apply a transformation trait to every type in the list and return a seqan3::type_list of the results.
Definition: traits.hpp:471
Checks if program is run interactively and retrieves dimensions of terminal (Transferred from seqan2)...
Forward declares seqan3::detail::test_accessor.