| // Copyright (c) 2020 Google LLC All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //! Shared functionality between argh_derive and the argh runtime. |
| //! |
| //! This library is intended only for internal use by these two crates. |
| |
| /// Information about a particular command used for output. |
| pub struct CommandInfo<'a> { |
| /// The name of the command. |
| pub name: &'a str, |
| /// A short description of the command's functionality. |
| pub description: &'a str, |
| } |
| |
| pub const INDENT: &str = " "; |
| const DESCRIPTION_INDENT: usize = 20; |
| const WRAP_WIDTH: usize = 80; |
| |
| /// Write command names and descriptions to an output string. |
| pub fn write_description(out: &mut String, cmd: &CommandInfo<'_>) { |
| let mut current_line = INDENT.to_string(); |
| current_line.push_str(cmd.name); |
| |
| if cmd.description.is_empty() { |
| new_line(&mut current_line, out); |
| return; |
| } |
| |
| if !indent_description(&mut current_line) { |
| // Start the description on a new line if the flag names already |
| // add up to more than DESCRIPTION_INDENT. |
| new_line(&mut current_line, out); |
| } |
| |
| let mut words = cmd.description.split(' ').peekable(); |
| while let Some(first_word) = words.next() { |
| indent_description(&mut current_line); |
| current_line.push_str(first_word); |
| |
| 'inner: while let Some(&word) = words.peek() { |
| if (char_len(¤t_line) + char_len(word) + 1) > WRAP_WIDTH { |
| new_line(&mut current_line, out); |
| break 'inner; |
| } else { |
| // advance the iterator |
| let _ = words.next(); |
| current_line.push(' '); |
| current_line.push_str(word); |
| } |
| } |
| } |
| new_line(&mut current_line, out); |
| } |
| |
| // Indent the current line in to DESCRIPTION_INDENT chars. |
| // Returns a boolean indicating whether or not spacing was added. |
| fn indent_description(line: &mut String) -> bool { |
| let cur_len = char_len(line); |
| if cur_len < DESCRIPTION_INDENT { |
| let num_spaces = DESCRIPTION_INDENT - cur_len; |
| line.extend(std::iter::repeat(' ').take(num_spaces)); |
| true |
| } else { |
| false |
| } |
| } |
| |
| fn char_len(s: &str) -> usize { |
| s.chars().count() |
| } |
| |
| // Append a newline and the current line to the output, |
| // clearing the current line. |
| fn new_line(current_line: &mut String, out: &mut String) { |
| out.push('\n'); |
| out.push_str(current_line); |
| current_line.truncate(0); |
| } |