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#[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 #[clap(long = "help", action = clap::ArgAction::HelpLong)]
42 pub help: Option<bool>,
43
44 #[clap(long = "version", action = clap::ArgAction::Version)]
46 pub version: Option<bool>,
47
48 #[arg(short = 'C')]
50 pub disallow_overwriting_regular_files_via_output_redirection: bool,
51
52 #[arg(short = 'c', value_name = "COMMAND")]
54 pub command: Option<String>,
55
56 #[clap(short = 'i')]
58 pub interactive: bool,
59
60 #[clap(short = 'l', long = "login")]
62 pub login: bool,
63
64 #[clap(short = 'n')]
66 pub do_not_execute_commands: bool,
67
68 #[clap(long = "noediting")]
70 pub no_editing: bool,
71
72 #[clap(long = "noprofile")]
75 pub no_profile: bool,
76
77 #[clap(long = "norc")]
79 pub no_rc: bool,
80
81 #[clap(long = "noenv")]
83 pub do_not_inherit_env: bool,
84
85 #[clap(short = 'O', value_name = "OPTION")]
87 pub enabled_shopt_options: Vec<String>,
88
89 #[clap(long = "+O", hide = true)]
91 pub disabled_shopt_options: Vec<String>,
92
93 #[clap(long = "posix")]
95 pub posix: bool,
96
97 #[clap(short = 's')]
99 pub read_commands_from_stdin: bool,
100
101 #[clap(long = "sh")]
103 pub sh_mode: bool,
104
105 #[clap(short = 't')]
107 pub exit_after_one_command: bool,
108
109 #[clap(short = 'v', long = "verbose")]
111 pub verbose: bool,
112
113 #[clap(short = 'x')]
115 pub print_commands_and_arguments: bool,
116
117 #[clap(long = "disable-bracketed-paste")]
119 pub disable_bracketed_paste: bool,
120
121 #[clap(long = "disable-color")]
123 pub disable_color: bool,
124
125 #[clap(long = "enable-highlighting")]
127 pub enable_highlighting: bool,
128
129 #[clap(long = "input-backend")]
131 pub input_backend: Option<InputBackend>,
132
133 #[clap(long = "log-enable", value_name = "EVENT")]
135 pub enabled_log_events: Vec<events::TraceEvent>,
136
137 #[clap(allow_hyphen_values = true)]
140 pub script_path: Option<String>,
141
142 #[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#[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}