• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2020 Google LLC All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
4 
5 //! Shared functionality between argh_derive and the argh runtime.
6 //!
7 //! This library is intended only for internal use by these two crates.
8 
9 /// Information about a particular command used for output.
10 pub struct CommandInfo<'a> {
11     /// The name of the command.
12     pub name: &'a str,
13     /// A short description of the command's functionality.
14     pub description: &'a str,
15 }
16 
17 pub const INDENT: &str = "  ";
18 const DESCRIPTION_INDENT: usize = 20;
19 const WRAP_WIDTH: usize = 80;
20 
21 /// Write command names and descriptions to an output string.
write_description(out: &mut String, cmd: &CommandInfo<'_>)22 pub fn write_description(out: &mut String, cmd: &CommandInfo<'_>) {
23     let mut current_line = INDENT.to_string();
24     current_line.push_str(cmd.name);
25 
26     if cmd.description.is_empty() {
27         new_line(&mut current_line, out);
28         return;
29     }
30 
31     if !indent_description(&mut current_line) {
32         // Start the description on a new line if the flag names already
33         // add up to more than DESCRIPTION_INDENT.
34         new_line(&mut current_line, out);
35     }
36 
37     let mut words = cmd.description.split(' ').peekable();
38     while let Some(first_word) = words.next() {
39         indent_description(&mut current_line);
40         current_line.push_str(first_word);
41 
42         'inner: while let Some(&word) = words.peek() {
43             if (char_len(&current_line) + char_len(word) + 1) > WRAP_WIDTH {
44                 new_line(&mut current_line, out);
45                 break 'inner;
46             } else {
47                 // advance the iterator
48                 let _ = words.next();
49                 current_line.push(' ');
50                 current_line.push_str(word);
51             }
52         }
53     }
54     new_line(&mut current_line, out);
55 }
56 
57 // Indent the current line in to DESCRIPTION_INDENT chars.
58 // Returns a boolean indicating whether or not spacing was added.
indent_description(line: &mut String) -> bool59 fn indent_description(line: &mut String) -> bool {
60     let cur_len = char_len(line);
61     if cur_len < DESCRIPTION_INDENT {
62         let num_spaces = DESCRIPTION_INDENT - cur_len;
63         line.extend(std::iter::repeat(' ').take(num_spaces));
64         true
65     } else {
66         false
67     }
68 }
69 
char_len(s: &str) -> usize70 fn char_len(s: &str) -> usize {
71     s.chars().count()
72 }
73 
74 // Append a newline and the current line to the output,
75 // clearing the current line.
new_line(current_line: &mut String, out: &mut String)76 fn new_line(current_line: &mut String, out: &mut String) {
77     out.push('\n');
78     out.push_str(current_line);
79     current_line.truncate(0);
80 }
81