Skip to main content

brush_core/shell/
builder.rs

1//! Module defining the builder for creating shell instances.
2
3use std::{collections::HashMap, path::PathBuf};
4
5pub use shell_builder::State as ShellBuilderState;
6
7use super::Shell;
8use crate::{
9    ProfileLoadBehavior, RcLoadBehavior, ShellFd, ShellVariable, builtins, callstack, completion,
10    env, error, extensions, functions, jobs, openfiles, options, pathcache,
11    shell::KeyBindingsHelper, traps,
12};
13
14impl<SE: extensions::ShellExtensions, S: shell_builder::IsComplete> ShellBuilder<SE, S> {
15    /// Returns a new shell instance created with the options provided. Runs any
16    /// configuration loading as well.
17    pub async fn build(self) -> Result<Shell<SE>, error::Error> {
18        let mut options = self.build_settings();
19
20        let profile = std::mem::take(&mut options.profile);
21        let rc = std::mem::take(&mut options.rc);
22
23        // Construct the shell.
24        let mut shell = Shell::new(options)?;
25
26        // Load profiles/configuration, unless skipped.
27        if !profile.skip() || !rc.skip() {
28            shell.load_config(&profile, &rc).await?;
29        }
30
31        Ok(shell)
32    }
33}
34
35impl<SE: extensions::ShellExtensions, S: shell_builder::State> ShellBuilder<SE, S> {
36    /// Add a disabled option
37    pub fn disable_option(mut self, option: impl Into<String>) -> Self {
38        self.disabled_options.push(option.into());
39        self
40    }
41
42    /// Add an enabled option
43    pub fn enable_option(mut self, option: impl Into<String>) -> Self {
44        self.enabled_options.push(option.into());
45        self
46    }
47
48    /// Add many disabled options
49    pub fn disable_options(mut self, options: impl IntoIterator<Item: Into<String>>) -> Self {
50        self.disabled_options
51            .extend(options.into_iter().map(Into::into));
52        self
53    }
54
55    /// Add many enabled options
56    pub fn enable_options(mut self, options: impl IntoIterator<Item: Into<String>>) -> Self {
57        self.enabled_options
58            .extend(options.into_iter().map(Into::into));
59        self
60    }
61
62    /// Add a disabled shopt option
63    pub fn disable_shopt_option(mut self, option: impl Into<String>) -> Self {
64        self.disabled_shopt_options.push(option.into());
65        self
66    }
67
68    /// Add an enabled shopt option
69    pub fn enable_shopt_option(mut self, option: impl Into<String>) -> Self {
70        self.enabled_shopt_options.push(option.into());
71        self
72    }
73
74    /// Add many disabled shopt options
75    pub fn disable_shopt_options(mut self, options: impl IntoIterator<Item: Into<String>>) -> Self {
76        self.disabled_shopt_options
77            .extend(options.into_iter().map(Into::into));
78        self
79    }
80
81    /// Add many enabled shopt options
82    pub fn enable_shopt_options(mut self, options: impl IntoIterator<Item: Into<String>>) -> Self {
83        self.enabled_shopt_options
84            .extend(options.into_iter().map(Into::into));
85        self
86    }
87
88    /// Add a single builtin registration
89    pub fn builtin(mut self, name: impl Into<String>, reg: builtins::Registration<SE>) -> Self {
90        self.builtins.insert(name.into(), reg);
91        self
92    }
93
94    /// Add many builtin registrations
95    pub fn builtins(
96        mut self,
97        builtins: impl IntoIterator<Item = (String, builtins::Registration<SE>)>,
98    ) -> Self {
99        self.builtins.extend(builtins);
100        self
101    }
102
103    /// Adds a single variable to be initialized in the shell.
104    pub fn var(mut self, name: impl Into<String>, variable: ShellVariable) -> Self {
105        self.vars.insert(name.into(), variable);
106        self
107    }
108}
109
110/// Options for creating a new shell.
111#[derive(Default, bon::Builder)]
112#[builder(
113    builder_type(
114        name = ShellBuilder,
115        doc {
116        /// Builder for [Shell]
117    }),
118    finish_fn(
119        name = build_settings,
120        vis = "pub(self)",
121    ),
122    start_fn(
123        vis = "pub(self)"
124    )
125)]
126pub struct CreateOptions<SE: extensions::ShellExtensions = extensions::DefaultShellExtensions> {
127    /// Disabled options.
128    #[builder(field)]
129    pub disabled_options: Vec<String>,
130    /// Enabled options.
131    #[builder(field)]
132    pub enabled_options: Vec<String>,
133    /// Disabled shopt options.
134    #[builder(field)]
135    pub disabled_shopt_options: Vec<String>,
136    /// Enabled shopt options.
137    #[builder(field)]
138    pub enabled_shopt_options: Vec<String>,
139    /// Registered builtins.
140    #[builder(field)]
141    pub builtins: HashMap<String, builtins::Registration<SE>>,
142    /// Provides a set of variables to be initialized in the shell. If present, they
143    /// are assigned *after* inherited or well-known variables are set (when applicable).
144    #[builder(field)]
145    pub vars: HashMap<String, ShellVariable>,
146    /// Error behavior implementation.
147    #[builder(default)]
148    pub error_formatter: SE::ErrorFormatter,
149    /// Disallow overwriting regular files via output redirection.
150    #[builder(default)]
151    pub disallow_overwriting_regular_files_via_output_redirection: bool,
152    /// Do not execute commands.
153    #[builder(default)]
154    pub do_not_execute_commands: bool,
155    /// Exit after one command.
156    #[builder(default)]
157    pub exit_after_one_command: bool,
158    /// Whether the shell is interactive.
159    #[builder(default)]
160    pub interactive: bool,
161    /// Whether the shell is a login shell.
162    #[builder(default)]
163    pub login: bool,
164    /// Whether to skip using a readline-like interface for input.
165    #[builder(default)]
166    pub no_editing: bool,
167    /// System profile loading behavior.
168    #[builder(default)]
169    pub profile: ProfileLoadBehavior,
170    /// Rc file loading behavior.
171    #[builder(default)]
172    pub rc: RcLoadBehavior,
173    /// Whether to skip inheriting environment variables from the calling process.
174    #[builder(default)]
175    pub do_not_inherit_env: bool,
176    /// Whether to skip initializing well-known variables.
177    #[builder(default)]
178    pub skip_well_known_vars: bool,
179    /// Provides a set of initial open files to be tracked by the shell.
180    #[builder(default)]
181    pub fds: HashMap<ShellFd, openfiles::OpenFile>,
182    /// Whether to launch external commands as session leaders.
183    #[builder(default)]
184    pub external_cmd_leads_session: bool,
185    /// Initial working dir for the shell. If left unspecified, will be populated from
186    /// the host environment.
187    pub working_dir: Option<PathBuf>,
188    /// Whether the shell is in POSIX compliance mode.
189    #[builder(default)]
190    pub posix: bool,
191    /// Whether to print commands and arguments as they are read.
192    #[builder(default)]
193    pub print_commands_and_arguments: bool,
194    /// Whether commands are being read from stdin.
195    #[builder(default)]
196    pub read_commands_from_stdin: bool,
197    /// The name of the shell.
198    pub shell_name: Option<String>,
199    /// Base positional arguments for the shell (not including the shell name).
200    pub shell_args: Option<Vec<String>>,
201    /// Optionally provides a display string describing the version and variant of the shell.
202    pub shell_product_display_str: Option<String>,
203    /// Whether to run in maximal POSIX sh compatibility mode.
204    #[builder(default)]
205    pub sh_mode: bool,
206    /// Whether to treat expansion of unset variables as an error.
207    #[builder(default)]
208    pub treat_unset_variables_as_error: bool,
209    /// Whether to enable error-on-exit behavior.
210    #[builder(default)]
211    pub exit_on_nonzero_command_exit: bool,
212    /// Whether to disable pathname expansion.
213    #[builder(default)]
214    pub disable_pathname_expansion: bool,
215    /// Whether to print verbose output.
216    #[builder(default)]
217    pub verbose: bool,
218    /// Parser implementation to use.
219    #[builder(default)]
220    pub parser: crate::parser::ParserImpl,
221    /// Whether the shell is in command string mode (-c).
222    #[builder(default)]
223    pub command_string_mode: bool,
224    /// Maximum function call depth.
225    pub max_function_call_depth: Option<usize>,
226    /// Key bindings helper for the shell to use.
227    pub key_bindings: Option<KeyBindingsHelper>,
228    /// Brush implementation version.
229    pub shell_version: Option<String>,
230}
231
232impl<SE: extensions::ShellExtensions> Default for Shell<SE> {
233    fn default() -> Self {
234        Self {
235            error_formatter: SE::ErrorFormatter::default(),
236            traps: traps::TrapHandlerConfig::default(),
237            open_files: openfiles::OpenFiles::default(),
238            working_dir: PathBuf::default(),
239            env: env::ShellEnvironment::default(),
240            funcs: functions::FunctionEnv::default(),
241            options: options::RuntimeOptions::default(),
242            jobs: jobs::JobManager::default(),
243            aliases: HashMap::default(),
244            last_exit_status: 0,
245            last_exit_status_change_count: 0,
246            last_pipeline_statuses: vec![0],
247            depth: 0,
248            name: None,
249            args: vec![],
250            version: None,
251            product_display_str: None,
252            call_stack: callstack::CallStack::new(),
253            directory_stack: vec![],
254            completion_config: completion::Config::default(),
255            builtins: HashMap::default(),
256            program_location_cache: pathcache::PathCache::default(),
257            last_stopwatch_time: std::time::SystemTime::now(),
258            last_stopwatch_offset: 0,
259            parser_impl: crate::parser::ParserImpl::default(),
260            key_bindings: None,
261            history: None,
262        }
263    }
264}
265
266impl Shell {
267    /// Create an instance of [Shell] using the builder syntax
268    pub fn builder() -> ShellBuilder<extensions::DefaultShellExtensions, shell_builder::Empty> {
269        CreateOptions::builder()
270    }
271
272    /// Create an instance of [Shell] using the builder syntax, with custom extensions.
273    pub fn builder_with_extensions<SE: extensions::ShellExtensions>()
274    -> ShellBuilder<SE, shell_builder::Empty> {
275        CreateOptions::builder()
276    }
277}