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_shopt_options: Vec<String>,
94
95 #[clap(long = "+O", hide = true)]
97 pub disabled_shopt_options: Vec<String>,
98
99 #[clap(long = "posix")]
101 pub posix: bool,
102
103 #[clap(long = "rcfile", alias = "init-file", value_name = "FILE")]
105 pub rc_file: Option<PathBuf>,
106
107 #[clap(short = 's')]
109 pub read_commands_from_stdin: bool,
110
111 #[clap(long = "sh")]
113 pub sh_mode: bool,
114
115 #[clap(short = 't')]
117 pub exit_after_one_command: bool,
118
119 #[clap(short = 'v', long = "verbose")]
121 pub verbose: bool,
122
123 #[clap(short = 'x')]
125 pub print_commands_and_arguments: bool,
126
127 #[clap(long = "disable-bracketed-paste")]
129 pub disable_bracketed_paste: bool,
130
131 #[clap(long = "disable-color")]
133 pub disable_color: bool,
134
135 #[clap(long = "enable-highlighting")]
137 pub enable_highlighting: bool,
138
139 #[clap(long = "input-backend")]
141 pub input_backend: Option<InputBackend>,
142
143 #[clap(long = "debug", alias = "log-enable", value_name = "EVENT")]
145 pub enabled_debug_events: Vec<events::TraceEvent>,
146
147 #[clap(long = "disable-event", alias = "log-disable", value_name = "EVENT")]
149 pub disabled_events: Vec<events::TraceEvent>,
150
151 #[clap(allow_hyphen_values = true)]
154 pub script_path: Option<String>,
155
156 #[clap(allow_hyphen_values = true, num_args=1..)]
160 pub script_args: Vec<String>,
161}
162
163impl CommandLineArgs {
164 pub fn is_interactive(&self) -> bool {
166 if self.interactive {
169 return true;
170 }
171
172 if self.command.is_some() || self.script_path.is_some() {
174 return false;
175 }
176
177 if !std::io::stdin().is_terminal() || !std::io::stderr().is_terminal() {
179 return false;
180 }
181
182 true
184 }
185}
186
187#[doc(hidden)]
189fn brush_help_styles() -> clap::builder::Styles {
190 styling::Styles::styled()
191 .header(
192 styling::AnsiColor::Yellow.on_default()
193 | styling::Effects::BOLD
194 | styling::Effects::UNDERLINE,
195 )
196 .usage(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
197 .literal(styling::AnsiColor::Magenta.on_default() | styling::Effects::BOLD)
198 .placeholder(styling::AnsiColor::Cyan.on_default())
199}