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())]
44pub struct CommandLineArgs {
45 #[clap(long = "help", action = clap::ArgAction::HelpLong)]
47 pub help: Option<bool>,
48
49 #[clap(long = "version", action = clap::ArgAction::Version)]
51 pub version: Option<bool>,
52
53 #[arg(short = 'C')]
55 pub disallow_overwriting_regular_files_via_output_redirection: bool,
56
57 #[arg(short = 'c', value_name = "COMMAND")]
59 pub command: Option<String>,
60
61 #[clap(short = 'i')]
63 pub interactive: bool,
64
65 #[clap(short = 'l', long = "login")]
67 pub login: bool,
68
69 #[clap(short = 'n')]
71 pub do_not_execute_commands: bool,
72
73 #[clap(long = "noediting")]
75 pub no_editing: bool,
76
77 #[clap(long = "noprofile")]
80 pub no_profile: bool,
81
82 #[clap(long = "norc")]
84 pub no_rc: bool,
85
86 #[clap(long = "noenv")]
88 pub do_not_inherit_env: bool,
89
90 #[clap(short = 'o', value_name = "OPTION")]
92 pub enabled_options: Vec<String>,
93
94 #[clap(long = "+o", hide = true)]
96 pub disabled_options: Vec<String>,
97
98 #[clap(short = 'O', value_name = "SHOPT_OPTION")]
100 pub enabled_shopt_options: Vec<String>,
101
102 #[clap(long = "+O", hide = true)]
104 pub disabled_shopt_options: Vec<String>,
105
106 #[clap(long = "posix")]
108 pub posix: bool,
109
110 #[clap(long = "rcfile", alias = "init-file", value_name = "FILE")]
112 pub rc_file: Option<PathBuf>,
113
114 #[clap(short = 's')]
116 pub read_commands_from_stdin: bool,
117
118 #[clap(long = "sh")]
120 pub sh_mode: bool,
121
122 #[clap(short = 't')]
124 pub exit_after_one_command: bool,
125
126 #[clap(short = 'v', long = "verbose")]
128 pub verbose: bool,
129
130 #[clap(short = 'x')]
132 pub print_commands_and_arguments: bool,
133
134 #[clap(long = "disable-bracketed-paste")]
136 pub disable_bracketed_paste: bool,
137
138 #[clap(long = "disable-color")]
140 pub disable_color: bool,
141
142 #[clap(long = "enable-highlighting")]
144 pub enable_highlighting: bool,
145
146 #[clap(long = "input-backend")]
148 pub input_backend: Option<InputBackend>,
149
150 #[clap(long = "debug", alias = "log-enable", value_name = "EVENT")]
152 pub enabled_debug_events: Vec<events::TraceEvent>,
153
154 #[clap(
156 long = "disable-event",
157 alias = "log-disable",
158 value_name = "EVENT",
159 hide_possible_values = true
160 )]
161 pub disabled_events: Vec<events::TraceEvent>,
162
163 #[clap(trailing_var_arg = true, allow_hyphen_values = true)]
165 pub script_args: Vec<String>,
166}
167
168impl CommandLineArgs {
169 pub fn is_interactive(&self) -> bool {
171 if self.interactive {
174 return true;
175 }
176
177 if self.command.is_some() || !self.script_args.is_empty() {
179 return false;
180 }
181
182 if !std::io::stdin().is_terminal() || !std::io::stderr().is_terminal() {
184 return false;
185 }
186
187 true
189 }
190}
191
192#[doc(hidden)]
194fn brush_help_styles() -> clap::builder::Styles {
195 styling::Styles::styled()
196 .header(
197 styling::AnsiColor::Yellow.on_default()
198 | styling::Effects::BOLD
199 | styling::Effects::UNDERLINE,
200 )
201 .usage(styling::AnsiColor::Green.on_default() | styling::Effects::BOLD)
202 .literal(styling::AnsiColor::Magenta.on_default() | styling::Effects::BOLD)
203 .placeholder(styling::AnsiColor::Cyan.on_default())
204}