1use clap::{Parser, builder::styling};
4use std::{io::IsTerminal, path::PathBuf};
5
6use crate::{events, productinfo};
7
8const SHORT_DESCRIPTION: &str = "Bo[u]rn[e] RUsty SHell";
9
10const LONG_DESCRIPTION: &str = r"
11brush is a Rust-implemented, POSIX-style shell that aims to be compatible with bash.
12
13brush 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.
14";
15
16const VERSION: &str = const_format::concatcp!(
17 productinfo::PRODUCT_VERSION,
18 " (",
19 productinfo::PRODUCT_GIT_VERSION,
20 ")"
21);
22
23#[derive(Clone, Copy, clap::ValueEnum)]
25pub enum InputBackend {
26 Reedline,
28 Basic,
30 Minimal,
32}
33
34#[derive(Parser)]
36#[clap(name = productinfo::PRODUCT_NAME,
37 version = VERSION,
38 about = SHORT_DESCRIPTION,
39 long_about = LONG_DESCRIPTION,
40 author,
41 disable_help_flag = true,
42 disable_version_flag = true,
43 styles = brush_help_styles())]
44#[allow(clippy::module_name_repetitions)]
45pub struct CommandLineArgs {
46 #[clap(long = "help", action = clap::ArgAction::HelpLong)]
48 pub help: Option<bool>,
49
50 #[clap(long = "version", action = clap::ArgAction::Version)]
52 pub version: Option<bool>,
53
54 #[arg(short = 'C')]
56 pub disallow_overwriting_regular_files_via_output_redirection: bool,
57
58 #[arg(short = 'c', value_name = "COMMAND")]
60 pub command: Option<String>,
61
62 #[clap(short = 'i')]
64 pub interactive: bool,
65
66 #[clap(short = 'l', long = "login")]
68 pub login: bool,
69
70 #[clap(short = 'n')]
72 pub do_not_execute_commands: bool,
73
74 #[clap(long = "noediting")]
76 pub no_editing: bool,
77
78 #[clap(long = "noprofile")]
81 pub no_profile: bool,
82
83 #[clap(long = "norc")]
85 pub no_rc: bool,
86
87 #[clap(long = "noenv")]
89 pub do_not_inherit_env: bool,
90
91 #[clap(short = 'o', value_name = "OPTION")]
93 pub enabled_options: Vec<String>,
94
95 #[clap(long = "+o", hide = true)]
97 pub disabled_options: Vec<String>,
98
99 #[clap(short = 'O', value_name = "SHOPT_OPTION")]
101 pub enabled_shopt_options: Vec<String>,
102
103 #[clap(long = "+O", hide = true)]
105 pub disabled_shopt_options: Vec<String>,
106
107 #[clap(long = "posix")]
109 pub posix: bool,
110
111 #[clap(long = "rcfile", alias = "init-file", value_name = "FILE")]
113 pub rc_file: Option<PathBuf>,
114
115 #[clap(short = 's')]
117 pub read_commands_from_stdin: bool,
118
119 #[clap(long = "sh")]
121 pub sh_mode: bool,
122
123 #[clap(short = 't')]
125 pub exit_after_one_command: bool,
126
127 #[clap(short = 'v', long = "verbose")]
129 pub verbose: bool,
130
131 #[clap(short = 'x')]
133 pub print_commands_and_arguments: bool,
134
135 #[clap(long = "disable-bracketed-paste")]
137 pub disable_bracketed_paste: bool,
138
139 #[clap(long = "disable-color")]
141 pub disable_color: bool,
142
143 #[clap(long = "enable-highlighting")]
145 pub enable_highlighting: bool,
146
147 #[clap(long = "input-backend")]
149 pub input_backend: Option<InputBackend>,
150
151 #[clap(long = "debug", alias = "log-enable", value_name = "EVENT")]
153 pub enabled_debug_events: Vec<events::TraceEvent>,
154
155 #[clap(
157 long = "disable-event",
158 alias = "log-disable",
159 value_name = "EVENT",
160 hide_possible_values = true
161 )]
162 pub disabled_events: Vec<events::TraceEvent>,
163
164 #[clap(allow_hyphen_values = true)]
167 pub script_path: Option<String>,
168
169 #[clap(allow_hyphen_values = true, num_args=1..)]
173 pub script_args: Vec<String>,
174}
175
176impl CommandLineArgs {
177 pub fn is_interactive(&self) -> bool {
179 if self.interactive {
182 return true;
183 }
184
185 if self.command.is_some() || self.script_path.is_some() {
187 return false;
188 }
189
190 if !std::io::stdin().is_terminal() || !std::io::stderr().is_terminal() {
192 return false;
193 }
194
195 true
197 }
198}
199
200#[doc(hidden)]
202fn brush_help_styles() -> clap::builder::Styles {
203 styling::Styles::styled()
204 .header(
205 styling::AnsiColor::Yellow.on_default()
206 | styling::Effects::BOLD
207 | styling::Effects::UNDERLINE,
208 )
209 .usage(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
210 .literal(styling::AnsiColor::Magenta.on_default() | styling::Effects::BOLD)
211 .placeholder(styling::AnsiColor::Cyan.on_default())
212}