diff --git a/brush-core/src/arithmetic.rs b/brush-core/src/arithmetic.rs index ec12669c..9ae0cb44 100644 --- a/brush-core/src/arithmetic.rs +++ b/brush-core/src/arithmetic.rs @@ -56,7 +56,7 @@ impl ExpandAndEvaluate for ast::UnexpandedArithmeticExpr { .map_err(|_e| EvalError::FailedToExpandExpression)?; // Now parse. - let expr = brush_parser::parse_arithmetic_expression(&expanded_self) + let expr = brush_parser::arithmetic::parse(&expanded_self) .map_err(|_e| EvalError::ParseError(expanded_self))?; // Trace if applicable. diff --git a/brush-core/src/builtin.rs b/brush-core/src/builtin.rs index 03fc5d73..204b33c5 100644 --- a/brush-core/src/builtin.rs +++ b/brush-core/src/builtin.rs @@ -2,8 +2,7 @@ use clap::builder::styling; use clap::Parser; use futures::future::BoxFuture; -use crate::commands::CommandArg; -use crate::context; +use crate::commands; use crate::error; use crate::ExecutionResult; @@ -49,12 +48,11 @@ pub(crate) use minus_or_plus_flag_arg; #[allow(clippy::module_name_repetitions)] pub struct BuiltinResult { /// The exit code from the command. - pub exit_code: BuiltinExitCode, + pub exit_code: ExitCode, } /// Exit codes for built-in commands. -#[allow(clippy::module_name_repetitions)] -pub enum BuiltinExitCode { +pub enum ExitCode { /// The command was successful. Success, /// The inputs to the command were invalid. @@ -73,20 +71,20 @@ pub enum BuiltinExitCode { BreakLoop(u8), } -impl From for BuiltinExitCode { +impl From for ExitCode { fn from(result: ExecutionResult) -> Self { if let Some(count) = result.continue_loop { - BuiltinExitCode::ContinueLoop(count) + ExitCode::ContinueLoop(count) } else if let Some(count) = result.break_loop { - BuiltinExitCode::BreakLoop(count) + ExitCode::BreakLoop(count) } else if result.return_from_function_or_script { - BuiltinExitCode::ReturnFromFunctionOrScript(result.exit_code) + ExitCode::ReturnFromFunctionOrScript(result.exit_code) } else if result.exit_shell { - BuiltinExitCode::ExitShell(result.exit_code) + ExitCode::ExitShell(result.exit_code) } else if result.exit_code == 0 { - BuiltinExitCode::Success + ExitCode::Success } else { - BuiltinExitCode::Custom(result.exit_code) + ExitCode::Custom(result.exit_code) } } } @@ -97,16 +95,14 @@ impl From for BuiltinExitCode { /// /// * The context in which the command is being executed. /// * The arguments to the command. -#[allow(clippy::module_name_repetitions)] -pub type BuiltinCommandExecuteFunc = fn( - context::CommandExecutionContext<'_>, - Vec, +pub type CommandExecuteFunc = fn( + commands::ExecutionContext<'_>, + Vec, ) -> BoxFuture<'_, Result>; /// Trait implemented by built-in shell commands. -#[allow(clippy::module_name_repetitions)] #[async_trait::async_trait] -pub trait BuiltinCommand: Parser { +pub trait Command: Parser { /// Instantiates the built-in command with the given arguments. /// /// # Arguments @@ -145,8 +141,8 @@ pub trait BuiltinCommand: Parser { /// * `context` - The context in which the command is being executed. async fn execute( &self, - context: context::CommandExecutionContext<'_>, - ) -> Result; + context: commands::ExecutionContext<'_>, + ) -> Result; /// Returns the textual help content associated with the command. /// @@ -154,36 +150,32 @@ pub trait BuiltinCommand: Parser { /// /// * `name` - The name of the command. /// * `content_type` - The type of content to retrieve. - fn get_content(name: &str, content_type: BuiltinContentType) -> String { + fn get_content(name: &str, content_type: ContentType) -> String { let mut clap_command = Self::command().styles(brush_help_styles()); clap_command.set_bin_name(name); match content_type { - BuiltinContentType::DetailedHelp => clap_command.render_long_help().ansi().to_string(), - BuiltinContentType::ShortUsage => get_builtin_short_usage(name, &clap_command), - BuiltinContentType::ShortDescription => { - get_builtin_short_description(name, &clap_command) - } + ContentType::DetailedHelp => clap_command.render_long_help().ansi().to_string(), + ContentType::ShortUsage => get_builtin_short_usage(name, &clap_command), + ContentType::ShortDescription => get_builtin_short_description(name, &clap_command), } } } /// Trait implemented by built-in shell commands that take specially handled declarations /// as arguments. -#[allow(clippy::module_name_repetitions)] #[async_trait::async_trait] -pub trait BuiltinDeclarationCommand: BuiltinCommand { +pub trait DeclarationCommand: Command { /// Stores the declarations within the command instance. /// /// # Arguments /// /// * `declarations` - The declarations to store. - fn set_declarations(&mut self, declarations: Vec); + fn set_declarations(&mut self, declarations: Vec); } /// Type of help content, typically associated with a built-in command. -#[allow(clippy::module_name_repetitions)] -pub enum BuiltinContentType { +pub enum ContentType { /// Detailed help content for the command. DetailedHelp, /// Short usage information for the command. @@ -193,14 +185,13 @@ pub enum BuiltinContentType { } /// Encapsulates a registration for a built-in command. -#[allow(clippy::module_name_repetitions)] #[derive(Clone)] -pub struct BuiltinRegistration { +pub struct Registration { /// Function to execute the builtin. - pub execute_func: BuiltinCommandExecuteFunc, + pub execute_func: CommandExecuteFunc, /// Function to retrieve the builtin's content/help text. - pub content_func: fn(&str, BuiltinContentType) -> String, + pub content_func: fn(&str, ContentType) -> String, /// Has this registration been disabled? pub disabled: bool, diff --git a/brush-core/src/builtins/alias.rs b/brush-core/src/builtins/alias.rs index 3b55c08c..4e27a398 100644 --- a/brush-core/src/builtins/alias.rs +++ b/brush-core/src/builtins/alias.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Manage aliases within the shell. #[derive(Parser)] @@ -16,12 +16,12 @@ pub(crate) struct AliasCommand { } #[async_trait::async_trait] -impl BuiltinCommand for AliasCommand { +impl builtin::Command for AliasCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut exit_code = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut exit_code = builtin::ExitCode::Success; if self.print || self.aliases.is_empty() { for (name, value) in &context.shell.aliases { @@ -42,7 +42,7 @@ impl BuiltinCommand for AliasCommand { "{}: {alias}: not found", context.command_name )?; - exit_code = BuiltinExitCode::Custom(1); + exit_code = builtin::ExitCode::Custom(1); } } } diff --git a/brush-core/src/builtins/bg.rs b/brush-core/src/builtins/bg.rs index 382eda96..80ad6505 100644 --- a/brush-core/src/builtins/bg.rs +++ b/brush-core/src/builtins/bg.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Moves a job to run in the background. #[derive(Parser)] @@ -11,12 +11,12 @@ pub(crate) struct BgCommand { } #[async_trait::async_trait] -impl BuiltinCommand for BgCommand { +impl builtin::Command for BgCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut exit_code = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut exit_code = builtin::ExitCode::Success; if !self.job_specs.is_empty() { for job_spec in &self.job_specs { @@ -29,7 +29,7 @@ impl BuiltinCommand for BgCommand { context.command_name, job_spec )?; - exit_code = BuiltinExitCode::Custom(1); + exit_code = builtin::ExitCode::Custom(1); } } } else { @@ -37,7 +37,7 @@ impl BuiltinCommand for BgCommand { job.move_to_background()?; } else { writeln!(context.stderr(), "{}: no current job", context.command_name)?; - exit_code = BuiltinExitCode::Custom(1); + exit_code = builtin::ExitCode::Custom(1); } } diff --git a/brush-core/src/builtins/break_.rs b/brush-core/src/builtins/break_.rs index 2382047e..b709fabf 100644 --- a/brush-core/src/builtins/break_.rs +++ b/brush-core/src/builtins/break_.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Breaks out of a control-flow loop. #[derive(Parser)] @@ -11,17 +11,17 @@ pub(crate) struct BreakCommand { } #[async_trait::async_trait] -impl BuiltinCommand for BreakCommand { +impl builtin::Command for BreakCommand { async fn execute( &self, - _context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + _context: commands::ExecutionContext<'_>, + ) -> Result { // If specified, which_loop needs to be positive. if self.which_loop <= 0 { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } #[allow(clippy::cast_sign_loss)] - Ok(BuiltinExitCode::BreakLoop((self.which_loop - 1) as u8)) + Ok(builtin::ExitCode::BreakLoop((self.which_loop - 1) as u8)) } } diff --git a/brush-core/src/builtins/builtin_.rs b/brush-core/src/builtins/builtin_.rs index fbe52afa..bb7d1e8b 100644 --- a/brush-core/src/builtins/builtin_.rs +++ b/brush-core/src/builtins/builtin_.rs @@ -1,14 +1,11 @@ use clap::Parser; use std::io::Write; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - commands, -}; +use crate::{builtin, commands}; /// Directly invokes a built-in, without going through typical search order. #[derive(Parser)] -pub(crate) struct BuiltiCommand { +pub(crate) struct BuiltinCommand { /// Name of built-in to invoke. builtin_name: Option, @@ -18,11 +15,11 @@ pub(crate) struct BuiltiCommand { } #[async_trait::async_trait] -impl BuiltinCommand for BuiltiCommand { +impl builtin::Command for BuiltinCommand { async fn execute( &self, - mut context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + mut context: commands::ExecutionContext<'_>, + ) -> Result { if let Some(builtin_name) = &self.builtin_name { if let Some(builtin) = context.shell.builtins.get(builtin_name) { context.command_name.clone_from(builtin_name); @@ -33,13 +30,13 @@ impl BuiltinCommand for BuiltiCommand { (builtin.execute_func)(context, args) .await - .map(|res: crate::builtin::BuiltinResult| res.exit_code) + .map(|res: builtin::BuiltinResult| res.exit_code) } else { writeln!(context.stderr(), "{builtin_name}: command not found")?; - Ok(BuiltinExitCode::Custom(1)) + Ok(builtin::ExitCode::Custom(1)) } } else { - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } } diff --git a/brush-core/src/builtins/cd.rs b/brush-core/src/builtins/cd.rs index eb12eb12..050b54bb 100644 --- a/brush-core/src/builtins/cd.rs +++ b/brush-core/src/builtins/cd.rs @@ -3,7 +3,7 @@ use std::path::PathBuf; use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Change the current working directory. #[derive(Parser)] @@ -29,11 +29,11 @@ pub(crate) struct CdCommand { } #[async_trait::async_trait] -impl BuiltinCommand for CdCommand { +impl builtin::Command for CdCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { // TODO: implement options if self.force_follow_symlinks || self.use_physical_dir @@ -50,7 +50,7 @@ impl BuiltinCommand for CdCommand { PathBuf::from(home_var.to_string()) } else { writeln!(context.stderr(), "HOME not set")?; - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } }; @@ -58,10 +58,10 @@ impl BuiltinCommand for CdCommand { Ok(()) => {} Err(e) => { writeln!(context.stderr(), "cd: {e}")?; - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/colon.rs b/brush-core/src/builtins/colon.rs index 15a9ce65..64df45ea 100644 --- a/brush-core/src/builtins/colon.rs +++ b/brush-core/src/builtins/colon.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// No-op command. #[derive(Parser)] @@ -11,11 +11,11 @@ pub(crate) struct ColonCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ColonCommand { +impl builtin::Command for ColonCommand { async fn execute( &self, - _context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - Ok(BuiltinExitCode::Success) + _context: commands::ExecutionContext<'_>, + ) -> Result { + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/complete.rs b/brush-core/src/builtins/complete.rs index 625dfc7c..25ca14c2 100644 --- a/brush-core/src/builtins/complete.rs +++ b/brush-core/src/builtins/complete.rs @@ -3,8 +3,9 @@ use std::collections::HashMap; use std::fmt::Write as _; use std::io::Write; -use crate::builtin::{self, BuiltinCommand, BuiltinExitCode}; -use crate::completion::{self, CompleteAction, CompleteOption, CompletionSpec}; +use crate::builtin; +use crate::commands; +use crate::completion::{self, CompleteAction, CompleteOption, Spec}; use crate::error; #[derive(Parser)] @@ -92,7 +93,7 @@ pub(crate) struct CommonCompleteCommandArgs { } impl CommonCompleteCommandArgs { - fn create_spec(&self) -> completion::CompletionSpec { + fn create_spec(&self) -> completion::Spec { let filter_pattern_excludes; let filter_pattern = if let Some(filter_pattern) = self.filter_pattern.as_ref() { if let Some(filter_pattern) = filter_pattern.strip_prefix('!') { @@ -107,8 +108,8 @@ impl CommonCompleteCommandArgs { None }; - let mut spec = completion::CompletionSpec { - options: completion::CompletionOptions::default(), + let mut spec = completion::Spec { + options: completion::GenerationOptions::default(), actions: self.resolve_actions(), glob_pattern: self.glob_pattern.clone(), word_list: self.word_list.clone(), @@ -206,11 +207,11 @@ pub(crate) struct CompleteCommand { } #[async_trait::async_trait] -impl BuiltinCommand for CompleteCommand { +impl builtin::Command for CompleteCommand { async fn execute( &self, - mut context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + mut context: commands::ExecutionContext<'_>, + ) -> Result { // If -D, -E, or -I are specified, then any names provided are ignored. if self.use_as_default || self.use_for_empty_line @@ -224,14 +225,14 @@ impl BuiltinCommand for CompleteCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } impl CompleteCommand { fn process_global( &self, - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, ) -> Result<(), crate::error::Error> { // These are processed in an intentional order. let special_option_name; @@ -282,7 +283,7 @@ impl CompleteCommand { } fn display_spec_for_command( - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut commands::ExecutionContext<'_>, name: &str, ) -> Result<(), error::Error> { if let Some(spec) = context.shell.completion_config.get(name) { @@ -293,10 +294,10 @@ impl CompleteCommand { } fn display_spec( - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, special_name: Option<&str>, command_name: Option<&str>, - spec: &CompletionSpec, + spec: &Spec, ) -> Result<(), error::Error> { let mut s = String::from("complete"); @@ -396,7 +397,7 @@ impl CompleteCommand { fn process_for_command( &self, - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, name: &str, ) -> Result<(), crate::error::Error> { if self.print { @@ -424,16 +425,16 @@ pub(crate) struct CompGenCommand { } #[async_trait::async_trait] -impl BuiltinCommand for CompGenCommand { +impl builtin::Command for CompGenCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let spec = self.common_args.create_spec(); let token_to_complete = self.word.as_deref().unwrap_or_default(); - let completion_context = completion::CompletionContext { + let completion_context = completion::Context { token_to_complete, preceding_token: None, command_name: None, @@ -451,17 +452,17 @@ impl BuiltinCommand for CompGenCommand { .await?; match result { - completion::CompletionResult::Candidates(candidates, _options) => { + completion::Answer::Candidates(candidates, _options) => { for candidate in candidates { writeln!(context.stdout(), "{candidate}")?; } } - completion::CompletionResult::RestartCompletionProcess => { + completion::Answer::RestartCompletionProcess => { return error::unimp("restart completion") } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } @@ -486,11 +487,11 @@ pub(crate) struct CompOptCommand { } #[async_trait::async_trait] -impl BuiltinCommand for CompOptCommand { +impl builtin::Command for CompOptCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let mut options = HashMap::new(); for option in &self.disabled_options { options.insert(option.clone(), false); @@ -505,7 +506,7 @@ impl BuiltinCommand for CompOptCommand { context.stderr(), "compopt: cannot specify names with -D, -E, or -I" )?; - return Ok(builtin::BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } for name in &self.names { @@ -516,7 +517,7 @@ impl BuiltinCommand for CompOptCommand { if let Some(spec) = &mut context.shell.completion_config.default { Self::set_options_for_spec(spec, &options); } else { - let mut spec = CompletionSpec::default(); + let mut spec = Spec::default(); Self::set_options_for_spec(&mut spec, &options); std::mem::swap( &mut context.shell.completion_config.default, @@ -527,7 +528,7 @@ impl BuiltinCommand for CompOptCommand { if let Some(spec) = &mut context.shell.completion_config.empty_line { Self::set_options_for_spec(spec, &options); } else { - let mut spec = CompletionSpec::default(); + let mut spec = Spec::default(); Self::set_options_for_spec(&mut spec, &options); std::mem::swap( &mut context.shell.completion_config.empty_line, @@ -538,7 +539,7 @@ impl BuiltinCommand for CompOptCommand { if let Some(spec) = &mut context.shell.completion_config.initial_word { Self::set_options_for_spec(spec, &options); } else { - let mut spec = CompletionSpec::default(); + let mut spec = Spec::default(); Self::set_options_for_spec(&mut spec, &options); std::mem::swap( &mut context.shell.completion_config.initial_word, @@ -557,17 +558,17 @@ impl BuiltinCommand for CompOptCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } impl CompOptCommand { - fn set_options_for_spec(spec: &mut CompletionSpec, options: &HashMap) { + fn set_options_for_spec(spec: &mut Spec, options: &HashMap) { Self::set_options(&mut spec.options, options); } fn set_options( - target_options: &mut completion::CompletionOptions, + target_options: &mut completion::GenerationOptions, options: &HashMap, ) { for (option, value) in options { diff --git a/brush-core/src/builtins/continue_.rs b/brush-core/src/builtins/continue_.rs index 284bfae6..3b3f5dd3 100644 --- a/brush-core/src/builtins/continue_.rs +++ b/brush-core/src/builtins/continue_.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Continue to the next iteration of a control-flow loop. #[derive(Parser)] @@ -11,17 +11,17 @@ pub(crate) struct ContinueCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ContinueCommand { +impl builtin::Command for ContinueCommand { async fn execute( &self, - _context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + _context: commands::ExecutionContext<'_>, + ) -> Result { // If specified, which_loop needs to be positive. if self.which_loop <= 0 { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } #[allow(clippy::cast_sign_loss)] - Ok(BuiltinExitCode::ContinueLoop((self.which_loop - 1) as u8)) + Ok(builtin::ExitCode::ContinueLoop((self.which_loop - 1) as u8)) } } diff --git a/brush-core/src/builtins/declare.rs b/brush-core/src/builtins/declare.rs index f2121182..8f21140b 100644 --- a/brush-core/src/builtins/declare.rs +++ b/brush-core/src/builtins/declare.rs @@ -3,8 +3,7 @@ use itertools::Itertools; use std::io::Write; use crate::{ - builtin::{self, BuiltinCommand, BuiltinDeclarationCommand, BuiltinExitCode}, - commands::CommandArg, + builtin, commands, env::{EnvironmentLookup, EnvironmentScope}, error, variables::{ @@ -73,7 +72,7 @@ pub(crate) struct DeclareCommand { // N.B. These are skipped by clap, but filled in by the BuiltinDeclarationCommand trait. // #[clap(skip)] - declarations: Vec, + declarations: Vec, } #[derive(Clone, Copy)] @@ -83,23 +82,23 @@ enum DeclareVerb { Readonly, } -impl BuiltinDeclarationCommand for DeclareCommand { - fn set_declarations(&mut self, declarations: Vec) { +impl builtin::DeclarationCommand for DeclareCommand { + fn set_declarations(&mut self, declarations: Vec) { self.declarations = declarations; } } #[allow(clippy::too_many_lines)] #[async_trait::async_trait] -impl BuiltinCommand for DeclareCommand { +impl builtin::Command for DeclareCommand { fn takes_plus_options() -> bool { true } async fn execute( &self, - mut context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + mut context: commands::ExecutionContext<'_>, + ) -> Result { let verb = match context.command_name.as_str() { "local" => DeclareVerb::Local, "readonly" => DeclareVerb::Readonly, @@ -112,19 +111,19 @@ impl BuiltinCommand for DeclareCommand { context.stderr(), "UNIMPLEMENTED: declare -I: locals inherit from previous scope" )?; - return Ok(BuiltinExitCode::Unimplemented); + return Ok(builtin::ExitCode::Unimplemented); } - let mut result = BuiltinExitCode::Success; + let mut result = builtin::ExitCode::Success; if !self.declarations.is_empty() { for declaration in &self.declarations { if self.print && !matches!(verb, DeclareVerb::Readonly) { if !self.try_display_declaration(&mut context, declaration, verb)? { - result = BuiltinExitCode::Custom(1); + result = builtin::ExitCode::Custom(1); } } else { if !self.process_declaration(&mut context, declaration, verb)? { - result = BuiltinExitCode::Custom(1); + result = builtin::ExitCode::Custom(1); } } } @@ -149,13 +148,13 @@ impl BuiltinCommand for DeclareCommand { impl DeclareCommand { fn try_display_declaration( &self, - context: &mut crate::context::CommandExecutionContext<'_>, - declaration: &CommandArg, + context: &mut crate::commands::ExecutionContext<'_>, + declaration: &commands::CommandArg, verb: DeclareVerb, ) -> Result { let name = match declaration { - CommandArg::String(s) => s, - CommandArg::Assignment(_) => { + commands::CommandArg::String(s) => s, + commands::CommandArg::Assignment(_) => { writeln!(context.stderr(), "declare: {declaration}: not found")?; return Ok(false); } @@ -212,8 +211,8 @@ impl DeclareCommand { fn process_declaration( &self, - context: &mut crate::context::CommandExecutionContext<'_>, - declaration: &CommandArg, + context: &mut crate::commands::ExecutionContext<'_>, + declaration: &commands::CommandArg, verb: DeclareVerb, ) -> Result { let create_var_local = matches!(verb, DeclareVerb::Local) @@ -290,7 +289,7 @@ impl DeclareCommand { #[allow(clippy::unwrap_in_result)] fn declaration_to_name_and_value( - declaration: &CommandArg, + declaration: &commands::CommandArg, ) -> Result<(String, Option, Option, bool), error::Error> { let name; let assigned_index; @@ -298,7 +297,7 @@ impl DeclareCommand { let name_is_array; match declaration { - CommandArg::String(s) => { + commands::CommandArg::String(s) => { // We need to handle the case of someone invoking `declare array[index]`. // In such case, we ignore the index and treat it as a declaration of // the array. @@ -317,7 +316,7 @@ impl DeclareCommand { } initial_value = None; } - CommandArg::Assignment(assignment) => { + commands::CommandArg::Assignment(assignment) => { match &assignment.name { brush_parser::ast::AssignmentName::VariableName(var_name) => { name = var_name.to_owned(); @@ -368,7 +367,7 @@ impl DeclareCommand { fn display_matching_env_declarations( &self, - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, verb: DeclareVerb, ) -> Result<(), error::Error> { // @@ -476,7 +475,7 @@ impl DeclareCommand { fn display_matching_functions( &self, - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, ) -> Result<(), error::Error> { for (name, registration) in context.shell.funcs.iter().sorted_by_key(|v| v.0) { if self.function_names_only { diff --git a/brush-core/src/builtins/dirs.rs b/brush-core/src/builtins/dirs.rs index c6a9f4eb..6ac62b41 100644 --- a/brush-core/src/builtins/dirs.rs +++ b/brush-core/src/builtins/dirs.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Manage the current directory stack. #[derive(Parser, Debug, Default)] @@ -25,11 +25,11 @@ pub(crate) struct DirsCommand { } #[async_trait::async_trait] -impl BuiltinCommand for DirsCommand { +impl builtin::Command for DirsCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.clear { context.shell.directory_stack.clear(); } else { @@ -62,9 +62,9 @@ impl BuiltinCommand for DirsCommand { } } - return Ok(BuiltinExitCode::Success); + return Ok(builtin::ExitCode::Success); } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/dot.rs b/brush-core/src/builtins/dot.rs index b9d2f65e..dd288baa 100644 --- a/brush-core/src/builtins/dot.rs +++ b/brush-core/src/builtins/dot.rs @@ -2,10 +2,7 @@ use std::path::Path; use clap::Parser; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - interp::ExecutionParameters, -}; +use crate::{builtin, commands, interp::ExecutionParameters}; /// Evalute the provided script in the current shell environment. #[derive(Debug, Parser)] @@ -19,11 +16,11 @@ pub(crate) struct DotCommand { } #[async_trait::async_trait] -impl BuiltinCommand for DotCommand { +impl builtin::Command for DotCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { // TODO: Handle trap inheritance. let script_args: Vec<_> = self.script_args.iter().map(|a| a.as_str()).collect(); @@ -39,9 +36,9 @@ impl BuiltinCommand for DotCommand { .await?; if result.exit_code != 0 { - return Ok(crate::builtin::BuiltinExitCode::Custom(result.exit_code)); + return Ok(builtin::ExitCode::Custom(result.exit_code)); } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/echo.rs b/brush-core/src/builtins/echo.rs index 84017bf9..45213758 100644 --- a/brush-core/src/builtins/echo.rs +++ b/brush-core/src/builtins/echo.rs @@ -1,8 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; -use crate::escape; +use crate::{builtin, commands, escape}; /// Echo text to standard output. #[derive(Parser)] @@ -26,11 +25,11 @@ pub(crate) struct EchoCommand { } #[async_trait::async_trait] -impl BuiltinCommand for EchoCommand { +impl builtin::Command for EchoCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let mut trailing_newline = !self.no_trailing_newline; let mut s; if self.interpret_backslash_escapes { @@ -60,6 +59,6 @@ impl BuiltinCommand for EchoCommand { } write!(context.stdout(), "{s}")?; - return Ok(BuiltinExitCode::Success); + return Ok(builtin::ExitCode::Success); } } diff --git a/brush-core/src/builtins/enable.rs b/brush-core/src/builtins/enable.rs index 008cb1a1..b7d947a1 100644 --- a/brush-core/src/builtins/enable.rs +++ b/brush-core/src/builtins/enable.rs @@ -2,7 +2,8 @@ use clap::Parser; use itertools::Itertools; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::builtin; +use crate::commands; use crate::error; /// Enable, disable, or display built-in commands. @@ -37,12 +38,12 @@ pub(crate) struct EnableCommand { } #[async_trait::async_trait] -impl BuiltinCommand for EnableCommand { +impl builtin::Command for EnableCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut result = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut result = builtin::ExitCode::Success; if self.shared_object_path.is_some() { return error::unimp("enable -f"); @@ -57,7 +58,7 @@ impl BuiltinCommand for EnableCommand { builtin.disabled = self.disable; } else { writeln!(context.stderr(), "{name}: not a shell builtin")?; - result = BuiltinExitCode::Custom(1); + result = builtin::ExitCode::Custom(1); } } } else { diff --git a/brush-core/src/builtins/eval.rs b/brush-core/src/builtins/eval.rs index 05ca376f..25879669 100644 --- a/brush-core/src/builtins/eval.rs +++ b/brush-core/src/builtins/eval.rs @@ -1,7 +1,4 @@ -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - interp::ExecutionParameters, -}; +use crate::{builtin, commands, interp::ExecutionParameters}; use clap::Parser; /// Evalute the given string as script. @@ -13,11 +10,11 @@ pub(crate) struct EvalCommand { } #[async_trait::async_trait] -impl BuiltinCommand for EvalCommand { +impl builtin::Command for EvalCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if !self.args.is_empty() { let args_concatenated = self.args.join(" "); @@ -33,9 +30,9 @@ impl BuiltinCommand for EvalCommand { ) .await?; - Ok(BuiltinExitCode::Custom(exec_result.exit_code)) + Ok(builtin::ExitCode::Custom(exec_result.exit_code)) } else { - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } } diff --git a/brush-core/src/builtins/exec.rs b/brush-core/src/builtins/exec.rs index d8503da4..12f47f19 100644 --- a/brush-core/src/builtins/exec.rs +++ b/brush-core/src/builtins/exec.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::{borrow::Cow, os::unix::process::CommandExt}; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - commands, error, -}; +use crate::{builtin, commands, error}; /// Exec the provided command. #[derive(Parser)] @@ -27,13 +24,13 @@ pub(crate) struct ExecCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ExecCommand { +impl builtin::Command for ExecCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.args.is_empty() { - return Ok(BuiltinExitCode::Success); + return Ok(builtin::ExitCode::Success); } let mut argv0 = Cow::Borrowed(self.name_for_argv0.as_ref().unwrap_or(&self.args[0])); @@ -58,9 +55,9 @@ impl BuiltinCommand for ExecCommand { let exec_error = cmd.exec(); if exec_error.kind() == std::io::ErrorKind::NotFound { - Ok(BuiltinExitCode::Custom(127)) + Ok(builtin::ExitCode::Custom(127)) } else { - Err(crate::error::Error::from(exec_error)) + Err(error::Error::from(exec_error)) } } } diff --git a/brush-core/src/builtins/exit.rs b/brush-core/src/builtins/exit.rs index 6dff89c2..610922ab 100644 --- a/brush-core/src/builtins/exit.rs +++ b/brush-core/src/builtins/exit.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Exit the shell. #[derive(Parser)] @@ -10,11 +10,11 @@ pub(crate) struct ExitCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ExitCommand { +impl builtin::Command for ExitCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let code_8bit: u8; #[allow(clippy::cast_sign_loss)] @@ -24,6 +24,6 @@ impl BuiltinCommand for ExitCommand { code_8bit = context.shell.last_exit_status; } - Ok(BuiltinExitCode::ExitShell(code_8bit)) + Ok(builtin::ExitCode::ExitShell(code_8bit)) } } diff --git a/brush-core/src/builtins/export.rs b/brush-core/src/builtins/export.rs index 6356377e..f2d94ea5 100644 --- a/brush-core/src/builtins/export.rs +++ b/brush-core/src/builtins/export.rs @@ -3,8 +3,7 @@ use itertools::Itertools; use std::io::Write; use crate::{ - builtin::{BuiltinCommand, BuiltinDeclarationCommand, BuiltinExitCode}, - commands, + builtin, commands, env::{EnvironmentLookup, EnvironmentScope}, variables, }; @@ -30,18 +29,18 @@ pub(crate) struct ExportCommand { declarations: Vec, } -impl BuiltinDeclarationCommand for ExportCommand { +impl builtin::DeclarationCommand for ExportCommand { fn set_declarations(&mut self, declarations: Vec) { self.declarations = declarations; } } #[async_trait::async_trait] -impl BuiltinCommand for ExportCommand { +impl builtin::Command for ExportCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if !self.declarations.is_empty() { for decl in &self.declarations { match decl { @@ -57,7 +56,7 @@ impl BuiltinCommand for ExportCommand { brush_parser::ast::AssignmentName::VariableName(name) => name, brush_parser::ast::AssignmentName::ArrayElementName(_, _) => { writeln!(context.stderr(), "not a valid variable name")?; - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } }; @@ -104,6 +103,6 @@ impl BuiltinCommand for ExportCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/false_.rs b/brush-core/src/builtins/false_.rs index f5e0ae45..d3262ea9 100644 --- a/brush-core/src/builtins/false_.rs +++ b/brush-core/src/builtins/false_.rs @@ -1,17 +1,17 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Return a non-zero exit code. #[derive(Parser)] pub(crate) struct FalseCommand {} #[async_trait::async_trait] -impl BuiltinCommand for FalseCommand { +impl builtin::Command for FalseCommand { async fn execute( &self, - _context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - Ok(BuiltinExitCode::Custom(1)) + _context: commands::ExecutionContext<'_>, + ) -> Result { + Ok(builtin::ExitCode::Custom(1)) } } diff --git a/brush-core/src/builtins/fg.rs b/brush-core/src/builtins/fg.rs index b3e33c2e..0958cded 100644 --- a/brush-core/src/builtins/fg.rs +++ b/brush-core/src/builtins/fg.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Move a specified job to the foreground. #[derive(Parser)] @@ -11,11 +11,11 @@ pub(crate) struct FgCommand { } #[async_trait::async_trait] -impl BuiltinCommand for FgCommand { +impl builtin::Command for FgCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let mut stderr = context.stdout(); if let Some(job_spec) = &self.job_spec { @@ -24,14 +24,14 @@ impl BuiltinCommand for FgCommand { writeln!(stderr, "{}", job.command_line)?; let result = job.wait().await?; - Ok(BuiltinExitCode::from(result)) + Ok(builtin::ExitCode::from(result)) } else { writeln!( stderr, "{}: {}: no such job", job_spec, context.command_name )?; - Ok(BuiltinExitCode::Custom(1)) + Ok(builtin::ExitCode::Custom(1)) } } else { if let Some(job) = context.shell.jobs.current_job_mut() { @@ -39,10 +39,10 @@ impl BuiltinCommand for FgCommand { writeln!(stderr, "{}", job.command_line)?; let result = job.wait().await?; - Ok(BuiltinExitCode::from(result)) + Ok(builtin::ExitCode::from(result)) } else { writeln!(stderr, "{}: no current job", context.command_name)?; - Ok(BuiltinExitCode::Custom(1)) + Ok(builtin::ExitCode::Custom(1)) } } } diff --git a/brush-core/src/builtins/getopts.rs b/brush-core/src/builtins/getopts.rs index 038e253b..37e22630 100644 --- a/brush-core/src/builtins/getopts.rs +++ b/brush-core/src/builtins/getopts.rs @@ -2,10 +2,7 @@ use std::{borrow::Cow, collections::HashMap}; use clap::Parser; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - variables, -}; +use crate::{builtin, commands, variables}; /// Parse command options. #[derive(Parser)] @@ -22,11 +19,11 @@ pub(crate) struct GetOptsCommand { } #[async_trait::async_trait] -impl BuiltinCommand for GetOptsCommand { +impl builtin::Command for GetOptsCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let mut args = HashMap::::new(); // Build the args map @@ -37,7 +34,7 @@ impl BuiltinCommand for GetOptsCommand { args.insert(last_char, true); continue; } else { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } } @@ -54,17 +51,17 @@ impl BuiltinCommand for GetOptsCommand { .parse()?; if next_index < 1 { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } let mut next_index_zero_based = next_index - 1; if next_index_zero_based >= self.args.len() { - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } let arg = self.args[next_index_zero_based].as_str(); if !arg.starts_with('-') || arg.len() != 2 { - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } // Single character option @@ -75,7 +72,7 @@ impl BuiltinCommand for GetOptsCommand { next_index_zero_based += 1; if next_index_zero_based >= self.args.len() { - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } let opt_arg = self.args[next_index_zero_based].as_str(); @@ -105,9 +102,9 @@ impl BuiltinCommand for GetOptsCommand { crate::env::EnvironmentScope::Global, )?; } else { - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/help.rs b/brush-core/src/builtins/help.rs index 914f906e..ae8841cf 100644 --- a/brush-core/src/builtins/help.rs +++ b/brush-core/src/builtins/help.rs @@ -1,7 +1,4 @@ -use crate::{ - builtin::{self, BuiltinCommand, BuiltinExitCode, BuiltinRegistration}, - context, -}; +use crate::{builtin, commands, error}; use clap::Parser; use itertools::Itertools; use std::io::Write; @@ -28,11 +25,11 @@ pub(crate) struct HelpCommand { const VERSION: &str = env!("CARGO_PKG_VERSION"); #[async_trait::async_trait] -impl BuiltinCommand for HelpCommand { +impl builtin::Command for HelpCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.topic_patterns.is_empty() { Self::display_general_help(&context)?; } else { @@ -41,13 +38,13 @@ impl BuiltinCommand for HelpCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } impl HelpCommand { fn display_general_help( - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, ) -> Result<(), crate::error::Error> { const COLUMN_COUNT: usize = 3; @@ -76,7 +73,7 @@ impl HelpCommand { fn display_help_for_topic_pattern( &self, - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, topic_pattern: &str, ) -> Result<(), crate::error::Error> { let pattern = crate::patterns::Pattern::from(topic_pattern); @@ -102,16 +99,16 @@ impl HelpCommand { fn display_help_for_builtin( &self, - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, name: &str, - registration: &BuiltinRegistration, - ) -> Result<(), crate::error::Error> { + registration: &builtin::Registration, + ) -> Result<(), error::Error> { let content_type = if self.short_description { - builtin::BuiltinContentType::ShortDescription + builtin::ContentType::ShortDescription } else if self.short_usage { - builtin::BuiltinContentType::ShortUsage + builtin::ContentType::ShortUsage } else { - builtin::BuiltinContentType::DetailedHelp + builtin::ContentType::DetailedHelp }; let content = (registration.content_func)(name, content_type); @@ -122,8 +119,8 @@ impl HelpCommand { } fn get_builtins_sorted_by_name<'a>( - context: &'a context::CommandExecutionContext<'_>, -) -> Vec<(&'a String, &'a BuiltinRegistration)> { + context: &'a commands::ExecutionContext<'_>, +) -> Vec<(&'a String, &'a builtin::Registration)> { context .shell .builtins diff --git a/brush-core/src/builtins/jobs.rs b/brush-core/src/builtins/jobs.rs index 7875fde4..4e473204 100644 --- a/brush-core/src/builtins/jobs.rs +++ b/brush-core/src/builtins/jobs.rs @@ -1,8 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; -use crate::{error, jobs}; +use crate::{builtin, commands, error, jobs}; /// Manage jobs. #[derive(Parser)] @@ -33,11 +32,11 @@ pub(crate) struct JobsCommand { } #[async_trait::async_trait] -impl BuiltinCommand for JobsCommand { +impl builtin::Command for JobsCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.also_show_pids { return error::unimp("jobs -l"); } @@ -53,14 +52,14 @@ impl BuiltinCommand for JobsCommand { return error::unimp("jobs with job specs"); } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } impl JobsCommand { fn display_job( &self, - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, job: &jobs::Job, ) -> Result<(), crate::error::Error> { if self.running_jobs_only && !matches!(job.state, jobs::JobState::Running) { diff --git a/brush-core/src/builtins/kill.rs b/brush-core/src/builtins/kill.rs index 4a971817..81adf2d2 100644 --- a/brush-core/src/builtins/kill.rs +++ b/brush-core/src/builtins/kill.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - error, -}; +use crate::{builtin, commands, error}; /// Signal a job or process. #[derive(Parser)] @@ -29,11 +26,11 @@ pub(crate) struct KillCommand { } #[async_trait::async_trait] -impl BuiltinCommand for KillCommand { +impl builtin::Command for KillCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.signal_name.is_some() { return error::unimp("kill -s"); } @@ -46,10 +43,10 @@ impl BuiltinCommand for KillCommand { } else { if self.args.len() != 1 { writeln!(context.stderr(), "{}: invalid usage", context.command_name)?; - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } - let exit_code = BuiltinExitCode::Success; + let exit_code = builtin::ExitCode::Success; let pid_or_job_spec = &self.args[0]; if pid_or_job_spec.starts_with('%') { @@ -63,7 +60,7 @@ impl BuiltinCommand for KillCommand { context.command_name, pid_or_job_spec )?; - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } } else { let pid = pid_or_job_spec.parse::()?; diff --git a/brush-core/src/builtins/let_.rs b/brush-core/src/builtins/let_.rs index cec1a4fa..a087c947 100644 --- a/brush-core/src/builtins/let_.rs +++ b/brush-core/src/builtins/let_.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::{ - arithmetic::Evaluatable, - builtin::{BuiltinCommand, BuiltinExitCode}, -}; +use crate::{arithmetic::Evaluatable, builtin, commands}; /// Evalute arithmetic expressions. #[derive(Parser)] @@ -15,12 +12,12 @@ pub(crate) struct LetCommand { } #[async_trait::async_trait] -impl BuiltinCommand for LetCommand { +impl builtin::Command for LetCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut exit_code = BuiltinExitCode::InvalidUsage; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut exit_code = builtin::ExitCode::InvalidUsage; if self.exprs.is_empty() { writeln!(context.stderr(), "missing expression")?; @@ -28,13 +25,13 @@ impl BuiltinCommand for LetCommand { } for expr in &self.exprs { - let parsed = brush_parser::parse_arithmetic_expression(expr.as_str())?; + let parsed = brush_parser::arithmetic::parse(expr.as_str())?; let evaluated = parsed.eval(context.shell).await?; if evaluated == 0 { - exit_code = BuiltinExitCode::Custom(1); + exit_code = builtin::ExitCode::Custom(1); } else { - exit_code = BuiltinExitCode::Custom(0); + exit_code = builtin::ExitCode::Custom(0); } } diff --git a/brush-core/src/builtins/mod.rs b/brush-core/src/builtins/mod.rs index 7919b26a..f9c868a5 100644 --- a/brush-core/src/builtins/mod.rs +++ b/brush-core/src/builtins/mod.rs @@ -2,11 +2,8 @@ use futures::future::BoxFuture; use std::collections::HashMap; use std::io::Write; -use crate::builtin::{ - self, BuiltinCommand, BuiltinDeclarationCommand, BuiltinRegistration, BuiltinResult, -}; -use crate::commands::CommandArg; -use crate::context; +use crate::builtin; +use crate::commands::{self, CommandArg}; use crate::error; mod alias; @@ -54,8 +51,8 @@ mod unimp; mod unset; mod wait; -fn builtin() -> BuiltinRegistration { - BuiltinRegistration { +fn builtin() -> builtin::Registration { + builtin::Registration { execute_func: exec_builtin::, content_func: get_builtin_content::, disabled: false, @@ -64,8 +61,8 @@ fn builtin() -> BuiltinRegistration { } } -fn special_builtin() -> BuiltinRegistration { - BuiltinRegistration { +fn special_builtin() -> builtin::Registration { + builtin::Registration { execute_func: exec_builtin::, content_func: get_builtin_content::, disabled: false, @@ -74,8 +71,8 @@ fn special_builtin() -> BuiltinRegistration { } } -fn decl_builtin() -> BuiltinRegistration { - BuiltinRegistration { +fn decl_builtin() -> builtin::Registration { + builtin::Registration { execute_func: exec_declaration_builtin::, content_func: get_builtin_content::, disabled: false, @@ -84,8 +81,8 @@ fn decl_builtin() -> BuiltinRegistration { } } -fn special_decl_builtin() -> BuiltinRegistration { - BuiltinRegistration { +fn special_decl_builtin() -> builtin::Registration { + builtin::Registration { execute_func: exec_declaration_builtin::, content_func: get_builtin_content::, disabled: false, @@ -94,24 +91,24 @@ fn special_decl_builtin() -> BuiltinRegistr } } -fn get_builtin_content( +fn get_builtin_content( name: &str, - content_type: builtin::BuiltinContentType, + content_type: builtin::ContentType, ) -> String { T::get_content(name, content_type) } -fn exec_builtin( - context: context::CommandExecutionContext<'_>, +fn exec_builtin( + context: commands::ExecutionContext<'_>, args: Vec, -) -> BoxFuture<'_, Result> { +) -> BoxFuture<'_, Result> { Box::pin(async move { exec_builtin_impl::(context, args).await }) } -async fn exec_builtin_impl( - context: context::CommandExecutionContext<'_>, +async fn exec_builtin_impl( + context: commands::ExecutionContext<'_>, args: Vec, -) -> Result { +) -> Result { let plain_args = args.into_iter().map(|arg| match arg { CommandArg::String(s) => s, CommandArg::Assignment(a) => a.to_string(), @@ -122,28 +119,28 @@ async fn exec_builtin_impl( Ok(command) => command, Err(e) => { writeln!(context.stderr(), "{e}")?; - return Ok(BuiltinResult { - exit_code: builtin::BuiltinExitCode::InvalidUsage, + return Ok(builtin::BuiltinResult { + exit_code: builtin::ExitCode::InvalidUsage, }); } }; - Ok(BuiltinResult { + Ok(builtin::BuiltinResult { exit_code: command.execute(context).await?, }) } -fn exec_declaration_builtin( - context: context::CommandExecutionContext<'_>, +fn exec_declaration_builtin( + context: commands::ExecutionContext<'_>, args: Vec, -) -> BoxFuture<'_, Result> { +) -> BoxFuture<'_, Result> { Box::pin(async move { exec_declaration_builtin_impl::(context, args).await }) } -async fn exec_declaration_builtin_impl( - context: context::CommandExecutionContext<'_>, +async fn exec_declaration_builtin_impl( + context: commands::ExecutionContext<'_>, args: Vec, -) -> Result { +) -> Result { let mut options = vec![]; let mut declarations = vec![]; @@ -161,15 +158,15 @@ async fn exec_declaration_builtin_impl( Ok(command) => command, Err(e) => { writeln!(context.stderr(), "{e}")?; - return Ok(BuiltinResult { - exit_code: builtin::BuiltinExitCode::InvalidUsage, + return Ok(builtin::BuiltinResult { + exit_code: builtin::ExitCode::InvalidUsage, }); } }; command.set_declarations(declarations); - Ok(BuiltinResult { + Ok(builtin::BuiltinResult { exit_code: command.execute(context).await?, }) } @@ -177,8 +174,8 @@ async fn exec_declaration_builtin_impl( #[allow(clippy::too_many_lines)] pub(crate) fn get_default_builtins( options: &crate::CreateOptions, -) -> HashMap { - let mut m = HashMap::::new(); +) -> HashMap { + let mut m = HashMap::::new(); // // POSIX special builtins @@ -247,7 +244,7 @@ pub(crate) fn get_default_builtins( m.insert("ulimit".into(), builtin::()); if !options.sh_mode { - m.insert("builtin".into(), builtin::()); + m.insert("builtin".into(), builtin::()); m.insert("declare".into(), decl_builtin::()); m.insert("echo".into(), builtin::()); m.insert("enable".into(), builtin::()); diff --git a/brush-core/src/builtins/popd.rs b/brush-core/src/builtins/popd.rs index 1641c806..f1f1294e 100644 --- a/brush-core/src/builtins/popd.rs +++ b/brush-core/src/builtins/popd.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Pop a path from the current directory stack. #[derive(Parser)] @@ -15,11 +15,11 @@ pub(crate) struct PopdCommand { } #[async_trait::async_trait] -impl BuiltinCommand for PopdCommand { +impl builtin::Command for PopdCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if let Some(popped) = context.shell.directory_stack.pop() { if !self.no_directory_change { context.shell.set_working_dir(&popped)?; @@ -30,9 +30,9 @@ impl BuiltinCommand for PopdCommand { dirs_cmd.execute(context).await?; } else { writeln!(context.stderr(), "popd: directory stack empty")?; - return Ok(BuiltinExitCode::Custom(1)); + return Ok(builtin::ExitCode::Custom(1)); } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/printf.rs b/brush-core/src/builtins/printf.rs index 2938f338..133796b7 100644 --- a/brush-core/src/builtins/printf.rs +++ b/brush-core/src/builtins/printf.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - expansion, -}; +use crate::{builtin, commands, expansion}; /// Format a string. #[derive(Parser)] @@ -23,11 +20,11 @@ pub(crate) struct PrintfCommand { } #[async_trait::async_trait] -impl BuiltinCommand for PrintfCommand { +impl builtin::Command for PrintfCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { // TODO: Don't call external printf command. let mut cmd = std::process::Command::new("printf"); cmd.env_clear(); @@ -44,7 +41,9 @@ impl BuiltinCommand for PrintfCommand { if !output.status.success() { #[allow(clippy::cast_possible_truncation)] #[allow(clippy::cast_sign_loss)] - return Ok(BuiltinExitCode::Custom(output.status.code().unwrap() as u8)); + return Ok(builtin::ExitCode::Custom( + output.status.code().unwrap() as u8 + )); } if let Some(variable_name) = &self.output_variable { @@ -54,6 +53,6 @@ impl BuiltinCommand for PrintfCommand { context.stdout().flush()?; } - return Ok(BuiltinExitCode::Success); + return Ok(builtin::ExitCode::Success); } } diff --git a/brush-core/src/builtins/pushd.rs b/brush-core/src/builtins/pushd.rs index a5248d2f..8cfc0874 100644 --- a/brush-core/src/builtins/pushd.rs +++ b/brush-core/src/builtins/pushd.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Push a path onto the current directory stack. #[derive(Parser)] @@ -17,11 +17,11 @@ pub(crate) struct PushdCommand { } #[async_trait::async_trait] -impl BuiltinCommand for PushdCommand { +impl builtin::Command for PushdCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.no_directory_change { context .shell @@ -40,6 +40,6 @@ impl BuiltinCommand for PushdCommand { let dirs_cmd = crate::builtins::dirs::DirsCommand::default(); dirs_cmd.execute(context).await?; - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/pwd.rs b/brush-core/src/builtins/pwd.rs index fbd3702e..c2dab9e9 100644 --- a/brush-core/src/builtins/pwd.rs +++ b/brush-core/src/builtins/pwd.rs @@ -1,4 +1,4 @@ -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; use clap::Parser; use std::io::Write; @@ -18,11 +18,11 @@ pub(crate) struct PwdCommand { } #[async_trait::async_trait] -impl BuiltinCommand for PwdCommand { +impl builtin::Command for PwdCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { // // TODO: implement flags // TODO: look for 'physical' option in execution context @@ -30,13 +30,13 @@ impl BuiltinCommand for PwdCommand { if self.physical || self.allow_symlinks { writeln!(context.stderr(), "UNIMPLEMENTED: pwd with -P or -L")?; - return Ok(BuiltinExitCode::Unimplemented); + return Ok(builtin::ExitCode::Unimplemented); } let cwd = context.shell.working_dir.to_string_lossy().into_owned(); writeln!(context.stdout(), "{cwd}")?; - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/read.rs b/brush-core/src/builtins/read.rs index 3f023a2a..6a504458 100644 --- a/brush-core/src/builtins/read.rs +++ b/brush-core/src/builtins/read.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::{collections::VecDeque, io::Read}; -use crate::{builtin::BuiltinCommand, env, error, openfiles, variables}; +use crate::{builtin, commands, env, error, openfiles, variables}; /// Parse standard input. #[derive(Parser)] @@ -43,11 +43,11 @@ pub(crate) struct ReadCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ReadCommand { +impl builtin::Command for ReadCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.array_variable.is_some() { return error::unimp("read -a"); } @@ -98,9 +98,9 @@ impl BuiltinCommand for ReadCommand { return error::unimp("too few variable names"); } } - Ok(crate::builtin::BuiltinExitCode::Success) + Ok(crate::builtin::ExitCode::Success) } else { - Ok(crate::builtin::BuiltinExitCode::Custom(1)) + Ok(crate::builtin::ExitCode::Custom(1)) } } } diff --git a/brush-core/src/builtins/return_.rs b/brush-core/src/builtins/return_.rs index 6b371150..570e93e9 100644 --- a/brush-core/src/builtins/return_.rs +++ b/brush-core/src/builtins/return_.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Return from the current function. #[derive(Parser)] @@ -10,11 +10,11 @@ pub(crate) struct ReturnCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ReturnCommand { +impl builtin::Command for ReturnCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let code_8bit: u8; #[allow(clippy::cast_sign_loss)] if let Some(code_32bit) = &self.code { @@ -24,6 +24,6 @@ impl BuiltinCommand for ReturnCommand { } // TODO: only allow return invocation from a function or script - Ok(BuiltinExitCode::ReturnFromFunctionOrScript(code_8bit)) + Ok(builtin::ExitCode::ReturnFromFunctionOrScript(code_8bit)) } } diff --git a/brush-core/src/builtins/set.rs b/brush-core/src/builtins/set.rs index 5526cbb8..b2047e55 100644 --- a/brush-core/src/builtins/set.rs +++ b/brush-core/src/builtins/set.rs @@ -2,8 +2,7 @@ use std::collections::HashMap; use clap::Parser; -use crate::builtin::{self, BuiltinCommand, BuiltinExitCode}; -use crate::{error, namedoptions}; +use crate::{builtin, commands, error, namedoptions}; builtin::minus_or_plus_flag_arg!( ExportVariablesOnModification, @@ -131,7 +130,7 @@ pub(crate) struct SetCommand { } #[async_trait::async_trait] -impl BuiltinCommand for SetCommand { +impl builtin::Command for SetCommand { fn takes_plus_options() -> bool { true } @@ -139,9 +138,9 @@ impl BuiltinCommand for SetCommand { #[allow(clippy::too_many_lines)] async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut result = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut result = builtin::ExitCode::Success; if let Some(value) = self.print_commands_and_arguments.to_bool() { context.shell.options.print_commands_and_arguments = value; @@ -253,7 +252,7 @@ impl BuiltinCommand for SetCommand { if let Some(option_def) = namedoptions::SET_O_OPTIONS.get(option_name.as_str()) { (option_def.setter)(context.shell, value); } else { - result = BuiltinExitCode::InvalidUsage; + result = builtin::ExitCode::InvalidUsage; } } diff --git a/brush-core/src/builtins/shift.rs b/brush-core/src/builtins/shift.rs index e61882bb..ccff26a1 100644 --- a/brush-core/src/builtins/shift.rs +++ b/brush-core/src/builtins/shift.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Shift positional arguments. #[derive(Parser)] @@ -10,26 +10,26 @@ pub(crate) struct ShiftCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ShiftCommand { +impl builtin::Command for ShiftCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let n = self.n.unwrap_or(1); if n < 0 { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } #[allow(clippy::cast_sign_loss)] let n = n as usize; if n > context.shell.positional_parameters.len() { - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } context.shell.positional_parameters.drain(0..n); - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/shopt.rs b/brush-core/src/builtins/shopt.rs index 880e9f6a..b238db89 100644 --- a/brush-core/src/builtins/shopt.rs +++ b/brush-core/src/builtins/shopt.rs @@ -2,7 +2,7 @@ use clap::Parser; use itertools::Itertools; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Manage shopt-style options. #[derive(Parser)] @@ -32,22 +32,22 @@ pub(crate) struct ShoptCommand { } #[async_trait::async_trait] -impl BuiltinCommand for ShoptCommand { +impl builtin::Command for ShoptCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.set && self.unset { writeln!( context.stderr(), "cannot set and unset shell options simultaneously" )?; - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } if self.options.is_empty() { if self.quiet { - return Ok(BuiltinExitCode::Success); + return Ok(builtin::ExitCode::Success); } // Enumerate all options of the selected type. @@ -84,9 +84,9 @@ impl BuiltinCommand for ShoptCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } else { - let mut return_value = BuiltinExitCode::Success; + let mut return_value = builtin::ExitCode::Success; // Enumerate only the specified options. for option_name in &self.options { @@ -104,7 +104,7 @@ impl BuiltinCommand for ShoptCommand { } else { let option_value = (option_definition.getter)(context.shell); if !option_value { - return_value = BuiltinExitCode::Custom(1); + return_value = builtin::ExitCode::Custom(1); } if !self.quiet { @@ -135,7 +135,7 @@ impl BuiltinCommand for ShoptCommand { context.command_name, option_name )?; - return_value = BuiltinExitCode::Custom(1); + return_value = builtin::ExitCode::Custom(1); } } diff --git a/brush-core/src/builtins/test.rs b/brush-core/src/builtins/test.rs index 5bab9cb4..33da0d0c 100644 --- a/brush-core/src/builtins/test.rs +++ b/brush-core/src/builtins/test.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - error, tests, Shell, -}; +use crate::{builtin, commands, error, tests, Shell}; /// Evaluate test expression. #[derive(Parser)] @@ -15,11 +12,11 @@ pub(crate) struct TestCommand { } #[async_trait::async_trait] -impl BuiltinCommand for TestCommand { +impl builtin::Command for TestCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { let mut args = self.args.as_slice(); if context.command_name == "[" { @@ -27,7 +24,7 @@ impl BuiltinCommand for TestCommand { Some(s) if s == "]" => (), None | Some(_) => { writeln!(context.stderr(), "[: missing ']'")?; - return Ok(BuiltinExitCode::InvalidUsage); + return Ok(builtin::ExitCode::InvalidUsage); } } @@ -35,15 +32,15 @@ impl BuiltinCommand for TestCommand { } if execute_test(context.shell, args)? { - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } else { - Ok(BuiltinExitCode::Custom(1)) + Ok(builtin::ExitCode::Custom(1)) } } } fn execute_test(shell: &mut Shell, args: &[String]) -> Result { let test_command = - brush_parser::parse_test_command(args).map_err(error::Error::TestCommandParseError)?; + brush_parser::test_command::parse(args).map_err(error::Error::TestCommandParseError)?; tests::eval_test_expr(&test_command, shell) } diff --git a/brush-core/src/builtins/trap.rs b/brush-core/src/builtins/trap.rs index db2a5817..86d3f1af 100644 --- a/brush-core/src/builtins/trap.rs +++ b/brush-core/src/builtins/trap.rs @@ -1,10 +1,7 @@ use clap::Parser; use std::{io::Write, str::FromStr}; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - error, traps, -}; +use crate::{builtin, commands, error, traps}; /// Manage signal traps. #[derive(Parser)] @@ -21,14 +18,14 @@ pub(crate) struct TrapCommand { } #[async_trait::async_trait] -impl BuiltinCommand for TrapCommand { +impl builtin::Command for TrapCommand { async fn execute( &self, - mut context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + mut context: commands::ExecutionContext<'_>, + ) -> Result { if self.list_signals { Self::display_signals(&context)?; - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } else if self.print_trap_commands || self.args.is_empty() { if !self.args.is_empty() { for signal_type in &self.args { @@ -38,12 +35,12 @@ impl BuiltinCommand for TrapCommand { } else { Self::display_all_handlers(&context)?; } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } else if self.args.len() == 1 { let signal = self.args[0].as_str(); let signal_type = parse_signal(signal)?; Self::remove_all_handlers(&mut context, signal_type); - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } else { let handler = &self.args[0]; @@ -53,15 +50,13 @@ impl BuiltinCommand for TrapCommand { } Self::register_handler(&mut context, signal_types, handler.as_str()); - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } } impl TrapCommand { - fn display_signals( - context: &crate::context::CommandExecutionContext<'_>, - ) -> Result<(), error::Error> { + fn display_signals(context: &commands::ExecutionContext<'_>) -> Result<(), error::Error> { for signal in nix::sys::signal::Signal::iterator() { writeln!(context.stdout(), "{}: {signal}", signal as i32)?; } @@ -69,9 +64,7 @@ impl TrapCommand { Ok(()) } - fn display_all_handlers( - context: &crate::context::CommandExecutionContext<'_>, - ) -> Result<(), error::Error> { + fn display_all_handlers(context: &commands::ExecutionContext<'_>) -> Result<(), error::Error> { for signal in context.shell.traps.handlers.keys() { Self::display_handlers_for(context, *signal)?; } @@ -79,7 +72,7 @@ impl TrapCommand { } fn display_handlers_for( - context: &crate::context::CommandExecutionContext<'_>, + context: &commands::ExecutionContext<'_>, signal_type: traps::TrapSignal, ) -> Result<(), error::Error> { if let Some(handler) = context.shell.traps.handlers.get(&signal_type) { @@ -89,14 +82,14 @@ impl TrapCommand { } fn remove_all_handlers( - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, signal: traps::TrapSignal, ) { context.shell.traps.remove_handlers(signal); } fn register_handler( - context: &mut crate::context::CommandExecutionContext<'_>, + context: &mut crate::commands::ExecutionContext<'_>, signals: Vec, handler: &str, ) { diff --git a/brush-core/src/builtins/true_.rs b/brush-core/src/builtins/true_.rs index fddd21bc..0fe73b8c 100644 --- a/brush-core/src/builtins/true_.rs +++ b/brush-core/src/builtins/true_.rs @@ -1,17 +1,17 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Return 0. #[derive(Parser)] pub(crate) struct TrueCommand {} #[async_trait::async_trait] -impl BuiltinCommand for TrueCommand { +impl builtin::Command for TrueCommand { async fn execute( &self, - _context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - Ok(BuiltinExitCode::Success) + _context: commands::ExecutionContext<'_>, + ) -> Result { + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/type_.rs b/brush-core/src/builtins/type_.rs index 9952b5e3..9d7c99e9 100644 --- a/brush-core/src/builtins/type_.rs +++ b/brush-core/src/builtins/type_.rs @@ -6,10 +6,7 @@ use clap::Parser; use crate::files::PathExt; use crate::keywords; -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - Shell, -}; +use crate::{builtin, commands, Shell}; /// Inspect the type of a named shell item. #[derive(Parser)] @@ -41,12 +38,12 @@ enum ResolvedType { } #[async_trait::async_trait] -impl BuiltinCommand for TypeCommand { +impl builtin::Command for TypeCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut result = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut result = builtin::ExitCode::Success; for name in &self.names { let resolved_types = self.resolve_types(context.shell, name); @@ -56,7 +53,7 @@ impl BuiltinCommand for TypeCommand { writeln!(context.stderr(), "type: {name} not found")?; } - result = BuiltinExitCode::Custom(1); + result = builtin::ExitCode::Custom(1); continue; } diff --git a/brush-core/src/builtins/umask.rs b/brush-core/src/builtins/umask.rs index d060b8b7..3301def6 100644 --- a/brush-core/src/builtins/umask.rs +++ b/brush-core/src/builtins/umask.rs @@ -1,7 +1,4 @@ -use crate::{ - builtin::{BuiltinCommand, BuiltinExitCode}, - error, -}; +use crate::{builtin, commands, error}; use clap::Parser; use std::io::Write; @@ -25,11 +22,11 @@ pub(crate) struct UmaskCommand { } #[async_trait::async_trait] -impl BuiltinCommand for UmaskCommand { +impl builtin::Command for UmaskCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if let Some(mode) = &self.mode { if mode.starts_with(|c: char| c.is_digit(8)) { let parsed = u32::from_str_radix(mode.as_str(), 8)?; @@ -56,7 +53,7 @@ impl BuiltinCommand for UmaskCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/unalias.rs b/brush-core/src/builtins/unalias.rs index def30b8c..6f897212 100644 --- a/brush-core/src/builtins/unalias.rs +++ b/brush-core/src/builtins/unalias.rs @@ -1,7 +1,7 @@ use clap::Parser; use std::io::Write; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Unset a shell alias. #[derive(Parser)] @@ -15,12 +15,12 @@ pub(crate) struct UnaliasCommand { } #[async_trait::async_trait] -impl BuiltinCommand for UnaliasCommand { +impl builtin::Command for UnaliasCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { - let mut exit_code = BuiltinExitCode::Success; + context: commands::ExecutionContext<'_>, + ) -> Result { + let mut exit_code = builtin::ExitCode::Success; if self.remove_all { context.shell.aliases.clear(); @@ -33,7 +33,7 @@ impl BuiltinCommand for UnaliasCommand { context.command_name, alias )?; - exit_code = BuiltinExitCode::Custom(1); + exit_code = builtin::ExitCode::Custom(1); } } } diff --git a/brush-core/src/builtins/unimp.rs b/brush-core/src/builtins/unimp.rs index e75c78a4..46e7431a 100644 --- a/brush-core/src/builtins/unimp.rs +++ b/brush-core/src/builtins/unimp.rs @@ -1,7 +1,4 @@ -use crate::{ - builtin::{BuiltinCommand, BuiltinDeclarationCommand, BuiltinExitCode}, - commands, -}; +use crate::{builtin, commands}; use std::io::Write; use clap::Parser; @@ -17,11 +14,11 @@ pub(crate) struct UnimplementedCommand { } #[async_trait::async_trait] -impl BuiltinCommand for UnimplementedCommand { +impl builtin::Command for UnimplementedCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { writeln!( context.stderr(), "UNIMPLEMENTED: {}: built-in unimplemented: {} {}", @@ -33,11 +30,11 @@ impl BuiltinCommand for UnimplementedCommand { context.command_name, self.args.join(" ") )?; - Ok(BuiltinExitCode::Unimplemented) + Ok(builtin::ExitCode::Unimplemented) } } -impl BuiltinDeclarationCommand for UnimplementedCommand { +impl builtin::DeclarationCommand for UnimplementedCommand { fn set_declarations(&mut self, declarations: Vec) { self.declarations = declarations; } diff --git a/brush-core/src/builtins/unset.rs b/brush-core/src/builtins/unset.rs index ff445683..579bf8b0 100644 --- a/brush-core/src/builtins/unset.rs +++ b/brush-core/src/builtins/unset.rs @@ -1,6 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; +use crate::{builtin, commands}; /// Unset a variable. #[derive(Parser)] @@ -44,11 +44,11 @@ impl UnsetNameInterpretation { } #[async_trait::async_trait] -impl BuiltinCommand for UnsetCommand { +impl builtin::Command for UnsetCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { // // TODO: implement nameref // @@ -95,6 +95,6 @@ impl BuiltinCommand for UnsetCommand { } } - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/builtins/wait.rs b/brush-core/src/builtins/wait.rs index bbb8da63..742b8f3f 100644 --- a/brush-core/src/builtins/wait.rs +++ b/brush-core/src/builtins/wait.rs @@ -1,7 +1,6 @@ use clap::Parser; -use crate::builtin::{BuiltinCommand, BuiltinExitCode}; -use crate::error; +use crate::{builtin, commands, error}; /// Wait for jobs to terminate. #[derive(Parser)] @@ -19,11 +18,11 @@ pub(crate) struct WaitCommand { } #[async_trait::async_trait] -impl BuiltinCommand for WaitCommand { +impl builtin::Command for WaitCommand { async fn execute( &self, - context: crate::context::CommandExecutionContext<'_>, - ) -> Result { + context: commands::ExecutionContext<'_>, + ) -> Result { if self.wait_for_terminate { return error::unimp("wait -f"); } @@ -39,6 +38,6 @@ impl BuiltinCommand for WaitCommand { context.shell.jobs.wait_all().await?; - Ok(BuiltinExitCode::Success) + Ok(builtin::ExitCode::Success) } } diff --git a/brush-core/src/commands.rs b/brush-core/src/commands.rs index 9eb065fd..e6ebf4bb 100644 --- a/brush-core/src/commands.rs +++ b/brush-core/src/commands.rs @@ -9,6 +9,33 @@ use crate::{ Shell, }; +/// Represents the context for executing a command. +pub struct ExecutionContext<'a> { + /// The shell in which the command is being executed. + pub shell: &'a mut Shell, + /// The name of the command being executed. + pub command_name: String, + /// The open files tracked by the current context. + pub open_files: crate::openfiles::OpenFiles, +} + +impl ExecutionContext<'_> { + /// Returns the standard input file; usable with `write!` et al. + pub fn stdin(&self) -> crate::openfiles::OpenFile { + self.open_files.files.get(&0).unwrap().try_dup().unwrap() + } + + /// Returns the standard output file; usable with `write!` et al. + pub fn stdout(&self) -> crate::openfiles::OpenFile { + self.open_files.files.get(&1).unwrap().try_dup().unwrap() + } + + /// Returns the standard error file; usable with `write!` et al. + pub fn stderr(&self) -> crate::openfiles::OpenFile { + self.open_files.files.get(&2).unwrap().try_dup().unwrap() + } +} + /// An argument to a command. #[derive(Clone, Debug)] pub enum CommandArg { diff --git a/brush-core/src/completion.rs b/brush-core/src/completion.rs index 6db1d777..77c326ff 100644 --- a/brush-core/src/completion.rs +++ b/brush-core/src/completion.rs @@ -1,3 +1,5 @@ +//! Implements programmable command completion support. + use clap::ValueEnum; use std::{ collections::{HashMap, HashSet}, @@ -8,6 +10,7 @@ use crate::{ env, error, jobs, namedoptions, patterns, traps, users, variables::ShellValueLiteral, Shell, }; +/// Type of action to take to generate completion candidates. #[derive(Clone, Debug, ValueEnum)] pub enum CompleteAction { /// Complete with valid aliases. @@ -84,6 +87,7 @@ pub enum CompleteAction { Variable, } +/// Options influencing how command completions are generated. #[derive(Clone, Debug, Eq, Hash, PartialEq, ValueEnum)] pub enum CompleteOption { /// Perform rest of default completions if no completions are generated. @@ -107,33 +111,32 @@ pub enum CompleteOption { /// Do not append a trailing space to completions at the end of the input line. #[clap(name = "nospace")] NoSpace, + /// Also generate directory completions. #[clap(name = "plusdirs")] PlusDirs, } /// Encapsulates the shell's programmable command completion configuration. -#[allow(clippy::module_name_repetitions)] #[derive(Clone, Default)] -pub struct CompletionConfig { - commands: HashMap, +pub struct Config { + commands: HashMap, /// Optionally, a completion spec to be used as a default, when earlier /// matches yield no candidates. - pub default: Option, + pub default: Option, /// Optionally, a completion spec to be used when the command line is empty. - pub empty_line: Option, + pub empty_line: Option, /// Optionally, a completion spec to be used for the initial word of a command line. - pub initial_word: Option, + pub initial_word: Option, /// Optionally, stores the current completion options in effect. May be mutated /// while a completion generation is in-flight. - pub current_completion_options: Option, + pub current_completion_options: Option, } /// Options for generating completions. -#[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, Default)] -pub struct CompletionOptions { +pub struct GenerationOptions { // // Options // @@ -157,14 +160,13 @@ pub struct CompletionOptions { /// Encapsulates a command completion specification; provides policy for how to /// generate completions for a given input. -#[allow(clippy::module_name_repetitions)] #[derive(Clone, Debug, Default)] -pub struct CompletionSpec { +pub struct Spec { // // Options // /// Options to use for completion. - pub options: CompletionOptions, + pub options: GenerationOptions, // // Generators @@ -199,9 +201,8 @@ pub struct CompletionSpec { } /// Encapsulates context used during completion generation. -#[allow(clippy::module_name_repetitions)] #[derive(Debug)] -pub struct CompletionContext<'a> { +pub struct Context<'a> { /// The token to complete. pub token_to_complete: &'a str, @@ -221,7 +222,7 @@ pub struct CompletionContext<'a> { pub tokens: &'a [&'a brush_parser::Token], } -impl CompletionSpec { +impl Spec { /// Generates completion candidates using this specification. /// /// # Arguments @@ -232,8 +233,8 @@ impl CompletionSpec { pub async fn get_completions( &self, shell: &mut Shell, - context: &CompletionContext<'_>, - ) -> Result { + context: &Context<'_>, + ) -> Result { let mut candidates: Vec = vec![]; // Store the current options in the shell; this is needed since the compopt @@ -383,8 +384,8 @@ impl CompletionSpec { .call_completion_function(shell, function_name.as_str(), context) .await?; match call_result { - CompletionResult::RestartCompletionProcess => return Ok(call_result), - CompletionResult::Candidates(mut new_candidates, _options) => { + Answer::RestartCompletionProcess => return Ok(call_result), + Answer::Candidates(mut new_candidates, _options) => { candidates.append(&mut new_candidates); } } @@ -427,7 +428,7 @@ impl CompletionSpec { &self.options }; - let processing_options = CandidateProcessingOptions { + let processing_options = ProcessingOptions { treat_as_filenames: options.file_names, no_autoquote_filenames: options.no_quote, no_trailing_space_at_end_of_line: options.no_space, @@ -457,15 +458,15 @@ impl CompletionSpec { candidates.sort(); } - Ok(CompletionResult::Candidates(candidates, processing_options)) + Ok(Answer::Candidates(candidates, processing_options)) } async fn call_completion_function( &self, shell: &mut Shell, function_name: &str, - context: &CompletionContext<'_>, - ) -> Result { + context: &Context<'_>, + ) -> Result { // TODO: Don't pollute the persistent environment with these? let vars_and_values: Vec<(&str, ShellValueLiteral)> = vec![ ("COMP_LINE", context.input_line.into()), @@ -509,23 +510,18 @@ impl CompletionSpec { // When the function returns the special value 124, then it's a request // for us to restart the completion process. if result == 124 { - Ok(CompletionResult::RestartCompletionProcess) + Ok(Answer::RestartCompletionProcess) } else { if let Some((_, reply)) = shell.env.get("COMPREPLY") { match reply.value() { - crate::variables::ShellValue::IndexedArray(values) => { - Ok(CompletionResult::Candidates( - values.values().map(|v| v.to_owned()).collect(), - CandidateProcessingOptions::default(), - )) - } + crate::variables::ShellValue::IndexedArray(values) => Ok(Answer::Candidates( + values.values().map(|v| v.to_owned()).collect(), + ProcessingOptions::default(), + )), _ => error::unimp("unexpected COMPREPLY value type"), } } else { - Ok(CompletionResult::Candidates( - vec![], - CandidateProcessingOptions::default(), - )) + Ok(Answer::Candidates(vec![], ProcessingOptions::default())) } } } @@ -539,12 +535,12 @@ pub struct Completions { /// The ordered list of completions. pub candidates: Vec, /// Options for processing the candidates. - pub options: CandidateProcessingOptions, + pub options: ProcessingOptions, } -/// Options governing how command completion candidates are processed. +/// Options governing how command completion candidates are processed after being generated. #[derive(Debug)] -pub struct CandidateProcessingOptions { +pub struct ProcessingOptions { /// Treat completions as file names. pub treat_as_filenames: bool, /// Don't auto-quote completions that are file names. @@ -553,7 +549,7 @@ pub struct CandidateProcessingOptions { pub no_trailing_space_at_end_of_line: bool, } -impl Default for CandidateProcessingOptions { +impl Default for ProcessingOptions { fn default() -> Self { Self { treat_as_filenames: true, @@ -563,12 +559,11 @@ impl Default for CandidateProcessingOptions { } } -/// Encapsulates a completion result. -#[allow(clippy::module_name_repetitions)] -pub enum CompletionResult { +/// Encapsulates a completion answer. +pub enum Answer { /// The completion process generated a set of candidates along with options /// controlling how to process them. - Candidates(Vec, CandidateProcessingOptions), + Candidates(Vec, ProcessingOptions), /// The completion process needs to be restarted. RestartCompletionProcess, } @@ -577,7 +572,7 @@ const EMPTY_COMMAND: &str = "_EmptycmD_"; const DEFAULT_COMMAND: &str = "_DefaultCmD_"; const INITIAL_WORD: &str = "_InitialWorD_"; -impl CompletionConfig { +impl Config { /// Removes a completion spec by name. /// /// # Arguments @@ -601,7 +596,7 @@ impl CompletionConfig { } /// Returns an iterator over the completion specs. - pub fn iter(&self) -> impl Iterator { + pub fn iter(&self) -> impl Iterator { self.commands.iter() } @@ -610,7 +605,7 @@ impl CompletionConfig { /// # Arguments /// /// * `name` - The name of the command. - pub fn get(&self, name: &str) -> Option<&CompletionSpec> { + pub fn get(&self, name: &str) -> Option<&Spec> { match name { EMPTY_COMMAND => self.empty_line.as_ref(), DEFAULT_COMMAND => self.default.as_ref(), @@ -626,7 +621,7 @@ impl CompletionConfig { /// /// * `name` - The name of the command. /// * `spec` - The completion spec to associate with the command. - pub fn set(&mut self, name: &str, spec: CompletionSpec) { + pub fn set(&mut self, name: &str, spec: Spec) { match name { EMPTY_COMMAND => { self.empty_line = Some(spec); @@ -651,23 +646,23 @@ impl CompletionConfig { /// # Arguments /// /// * `name` - The name of the command. - pub fn get_or_add_mut(&mut self, name: &str) -> &mut CompletionSpec { + pub fn get_or_add_mut(&mut self, name: &str) -> &mut Spec { match name { EMPTY_COMMAND => { if self.empty_line.is_none() { - self.empty_line = Some(CompletionSpec::default()); + self.empty_line = Some(Spec::default()); } self.empty_line.as_mut().unwrap() } DEFAULT_COMMAND => { if self.default.is_none() { - self.default = Some(CompletionSpec::default()); + self.default = Some(Spec::default()); } self.default.as_mut().unwrap() } INITIAL_WORD => { if self.initial_word.is_none() { - self.initial_word = Some(CompletionSpec::default()); + self.initial_word = Some(Spec::default()); } self.initial_word.as_mut().unwrap() } @@ -748,15 +743,15 @@ impl CompletionConfig { adjusted_tokens.push(&empty_token); // Get the completions. - let mut result = CompletionResult::RestartCompletionProcess; + let mut result = Answer::RestartCompletionProcess; let mut restart_count = 0; - while matches!(result, CompletionResult::RestartCompletionProcess) { + while matches!(result, Answer::RestartCompletionProcess) { if restart_count > MAX_RESTARTS { tracing::error!("possible infinite loop detected in completion process"); break; } - let completion_context = CompletionContext { + let completion_context = Context { token_to_complete: completion_prefix, preceding_token: preceding_token.map(|t| t.to_str()), command_name: adjusted_tokens.first().map(|token| token.to_str()), @@ -774,22 +769,22 @@ impl CompletionConfig { } match result { - CompletionResult::Candidates(candidates, options) => Ok(Completions { + Answer::Candidates(candidates, options) => Ok(Completions { start: insertion_point as usize, candidates, options, }), - CompletionResult::RestartCompletionProcess => Ok(Completions { + Answer::RestartCompletionProcess => Ok(Completions { start: insertion_point as usize, candidates: vec![], - options: CandidateProcessingOptions::default(), + options: ProcessingOptions::default(), }), } } else { Ok(Completions { start: position, candidates: vec![], - options: CandidateProcessingOptions::default(), + options: ProcessingOptions::default(), }) } } @@ -797,8 +792,8 @@ impl CompletionConfig { async fn get_completions_for_token<'a>( &self, shell: &mut Shell, - mut context: CompletionContext<'a>, - ) -> CompletionResult { + mut context: Context<'a>, + ) -> Answer { // N.B. We basic-expand the token-to-be-completed first. let mut throwaway_shell = shell.clone(); let expanded_token_to_complete = throwaway_shell @@ -808,7 +803,7 @@ impl CompletionConfig { context.token_to_complete = expanded_token_to_complete.as_str(); // See if we can find a completion spec matching the current command. - let mut found_spec: Option<&CompletionSpec> = None; + let mut found_spec: Option<&Spec> = None; if let Some(command_name) = context.command_name { if context.token_index == 0 { @@ -846,12 +841,9 @@ impl CompletionConfig { .to_owned() .get_completions(shell, &context) .await - .unwrap_or_else(|_err| { - CompletionResult::Candidates(vec![], CandidateProcessingOptions::default()) - }); + .unwrap_or_else(|_err| Answer::Candidates(vec![], ProcessingOptions::default())); - if !matches!(&result, CompletionResult::Candidates(candidates, _) if candidates.is_empty()) - { + if !matches!(&result, Answer::Candidates(candidates, _) if candidates.is_empty()) { return result; } } @@ -860,11 +852,7 @@ impl CompletionConfig { } } -fn get_file_completions( - shell: &Shell, - context: &CompletionContext, - must_be_dir: bool, -) -> Vec { +fn get_file_completions(shell: &Shell, context: &Context, must_be_dir: bool) -> Vec { let glob = std::format!("{}*", context.token_to_complete); let path_filter = |path: &Path| !must_be_dir || path.is_dir(); @@ -879,7 +867,7 @@ fn get_file_completions( .unwrap_or_default() } -fn get_command_completions(shell: &Shell, context: &CompletionContext) -> Vec { +fn get_command_completions(shell: &Shell, context: &Context) -> Vec { let mut candidates = HashSet::new(); let glob_pattern = std::format!("{}*", context.token_to_complete); @@ -893,10 +881,7 @@ fn get_command_completions(shell: &Shell, context: &CompletionContext) -> Vec CompletionResult { +fn get_completions_using_basic_lookup(shell: &Shell, context: &Context) -> Answer { let mut candidates = get_file_completions(shell, context, false); // If this appears to be the command token (and if there's *some* prefix without @@ -953,7 +938,7 @@ fn get_completions_using_basic_lookup( .collect(); } - CompletionResult::Candidates(candidates, CandidateProcessingOptions::default()) + Answer::Candidates(candidates, ProcessingOptions::default()) } fn split_string_using_ifs>(s: S, shell: &Shell) -> Vec { diff --git a/brush-core/src/context.rs b/brush-core/src/context.rs deleted file mode 100644 index bc9f87cc..00000000 --- a/brush-core/src/context.rs +++ /dev/null @@ -1,29 +0,0 @@ -use crate::Shell; - -/// Represents the context for executing a command. -#[allow(clippy::module_name_repetitions)] -pub struct CommandExecutionContext<'a> { - /// The shell in which the command is being executed. - pub shell: &'a mut Shell, - /// The name of the command being executed. - pub command_name: String, - /// The open files tracked by the current context. - pub open_files: crate::openfiles::OpenFiles, -} - -impl CommandExecutionContext<'_> { - /// Returns the standard input file; usable with `write!` et al. - pub fn stdin(&self) -> crate::openfiles::OpenFile { - self.open_files.files.get(&0).unwrap().try_dup().unwrap() - } - - /// Returns the standard output file; usable with `write!` et al. - pub fn stdout(&self) -> crate::openfiles::OpenFile { - self.open_files.files.get(&1).unwrap().try_dup().unwrap() - } - - /// Returns the standard error file; usable with `write!` et al. - pub fn stderr(&self) -> crate::openfiles::OpenFile { - self.open_files.files.get(&2).unwrap().try_dup().unwrap() - } -} diff --git a/brush-core/src/expansion.rs b/brush-core/src/expansion.rs index 9aa21d58..8e60c45b 100644 --- a/brush-core/src/expansion.rs +++ b/brush-core/src/expansion.rs @@ -414,7 +414,7 @@ impl<'a> WordExpander<'a> { // // Expand: tildes, parameters, command substitutions, arithmetic. // - let pieces = brush_parser::parse_word_for_expansion(word, &self.parser_options)?; + let pieces = brush_parser::word::parse(word, &self.parser_options)?; let mut expansions = vec![]; for piece in pieces { diff --git a/brush-core/src/interp.rs b/brush-core/src/interp.rs index 2ae62a97..91d0c4d0 100644 --- a/brush-core/src/interp.rs +++ b/brush-core/src/interp.rs @@ -16,7 +16,7 @@ use crate::shell::Shell; use crate::variables::{ ArrayLiteral, ShellValue, ShellValueLiteral, ShellValueUnsetType, ShellVariable, }; -use crate::{builtin, context, error, expansion, extendedtests, jobs, openfiles, traps}; +use crate::{builtin, error, expansion, extendedtests, jobs, openfiles, traps}; /// Encapsulates the result of executing a command. #[derive(Debug, Default)] @@ -996,7 +996,7 @@ impl ExecuteInPipeline for ast::SimpleCommand { } } - let cmd_context = context::CommandExecutionContext { + let cmd_context = commands::ExecutionContext { shell: context.shell, command_name: cmd_name, open_files, @@ -1265,7 +1265,7 @@ async fn apply_assignment( #[allow(clippy::too_many_lines)] async fn execute_external_command( - context: context::CommandExecutionContext<'_>, + context: commands::ExecutionContext<'_>, args: &[CommandArg], ) -> Result { // Filter out the args; we only want strings. @@ -1331,24 +1331,22 @@ async fn execute_external_command( } async fn execute_builtin_command( - builtin: &builtin::BuiltinRegistration, - context: context::CommandExecutionContext<'_>, + builtin: &builtin::Registration, + context: commands::ExecutionContext<'_>, args: Vec, ) -> Result { let exit_code = match (builtin.execute_func)(context, args).await { Ok(builtin_result) => match builtin_result.exit_code { - builtin::BuiltinExitCode::Success => 0, - builtin::BuiltinExitCode::InvalidUsage => 2, - builtin::BuiltinExitCode::Unimplemented => 99, - builtin::BuiltinExitCode::Custom(code) => code, - builtin::BuiltinExitCode::ExitShell(code) => return Ok(SpawnResult::ExitShell(code)), - builtin::BuiltinExitCode::ReturnFromFunctionOrScript(code) => { + builtin::ExitCode::Success => 0, + builtin::ExitCode::InvalidUsage => 2, + builtin::ExitCode::Unimplemented => 99, + builtin::ExitCode::Custom(code) => code, + builtin::ExitCode::ExitShell(code) => return Ok(SpawnResult::ExitShell(code)), + builtin::ExitCode::ReturnFromFunctionOrScript(code) => { return Ok(SpawnResult::ReturnFromFunctionOrScript(code)) } - builtin::BuiltinExitCode::BreakLoop(count) => return Ok(SpawnResult::BreakLoop(count)), - builtin::BuiltinExitCode::ContinueLoop(count) => { - return Ok(SpawnResult::ContinueLoop(count)) - } + builtin::ExitCode::BreakLoop(count) => return Ok(SpawnResult::BreakLoop(count)), + builtin::ExitCode::ContinueLoop(count) => return Ok(SpawnResult::ContinueLoop(count)), }, Err(e) => { tracing::error!("error: {}", e); @@ -1361,7 +1359,7 @@ async fn execute_builtin_command( pub(crate) async fn invoke_shell_function( function_definition: Arc, - mut context: context::CommandExecutionContext<'_>, + mut context: commands::ExecutionContext<'_>, args: &[CommandArg], ) -> Result { let ast::FunctionBody(body, redirects) = &function_definition.body; diff --git a/brush-core/src/lib.rs b/brush-core/src/lib.rs index 61327c2c..73a76d04 100644 --- a/brush-core/src/lib.rs +++ b/brush-core/src/lib.rs @@ -1,11 +1,12 @@ -//! Core implementation of the brush shell +//! Core implementation of the brush shell. Implements the shell's abstraction, its interpreter, and +//! various facilities used internally by the shell. + +pub mod completion; mod arithmetic; mod builtin; mod builtins; mod commands; -mod completion; -mod context; mod env; mod error; mod escape; @@ -28,7 +29,6 @@ mod traps; mod users; mod variables; -pub use completion::{CandidateProcessingOptions, Completions}; pub use error::Error; pub use interp::ExecutionResult; pub use shell::{CreateOptions, Shell}; diff --git a/brush-core/src/prompt.rs b/brush-core/src/prompt.rs index 1b74ae58..4244aa27 100644 --- a/brush-core/src/prompt.rs +++ b/brush-core/src/prompt.rs @@ -23,7 +23,7 @@ pub(crate) fn expand_prompt(shell: &Shell, spec: &str) -> Result Result, brush_parser::WordParseError> { - brush_parser::prompt::parse_prompt(spec.as_str()) + brush_parser::prompt::parse(spec.as_str()) } pub(crate) fn format_prompt_piece( diff --git a/brush-core/src/shell.rs b/brush-core/src/shell.rs index ff869f4e..cd7d717e 100644 --- a/brush-core/src/shell.rs +++ b/brush-core/src/shell.rs @@ -11,8 +11,8 @@ use crate::interp::{self, Execute, ExecutionParameters, ExecutionResult}; use crate::options::RuntimeOptions; use crate::variables::{self, ShellValue, ShellVariable}; use crate::{ - builtin, builtins, commands, completion, context, env, error, expansion, functions, jobs, - keywords, openfiles, patterns, prompt, traps, users, + builtin, builtins, commands, completion, env, error, expansion, functions, jobs, keywords, + openfiles, patterns, prompt, traps, users, }; /// Represents an instance of a shell. @@ -65,10 +65,10 @@ pub struct Shell { pub current_line_number: u32, /// Completion configuration. - pub completion_config: completion::CompletionConfig, + pub completion_config: completion::Config, /// Shell built-in commands. - pub builtins: HashMap, + pub builtins: HashMap, } impl Clone for Shell { @@ -156,7 +156,7 @@ impl Shell { script_call_stack: VecDeque::new(), directory_stack: vec![], current_line_number: 0, - completion_config: completion::CompletionConfig::default(), + completion_config: completion::Config::default(), builtins: builtins::get_default_builtins(options), depth: 0, }; @@ -447,7 +447,7 @@ impl Shell { let func = func_registration.definition.clone(); - let context = context::CommandExecutionContext { + let context = commands::ExecutionContext { shell: self, command_name, open_files, diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index dd577088..85ba0601 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -25,9 +25,8 @@ pub struct InteractiveShell { } /// Represents an error encountered while running or otherwise managing an interactive shell. -#[allow(clippy::module_name_repetitions)] #[derive(thiserror::Error, Debug)] -pub enum InteractiveShellError { +pub enum ShellError { /// An error occurred with the embedded shell. #[error("{0}")] ShellError(#[from] brush_core::Error), @@ -53,7 +52,7 @@ impl InteractiveShell { /// # Arguments /// /// * `options` - Options for creating the interactive shell. - pub async fn new(options: &Options) -> Result { + pub async fn new(options: &Options) -> Result { // Set up shell first. Its initialization may influence how the // editor needs to operate. let shell = brush_core::Shell::new(&options.shell).await?; @@ -84,10 +83,7 @@ impl InteractiveShell { &mut self.editor.helper_mut().unwrap().shell } - fn new_editor( - options: &Options, - shell: brush_core::Shell, - ) -> Result { + fn new_editor(options: &Options, shell: brush_core::Shell) -> Result { let config = rustyline::config::Builder::new() .max_history_size(1000)? .history_ignore_dups(true)? @@ -106,7 +102,7 @@ impl InteractiveShell { /// Runs the interactive shell loop, reading commands from standard input and writing /// results to standard output and standard error. Continues until the shell /// normally exits or until a fatal error occurs. - pub async fn run_interactively(&mut self) -> Result<(), InteractiveShellError> { + pub async fn run_interactively(&mut self) -> Result<(), ShellError> { loop { // Check for any completed jobs. self.shell_mut().check_for_completed_jobs()?; @@ -148,9 +144,7 @@ impl InteractiveShell { Ok(()) } - async fn run_interactively_once( - &mut self, - ) -> Result { + async fn run_interactively_once(&mut self) -> Result { // If there's a variable called PROMPT_COMMAND, then run it first. if let Some((_, prompt_cmd)) = self.shell().env.get("PROMPT_COMMAND") { let prompt_cmd = prompt_cmd.value().to_cow_string().to_string(); @@ -256,10 +250,10 @@ impl EditorHelper { } }; - let mut completions = result.unwrap_or_else(|_| brush_core::Completions { + let mut completions = result.unwrap_or_else(|_| brush_core::completion::Completions { start: pos, candidates: vec![], - options: brush_core::CandidateProcessingOptions::default(), + options: brush_core::completion::ProcessingOptions::default(), }); // TODO: implement completion postprocessing diff --git a/brush-interactive/src/lib.rs b/brush-interactive/src/lib.rs index 45818a71..e1fdc81d 100644 --- a/brush-interactive/src/lib.rs +++ b/brush-interactive/src/lib.rs @@ -2,4 +2,4 @@ mod interactive_shell; -pub use interactive_shell::{InteractiveShell, InteractiveShellError, Options}; +pub use interactive_shell::{InteractiveShell, Options, ShellError}; diff --git a/brush-parser/src/arithmetic.rs b/brush-parser/src/arithmetic.rs index 2f430571..ce3cdf70 100644 --- a/brush-parser/src/arithmetic.rs +++ b/brush-parser/src/arithmetic.rs @@ -1,9 +1,14 @@ +//! Parser for shell arithmetic expressions. + use crate::ast; use crate::error; -pub fn parse_arithmetic_expression( - input: &str, -) -> Result { +/// Parses a shell arithmetic expression. +/// +/// # Arguments +/// +/// * `input` - The arithmetic expression to parse, in string form. +pub fn parse(input: &str) -> Result { tracing::debug!("parsing arithmetic expression: '{input}'"); // Special-case the empty string. diff --git a/brush-parser/src/ast.rs b/brush-parser/src/ast.rs index 4104db71..163efaf7 100644 --- a/brush-parser/src/ast.rs +++ b/brush-parser/src/ast.rs @@ -1,3 +1,6 @@ +//! Defines the Abstract Syntax Tree (ast) for shell programs. Includes types and utilities +//! for manipulating the AST. + use std::fmt::{Display, Write}; use crate::tokenizer; diff --git a/brush-parser/src/lib.rs b/brush-parser/src/lib.rs index 7bee604d..c26aacfc 100644 --- a/brush-parser/src/lib.rs +++ b/brush-parser/src/lib.rs @@ -1,16 +1,16 @@ -mod arithmetic; +//! Implements a tokenizer and parsers for POSIX / bash shell syntax. + +pub mod arithmetic; pub mod ast; -mod error; -mod parser; pub mod pattern; pub mod prompt; -mod test_command; -mod tokenizer; +pub mod test_command; pub mod word; -pub use arithmetic::parse_arithmetic_expression; +mod error; +mod parser; +mod tokenizer; + pub use error::{ParseError, TestCommandParseError, WordParseError}; pub use parser::{parse_tokens, Parser, ParserOptions, SourceInfo}; -pub use test_command::parse_test_command; pub use tokenizer::{tokenize_str, Token, TokenLocation}; -pub use word::parse_word_for_expansion; diff --git a/brush-parser/src/pattern.rs b/brush-parser/src/pattern.rs index 26e2eaa1..eec00f24 100644 --- a/brush-parser/src/pattern.rs +++ b/brush-parser/src/pattern.rs @@ -1,3 +1,5 @@ +//! Implements parsing for shell glob and extglob patterns. + use crate::error; /// Represents the kind of an extended glob. @@ -132,6 +134,11 @@ peg::parser! { } } +/// Returns whether or not a given character needs to be escaped in a regular expression. +/// +/// # Arguments +/// +/// * `c` - The character to check. pub fn regex_char_needs_escaping(c: char) -> bool { matches!( c, diff --git a/brush-parser/src/prompt.rs b/brush-parser/src/prompt.rs index 1f7204d3..87b96c0c 100644 --- a/brush-parser/src/prompt.rs +++ b/brush-parser/src/prompt.rs @@ -1,3 +1,5 @@ +//! Parser for shell prompt syntax (e.g., `PS1`). + use crate::error; /// A piece of a prompt string. @@ -127,7 +129,12 @@ peg::parser! { } } -pub fn parse_prompt(s: &str) -> Result, error::WordParseError> { +/// Parses a shell prompt string. +/// +/// # Arguments +/// +/// * `s` - The prompt string to parse. +pub fn parse(s: &str) -> Result, error::WordParseError> { let result = prompt_parser::prompt(s).map_err(error::WordParseError::Prompt)?; Ok(result) } diff --git a/brush-parser/src/test_command.rs b/brush-parser/src/test_command.rs index c41a8a0e..82df7dd6 100644 --- a/brush-parser/src/test_command.rs +++ b/brush-parser/src/test_command.rs @@ -1,6 +1,13 @@ +//! Parser for shell test commands. + use crate::{ast, error}; -pub fn parse_test_command(input: &[String]) -> Result { +/// Parses a test command expression. +/// +/// # Arguments +/// +/// * `input` - The test command expression to parse, in string form. +pub fn parse(input: &[String]) -> Result { let input: Vec<_> = input.iter().map(|s| s.as_str()).collect(); let expr = test_command::full_expression(input.as_slice()) diff --git a/brush-parser/src/word.rs b/brush-parser/src/word.rs index c6667edd..8a72c0a9 100644 --- a/brush-parser/src/word.rs +++ b/brush-parser/src/word.rs @@ -1,3 +1,14 @@ +//! Parser for shell words, used in expansion and other contexts. +//! +//! Implements support for: +//! +//! - Text quoting (single, double, ANSI C). +//! - Escape sequences. +//! - Tilde prefixes. +//! - Parameter expansion expressions. +//! - Command substitution expressions. +//! - Arithmetic expansion expressions. + use crate::ast; use crate::error; use crate::ParserOptions; @@ -229,10 +240,7 @@ pub enum ParameterTransformOp { /// /// * `word` - The word to parse. /// * `options` - The parser options to use. -pub fn parse_word_for_expansion( - word: &str, - options: &ParserOptions, -) -> Result, error::WordParseError> { +pub fn parse(word: &str, options: &ParserOptions) -> Result, error::WordParseError> { tracing::debug!("Parsing word '{}'", word); let pieces = expansion_parser::unexpanded_word(word, options) @@ -561,7 +569,7 @@ mod tests { super::expansion_parser::command("echo hi", &ParserOptions::default())?; super::expansion_parser::command_substitution("$(echo hi)", &ParserOptions::default())?; - let parsed = super::parse_word_for_expansion("$(echo hi)", &ParserOptions::default())?; + let parsed = super::parse("$(echo hi)", &ParserOptions::default())?; assert_matches!( &parsed[..], [WordPiece::CommandSubstitution(s)] if s.as_str() == "echo hi" @@ -580,7 +588,7 @@ mod tests { &ParserOptions::default(), )?; - let parsed = super::parse_word_for_expansion(r#"$(echo "hi")"#, &ParserOptions::default())?; + let parsed = super::parse(r#"$(echo "hi")"#, &ParserOptions::default())?; assert_matches!( &parsed[..], [WordPiece::CommandSubstitution(s)] if s.as_str() == r#"echo "hi""# @@ -591,7 +599,7 @@ mod tests { #[test] fn parse_command_substitution_with_embedded_extglob() -> Result<()> { - let parsed = super::parse_word_for_expansion("$(echo !(x))", &ParserOptions::default())?; + let parsed = super::parse("$(echo !(x))", &ParserOptions::default())?; assert_matches!( &parsed[..], [WordPiece::CommandSubstitution(s)] if s.as_str() == "echo !(x)" diff --git a/brush-shell/src/main.rs b/brush-shell/src/main.rs index 82506a28..1727fa7a 100644 --- a/brush-shell/src/main.rs +++ b/brush-shell/src/main.rs @@ -1,76 +1,78 @@ -//! The main entry point for the brush shell. +//! Implements the command-line interface for the `brush` shell. use std::{collections::HashSet, io::IsTerminal, path::Path}; use clap::{builder::styling, Parser}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, Layer}; +/// Parsed command-line arguments for the brush shell. #[derive(Parser)] #[clap(version, about, disable_help_flag = true, disable_version_flag = true, styles = brush_help_styles())] struct CommandLineArgs { - #[clap(long = "help", action = clap::ArgAction::HelpLong, help = "Display usage information")] + /// Display usage information. + #[clap(long = "help", action = clap::ArgAction::HelpLong)] help: Option, - #[clap(long = "version", action = clap::ArgAction::Version, help = "Display shell version")] + /// Display shell version. + #[clap(long = "version", action = clap::ArgAction::Version)] version: Option, - #[arg(short = 'c', help = "Execute the provided command and then exit")] + /// Execute the provided command and then exit. + #[arg(short = 'c')] command: Option, - #[clap(short = 'i', help = "Run in interactive mode")] + /// Run in interactive mode. + #[clap(short = 'i')] interactive: bool, - #[clap( - short = 'l', - long = "login", - help = "Make shell act as if it had been invoked as a login shell" - )] + /// Make shell act as if it had been invoked as a login shell. + #[clap(short = 'l', long = "login")] login: bool, - #[clap(long = "noediting", help = "Don't use readline for input.")] + /// Don't use readline for input. + #[clap(long = "noediting")] no_editing: bool, - #[clap( - long = "noprofile", - help = "Don't process any profile/login files (/etc/profile, ~/.bash_profile, ~/.bash_login, ~/.profile)." - )] + /// Don't process any profile/login files (`/etc/profile`, `~/.bash_profile`, `~/.bash_login`, `~/.profile`). + #[clap(long = "noprofile")] no_profile: bool, - #[clap( - long = "norc", - help = "Don't process ~/.bashrc if the shell is interactive." - )] + /// Don't process "rc" files if the shell is interactive (e.g., `~/.bashrc`, `~/.brushrc`). + #[clap(long = "norc")] no_rc: bool, - #[clap(long = "posix", help = "Disable non-POSIX extensions.")] + /// Disable non-POSIX extensions. + #[clap(long = "posix")] posix: bool, - #[clap(short = 's', help = "Read commands from standard input.")] + /// Read commands from standard input. + #[clap(short = 's')] read_commands_from_stdin: bool, - #[clap(long = "sh", help = "Run in sh compatibility mode.")] + /// Run in sh compatibility mode. + #[clap(long = "sh")] sh_mode: bool, - #[clap( - short = 'v', - long = "verbose", - help = "Print input when it's processed." - )] + /// Print input when it's processed. + #[clap(short = 'v', long = "verbose")] verbose: bool, - #[clap(short = 'x', help = "Print commands as they execute.")] + /// Print commands as they execute. + #[clap(short = 'x')] print_commands_and_arguments: bool, - #[clap(long = "disable-bracketed-paste", help = "Disable bracketed paste.")] + /// Disable bracketed paste. + #[clap(long = "disable-bracketed-paste")] disable_bracketed_paste: bool, + /// Enable debug logging for classes of tracing events. #[clap(long = "log-enable")] enabled_log_events: Vec, - #[clap(help = "Path to script to execute")] + /// Path to script to execute. script_path: Option, - #[clap(help = "Arguments for script")] + /// Arguments for script. script_args: Vec, } @@ -118,6 +120,7 @@ impl CommandLineArgs { } } +/// Main entry point for the `brush` shell. fn main() { // // Parse args. @@ -190,10 +193,17 @@ fn main() { std::process::exit(exit_code as i32); } +/// Run the brush shell. Returns the exit code. +/// +/// # Arguments +/// +/// * `cli_args` - The command-line arguments to the shell, in string form. +/// * `args` - The already-parsed command-line arguments. +#[doc(hidden)] async fn run( cli_args: Vec, args: CommandLineArgs, -) -> Result { +) -> Result { let argv0 = if args.sh_mode { // Simulate having been run as "sh". Some(String::from("sh")) @@ -249,6 +259,8 @@ async fn run( Ok(shell.shell().last_result()) } +/// Returns clap styling to be used for command-line help. +#[doc(hidden)] fn brush_help_styles() -> clap::builder::Styles { styling::Styles::styled() .header(