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(¤t_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