brush_shell/
args.rs

1use clap::{builder::styling, Parser};
2use std::io::IsTerminal;
3
4use crate::{events, productinfo};
5
6const SHORT_DESCRIPTION: &str = "Bo[u]rn[e] RUsty SHell";
7
8const LONG_DESCRIPTION: &str = r"
9brush is a Rust-implemented, POSIX-style shell that aims to be compatible with bash.
10
11brush is a work in progress. If you encounter any issues or discrepancies in behavior from bash, please report them at https://github.com/reubeno/brush.
12";
13
14const VERSION: &str = const_format::concatcp!(
15    productinfo::PRODUCT_VERSION,
16    " (",
17    productinfo::PRODUCT_GIT_VERSION,
18    ")"
19);
20
21#[derive(Clone, clap::ValueEnum)]
22pub enum InputBackend {
23    Reedline,
24    Basic,
25    Minimal,
26}
27
28/// Parsed command-line arguments for the brush shell.
29#[derive(Parser)]
30#[clap(name = productinfo::PRODUCT_NAME,
31       version = VERSION,
32       about = SHORT_DESCRIPTION,
33       long_about = LONG_DESCRIPTION,
34       author,
35       disable_help_flag = true,
36       disable_version_flag = true,
37       styles = brush_help_styles())]
38#[allow(clippy::module_name_repetitions)]
39pub struct CommandLineArgs {
40    /// Display usage information.
41    #[clap(long = "help", action = clap::ArgAction::HelpLong)]
42    pub help: Option<bool>,
43
44    /// Display shell version.
45    #[clap(long = "version", action = clap::ArgAction::Version)]
46    pub version: Option<bool>,
47
48    /// Enable noclobber shell option.
49    #[arg(short = 'C')]
50    pub disallow_overwriting_regular_files_via_output_redirection: bool,
51
52    /// Execute the provided command and then exit.
53    #[arg(short = 'c', value_name = "COMMAND")]
54    pub command: Option<String>,
55
56    /// Run in interactive mode.
57    #[clap(short = 'i')]
58    pub interactive: bool,
59
60    /// Make shell act as if it had been invoked as a login shell.
61    #[clap(short = 'l', long = "login")]
62    pub login: bool,
63
64    /// Do not execute commands.
65    #[clap(short = 'n')]
66    pub do_not_execute_commands: bool,
67
68    /// Don't use readline for input.
69    #[clap(long = "noediting")]
70    pub no_editing: bool,
71
72    /// Don't process any profile/login files (`/etc/profile`, `~/.bash_profile`, `~/.bash_login`,
73    /// `~/.profile`).
74    #[clap(long = "noprofile")]
75    pub no_profile: bool,
76
77    /// Don't process "rc" files if the shell is interactive (e.g., `~/.bashrc`, `~/.brushrc`).
78    #[clap(long = "norc")]
79    pub no_rc: bool,
80
81    /// Don't inherit environment variables from the calling process.
82    #[clap(long = "noenv")]
83    pub do_not_inherit_env: bool,
84
85    /// Enable shell option.
86    #[clap(short = 'O', value_name = "OPTION")]
87    pub enabled_shopt_options: Vec<String>,
88
89    /// Disable shell option.
90    #[clap(long = "+O", hide = true)]
91    pub disabled_shopt_options: Vec<String>,
92
93    /// Disable non-POSIX extensions.
94    #[clap(long = "posix")]
95    pub posix: bool,
96
97    /// Read commands from standard input.
98    #[clap(short = 's')]
99    pub read_commands_from_stdin: bool,
100
101    /// Run in sh compatibility mode.
102    #[clap(long = "sh")]
103    pub sh_mode: bool,
104
105    /// Run only one command.
106    #[clap(short = 't')]
107    pub exit_after_one_command: bool,
108
109    /// Print input when it's processed.
110    #[clap(short = 'v', long = "verbose")]
111    pub verbose: bool,
112
113    /// Print commands as they execute.
114    #[clap(short = 'x')]
115    pub print_commands_and_arguments: bool,
116
117    /// Disable bracketed paste.
118    #[clap(long = "disable-bracketed-paste")]
119    pub disable_bracketed_paste: bool,
120
121    /// Disable colorized output.
122    #[clap(long = "disable-color")]
123    pub disable_color: bool,
124
125    /// Enable syntax highlighting (experimental).
126    #[clap(long = "enable-highlighting")]
127    pub enable_highlighting: bool,
128
129    /// Input backend.
130    #[clap(long = "input-backend")]
131    pub input_backend: Option<InputBackend>,
132
133    /// Enable debug logging for classes of tracing events.
134    #[clap(long = "log-enable", value_name = "EVENT")]
135    pub enabled_log_events: Vec<events::TraceEvent>,
136
137    /// Path to script to execute.
138    // allow any string as command_name similar to sh
139    #[clap(allow_hyphen_values = true)]
140    pub script_path: Option<String>,
141
142    /// Arguments for script.
143    // `allow_hyphen_values`: do not strip `-` from flags
144    // `num_args=1..`: consume everything
145    #[clap(allow_hyphen_values = true, num_args=1..)]
146    pub script_args: Vec<String>,
147}
148
149impl CommandLineArgs {
150    pub fn is_interactive(&self) -> bool {
151        if self.interactive {
152            return true;
153        }
154
155        if self.command.is_some() || self.script_path.is_some() {
156            return false;
157        }
158
159        if !std::io::stdin().is_terminal() || !std::io::stderr().is_terminal() {
160            return false;
161        }
162
163        true
164    }
165}
166
167/// Returns clap styling to be used for command-line help.
168#[doc(hidden)]
169fn brush_help_styles() -> clap::builder::Styles {
170    styling::Styles::styled()
171        .header(
172            styling::AnsiColor::Yellow.on_default()
173                | styling::Effects::BOLD
174                | styling::Effects::UNDERLINE,
175        )
176        .usage(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
177        .literal(styling::AnsiColor::Magenta.on_default() | styling::Effects::BOLD)
178        .placeholder(styling::AnsiColor::Cyan.on_default())
179}