argh_shared/lib.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
// 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,
}
/// Information about the command line arguments for a given command.
#[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize)]
pub struct CommandInfoWithArgs<'a> {
/// The name of the command.
pub name: &'a str,
/// A short description of the command's functionality.
pub description: &'a str,
/// Examples of usage
pub examples: &'a [&'a str],
/// Flags
pub flags: &'a [FlagInfo<'a>],
/// Notes about usage
pub notes: &'a [&'a str],
/// The subcommands.
pub commands: Vec<SubCommandInfo<'a>>,
/// Positional args
pub positionals: &'a [PositionalInfo<'a>],
/// Error code information
pub error_codes: &'a [ErrorCodeInfo<'a>],
}
/// Information about a documented error code.
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
pub struct ErrorCodeInfo<'a> {
/// The code value.
pub code: i32,
/// Short description about what this code indicates.
pub description: &'a str,
}
/// Information about positional arguments
#[derive(Debug, PartialEq, Eq, serde::Serialize)]
pub struct PositionalInfo<'a> {
/// Name of the argument.
pub name: &'a str,
/// Description of the argument.
pub description: &'a str,
/// Optionality of the argument.
pub optionality: Optionality,
/// Visibility in the help for this argument.
/// `false` indicates this argument will not appear
/// in the help message.
pub hidden: bool,
}
/// Information about a subcommand.
/// Dynamic subcommands do not implement
/// get_args_info(), so the command field
/// only contains the name and description.
#[derive(Debug, Default, PartialEq, Eq, Clone, serde::Serialize)]
pub struct SubCommandInfo<'a> {
/// The subcommand name.
pub name: &'a str,
/// The information about the subcommand.
pub command: CommandInfoWithArgs<'a>,
}
/// Information about a flag or option.
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
pub struct FlagInfo<'a> {
/// The kind of flag.
pub kind: FlagInfoKind<'a>,
/// The optionality of the flag.
pub optionality: Optionality,
/// The long string of the flag.
pub long: &'a str,
/// The single character short indicator
/// for trhis flag.
pub short: Option<char>,
/// The description of the flag.
pub description: &'a str,
/// Visibility in the help for this argument.
/// `false` indicates this argument will not appear
/// in the help message.
pub hidden: bool,
}
/// The kind of flags.
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
pub enum FlagInfoKind<'a> {
/// switch represents a boolean flag,
#[default]
Switch,
/// option is a flag that also has an associated
/// value. This value is named `arg_name`.
Option { arg_name: &'a str },
}
/// The optionality defines the requirments related
/// to the presence of the argument on the command line.
#[derive(Debug, Default, PartialEq, Eq, serde::Serialize)]
pub enum Optionality {
/// Required indicates the argument is required
/// exactly once.
#[default]
Required,
/// Optional indicates the argument may or may not
/// be present.
Optional,
/// Repeating indicates the argument may appear zero
/// or more times.
Repeating,
/// Greedy is used for positional arguments which
/// capture the all command line input upto the next flag or
/// the end of the input.
Greedy,
}
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);
}