From c8025f4e01980b869cde7dbab7a50206eb87f3ef Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 11 Oct 2024 01:27:19 +0400 Subject: [PATCH 1/6] experiment: remove async-trait --- Cargo.lock | 1 - brush-core/Cargo.toml | 1 - brush-core/src/arithmetic.rs | 88 +-- brush-core/src/builtins.rs | 8 +- brush-core/src/builtins/alias.rs | 2 +- brush-core/src/builtins/bg.rs | 2 +- brush-core/src/builtins/break_.rs | 2 +- brush-core/src/builtins/brushinfo.rs | 2 +- brush-core/src/builtins/builtin_.rs | 2 +- brush-core/src/builtins/cd.rs | 2 +- brush-core/src/builtins/colon.rs | 2 +- brush-core/src/builtins/command.rs | 2 +- brush-core/src/builtins/complete.rs | 6 +- brush-core/src/builtins/continue_.rs | 2 +- brush-core/src/builtins/declare.rs | 2 +- brush-core/src/builtins/dirs.rs | 2 +- brush-core/src/builtins/dot.rs | 2 +- brush-core/src/builtins/echo.rs | 2 +- brush-core/src/builtins/enable.rs | 2 +- brush-core/src/builtins/eval.rs | 2 +- brush-core/src/builtins/exec.rs | 2 +- brush-core/src/builtins/exit.rs | 2 +- brush-core/src/builtins/export.rs | 2 +- brush-core/src/builtins/false_.rs | 2 +- brush-core/src/builtins/fg.rs | 2 +- brush-core/src/builtins/getopts.rs | 2 +- brush-core/src/builtins/help.rs | 2 +- brush-core/src/builtins/jobs.rs | 2 +- brush-core/src/builtins/kill.rs | 2 +- brush-core/src/builtins/let_.rs | 2 +- brush-core/src/builtins/popd.rs | 2 +- brush-core/src/builtins/printf.rs | 2 +- brush-core/src/builtins/pushd.rs | 2 +- brush-core/src/builtins/pwd.rs | 2 +- brush-core/src/builtins/read.rs | 2 +- brush-core/src/builtins/return_.rs | 2 +- brush-core/src/builtins/set.rs | 2 +- brush-core/src/builtins/shift.rs | 2 +- brush-core/src/builtins/shopt.rs | 2 +- brush-core/src/builtins/test.rs | 2 +- brush-core/src/builtins/trap.rs | 2 +- brush-core/src/builtins/true_.rs | 2 +- brush-core/src/builtins/type_.rs | 2 +- brush-core/src/builtins/umask.rs | 2 +- brush-core/src/builtins/unalias.rs | 2 +- brush-core/src/builtins/unimp.rs | 2 +- brush-core/src/builtins/unset.rs | 2 +- brush-core/src/builtins/wait.rs | 2 +- brush-core/src/error.rs | 1 + brush-core/src/interp.rs | 682 +++++++++++---------- brush-interactive/src/interactive_shell.rs | 150 ++--- brush-shell/src/brushctl.rs | 3 +- brush-shell/src/shell_factory.rs | 8 +- 53 files changed, 532 insertions(+), 502 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9c914c9e..998f6c22 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,7 +252,6 @@ version = "0.2.10" dependencies = [ "anyhow", "async-recursion", - "async-trait", "brush-parser", "cached", "cfg-if", diff --git a/brush-core/Cargo.toml b/brush-core/Cargo.toml index 7531a41e..0d1ffb74 100644 --- a/brush-core/Cargo.toml +++ b/brush-core/Cargo.toml @@ -18,7 +18,6 @@ workspace = true [dependencies] async-recursion = "1.1.0" -async-trait = "0.1.83" brush-parser = { version = "^0.2.8", path = "../brush-parser" } cached = "0.53.0" cfg-if = "1.0.0" diff --git a/brush-core/src/arithmetic.rs b/brush-core/src/arithmetic.rs index 4fd975a5..9c80d387 100644 --- a/brush-core/src/arithmetic.rs +++ b/brush-core/src/arithmetic.rs @@ -2,6 +2,7 @@ use std::borrow::Cow; use crate::{env, expansion, variables, Shell}; use brush_parser::ast; +use futures::future::{BoxFuture, FutureExt}; /// Represents an error that occurs during evaluation of an arithmetic expression. #[derive(Debug, thiserror::Error)] @@ -40,7 +41,7 @@ pub enum EvalError { } /// Trait implemented by arithmetic expressions that can be evaluated. -#[async_trait::async_trait] + pub trait ExpandAndEvaluate { /// Evaluate the given expression, returning the resulting numeric value. /// @@ -51,7 +52,6 @@ pub trait ExpandAndEvaluate { async fn eval(&self, shell: &mut Shell, trace_if_needed: bool) -> Result; } -#[async_trait::async_trait] impl ExpandAndEvaluate for ast::UnexpandedArithmeticExpr { async fn eval(&self, shell: &mut Shell, trace_if_needed: bool) -> Result { // Per documentation, first shell-expand it. @@ -76,58 +76,60 @@ impl ExpandAndEvaluate for ast::UnexpandedArithmeticExpr { } /// Trait implemented by evaluatable arithmetic expressions. -#[async_trait::async_trait] + pub trait Evaluatable { /// Evaluate the given arithmetic expression, returning the resulting numeric value. /// /// # Arguments /// /// * `shell` - The shell to use for evaluation. - async fn eval(&self, shell: &mut Shell) -> Result; + fn eval<'a>(&'a self, shell: &'a mut Shell) -> BoxFuture<'a, Result>; } -#[async_trait::async_trait] impl Evaluatable for ast::ArithmeticExpr { - async fn eval(&self, shell: &mut Shell) -> Result { - let value = match self { - ast::ArithmeticExpr::Literal(l) => *l, - ast::ArithmeticExpr::Reference(lvalue) => deref_lvalue(shell, lvalue).await?, - ast::ArithmeticExpr::UnaryOp(op, operand) => { - apply_unary_op(shell, *op, operand).await? - } - ast::ArithmeticExpr::BinaryOp(op, left, right) => { - apply_binary_op(shell, *op, left, right).await? - } - ast::ArithmeticExpr::Conditional(condition, then_expr, else_expr) => { - let conditional_eval = condition.eval(shell).await?; - - // Ensure we only evaluate the branch indicated by the condition. - if conditional_eval != 0 { - then_expr.eval(shell).await? - } else { - else_expr.eval(shell).await? + fn eval<'a>(&'a self, shell: &'a mut Shell) -> BoxFuture<'a, Result> { + async move { + let value = match self { + ast::ArithmeticExpr::Literal(l) => *l, + ast::ArithmeticExpr::Reference(lvalue) => deref_lvalue(shell, lvalue).await?, + ast::ArithmeticExpr::UnaryOp(op, operand) => { + apply_unary_op(shell, *op, operand).await? } - } - ast::ArithmeticExpr::Assignment(lvalue, expr) => { - let expr_eval = expr.eval(shell).await?; - assign(shell, lvalue, expr_eval).await? - } - ast::ArithmeticExpr::UnaryAssignment(op, lvalue) => { - apply_unary_assignment_op(shell, lvalue, *op).await? - } - ast::ArithmeticExpr::BinaryAssignment(op, lvalue, operand) => { - let value = apply_binary_op( - shell, - *op, - &ast::ArithmeticExpr::Reference(lvalue.clone()), - operand, - ) - .await?; - assign(shell, lvalue, value).await? - } - }; + ast::ArithmeticExpr::BinaryOp(op, left, right) => { + apply_binary_op(shell, *op, left, right).await? + } + ast::ArithmeticExpr::Conditional(condition, then_expr, else_expr) => { + let conditional_eval = condition.eval(shell).await?; + + // Ensure we only evaluate the branch indicated by the condition. + if conditional_eval != 0 { + then_expr.eval(shell).await? + } else { + else_expr.eval(shell).await? + } + } + ast::ArithmeticExpr::Assignment(lvalue, expr) => { + let expr_eval = expr.eval(shell).await?; + assign(shell, lvalue, expr_eval).await? + } + ast::ArithmeticExpr::UnaryAssignment(op, lvalue) => { + apply_unary_assignment_op(shell, lvalue, *op).await? + } + ast::ArithmeticExpr::BinaryAssignment(op, lvalue, operand) => { + let value = apply_binary_op( + shell, + *op, + &ast::ArithmeticExpr::Reference(lvalue.clone()), + operand, + ) + .await?; + assign(shell, lvalue, value).await? + } + }; - Ok(value) + Ok(value) + } + .boxed() } } diff --git a/brush-core/src/builtins.rs b/brush-core/src/builtins.rs index 2d19bb08..a8b90769 100644 --- a/brush-core/src/builtins.rs +++ b/brush-core/src/builtins.rs @@ -170,7 +170,7 @@ pub type CommandExecuteFunc = fn( pub type CommandContentFunc = fn(&str, ContentType) -> Result; /// Trait implemented by built-in shell commands. -#[async_trait::async_trait] + pub trait Command: Parser { /// Instantiates the built-in command with the given arguments. /// @@ -208,10 +208,10 @@ pub trait Command: Parser { /// # Arguments /// /// * `context` - The context in which the command is being executed. - async fn execute( + fn execute( &self, context: commands::ExecutionContext<'_>, - ) -> Result; + ) -> impl std::future::Future> + std::marker::Send; /// Returns the textual help content associated with the command. /// @@ -236,7 +236,7 @@ pub trait Command: Parser { /// Trait implemented by built-in shell commands that take specially handled declarations /// as arguments. -#[async_trait::async_trait] + pub trait DeclarationCommand: Command { /// Stores the declarations within the command instance. /// diff --git a/brush-core/src/builtins/alias.rs b/brush-core/src/builtins/alias.rs index 375c062c..99177ff3 100644 --- a/brush-core/src/builtins/alias.rs +++ b/brush-core/src/builtins/alias.rs @@ -15,7 +15,7 @@ pub(crate) struct AliasCommand { aliases: Vec, } -#[async_trait::async_trait] + impl builtins::Command for AliasCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/bg.rs b/brush-core/src/builtins/bg.rs index ed14d5fb..98d7d7bd 100644 --- a/brush-core/src/builtins/bg.rs +++ b/brush-core/src/builtins/bg.rs @@ -10,7 +10,7 @@ pub(crate) struct BgCommand { job_specs: Vec, } -#[async_trait::async_trait] + impl builtins::Command for BgCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/break_.rs b/brush-core/src/builtins/break_.rs index 15b2c244..83cdbf1e 100644 --- a/brush-core/src/builtins/break_.rs +++ b/brush-core/src/builtins/break_.rs @@ -10,7 +10,7 @@ pub(crate) struct BreakCommand { which_loop: i8, } -#[async_trait::async_trait] + impl builtins::Command for BreakCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/brushinfo.rs b/brush-core/src/builtins/brushinfo.rs index 3da0720e..d7d8a9d8 100644 --- a/brush-core/src/builtins/brushinfo.rs +++ b/brush-core/src/builtins/brushinfo.rs @@ -53,7 +53,7 @@ enum CompleteCommand { }, } -#[async_trait::async_trait] + impl builtins::Command for BrushInfoCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/builtin_.rs b/brush-core/src/builtins/builtin_.rs index 80754051..be522627 100644 --- a/brush-core/src/builtins/builtin_.rs +++ b/brush-core/src/builtins/builtin_.rs @@ -14,7 +14,7 @@ pub(crate) struct BuiltinCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for BuiltinCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/cd.rs b/brush-core/src/builtins/cd.rs index 860c3c96..b505f5c9 100644 --- a/brush-core/src/builtins/cd.rs +++ b/brush-core/src/builtins/cd.rs @@ -28,7 +28,7 @@ pub(crate) struct CdCommand { target_dir: Option, } -#[async_trait::async_trait] + impl builtins::Command for CdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/colon.rs b/brush-core/src/builtins/colon.rs index 50640ee0..85662e4a 100644 --- a/brush-core/src/builtins/colon.rs +++ b/brush-core/src/builtins/colon.rs @@ -10,7 +10,7 @@ pub(crate) struct ColonCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for ColonCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/command.rs b/brush-core/src/builtins/command.rs index bc0c2ea3..990445e0 100644 --- a/brush-core/src/builtins/command.rs +++ b/brush-core/src/builtins/command.rs @@ -26,7 +26,7 @@ pub(crate) struct CommandCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for CommandCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/complete.rs b/brush-core/src/builtins/complete.rs index 0f70bfdf..1fca5b45 100644 --- a/brush-core/src/builtins/complete.rs +++ b/brush-core/src/builtins/complete.rs @@ -210,7 +210,7 @@ pub(crate) struct CompleteCommand { names: Vec, } -#[async_trait::async_trait] + impl builtins::Command for CompleteCommand { async fn execute( &self, @@ -435,7 +435,7 @@ pub(crate) struct CompGenCommand { word: Option, } -#[async_trait::async_trait] + impl builtins::Command for CompGenCommand { async fn execute( &self, @@ -514,7 +514,7 @@ pub(crate) struct CompOptCommand { names: Vec, } -#[async_trait::async_trait] + impl builtins::Command for CompOptCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/continue_.rs b/brush-core/src/builtins/continue_.rs index 117f5ff5..673d0c36 100644 --- a/brush-core/src/builtins/continue_.rs +++ b/brush-core/src/builtins/continue_.rs @@ -10,7 +10,7 @@ pub(crate) struct ContinueCommand { which_loop: i8, } -#[async_trait::async_trait] + impl builtins::Command for ContinueCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/declare.rs b/brush-core/src/builtins/declare.rs index 407af940..82e13378 100644 --- a/brush-core/src/builtins/declare.rs +++ b/brush-core/src/builtins/declare.rs @@ -110,7 +110,7 @@ impl builtins::DeclarationCommand for DeclareCommand { } #[allow(clippy::too_many_lines)] -#[async_trait::async_trait] + impl builtins::Command for DeclareCommand { fn takes_plus_options() -> bool { true diff --git a/brush-core/src/builtins/dirs.rs b/brush-core/src/builtins/dirs.rs index 4ab5dd83..4e871263 100644 --- a/brush-core/src/builtins/dirs.rs +++ b/brush-core/src/builtins/dirs.rs @@ -25,7 +25,7 @@ pub(crate) struct DirsCommand { // TODO: implement +N and -N } -#[async_trait::async_trait] + impl builtins::Command for DirsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/dot.rs b/brush-core/src/builtins/dot.rs index 1c012e2a..2069ab34 100644 --- a/brush-core/src/builtins/dot.rs +++ b/brush-core/src/builtins/dot.rs @@ -15,7 +15,7 @@ pub(crate) struct DotCommand { pub script_args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for DotCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/echo.rs b/brush-core/src/builtins/echo.rs index ffa07dea..a98a2fbd 100644 --- a/brush-core/src/builtins/echo.rs +++ b/brush-core/src/builtins/echo.rs @@ -24,7 +24,7 @@ pub(crate) struct EchoCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for EchoCommand { /// Override the default [builtins::Command::new] function to handle clap's limitation related to `--`. /// See [crate::builtins::parse_known] for more information diff --git a/brush-core/src/builtins/enable.rs b/brush-core/src/builtins/enable.rs index aa1f590b..ada10a47 100644 --- a/brush-core/src/builtins/enable.rs +++ b/brush-core/src/builtins/enable.rs @@ -37,7 +37,7 @@ pub(crate) struct EnableCommand { names: Vec, } -#[async_trait::async_trait] + impl builtins::Command for EnableCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/eval.rs b/brush-core/src/builtins/eval.rs index 85887b5c..fc653dac 100644 --- a/brush-core/src/builtins/eval.rs +++ b/brush-core/src/builtins/eval.rs @@ -9,7 +9,7 @@ pub(crate) struct EvalCommand { pub args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for EvalCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/exec.rs b/brush-core/src/builtins/exec.rs index 6ab3443a..e9ca0b81 100644 --- a/brush-core/src/builtins/exec.rs +++ b/brush-core/src/builtins/exec.rs @@ -23,7 +23,7 @@ pub(crate) struct ExecCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for ExecCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/exit.rs b/brush-core/src/builtins/exit.rs index 5ce7d7eb..58b853b6 100644 --- a/brush-core/src/builtins/exit.rs +++ b/brush-core/src/builtins/exit.rs @@ -9,7 +9,7 @@ pub(crate) struct ExitCommand { code: Option, } -#[async_trait::async_trait] + impl builtins::Command for ExitCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/export.rs b/brush-core/src/builtins/export.rs index 28c48b95..2211a619 100644 --- a/brush-core/src/builtins/export.rs +++ b/brush-core/src/builtins/export.rs @@ -37,7 +37,7 @@ impl builtins::DeclarationCommand for ExportCommand { } } -#[async_trait::async_trait] + impl builtins::Command for ExportCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/false_.rs b/brush-core/src/builtins/false_.rs index d8379394..73bdbdee 100644 --- a/brush-core/src/builtins/false_.rs +++ b/brush-core/src/builtins/false_.rs @@ -6,7 +6,7 @@ use crate::{builtins, commands}; #[derive(Parser)] pub(crate) struct FalseCommand {} -#[async_trait::async_trait] + impl builtins::Command for FalseCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/fg.rs b/brush-core/src/builtins/fg.rs index e553aadd..6fedd30a 100644 --- a/brush-core/src/builtins/fg.rs +++ b/brush-core/src/builtins/fg.rs @@ -10,7 +10,7 @@ pub(crate) struct FgCommand { job_spec: Option, } -#[async_trait::async_trait] + impl builtins::Command for FgCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/getopts.rs b/brush-core/src/builtins/getopts.rs index 895b9260..9beaca4a 100644 --- a/brush-core/src/builtins/getopts.rs +++ b/brush-core/src/builtins/getopts.rs @@ -18,7 +18,7 @@ pub(crate) struct GetOptsCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for GetOptsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/help.rs b/brush-core/src/builtins/help.rs index 014c7d0a..93fb2b6c 100644 --- a/brush-core/src/builtins/help.rs +++ b/brush-core/src/builtins/help.rs @@ -22,7 +22,7 @@ pub(crate) struct HelpCommand { topic_patterns: Vec, } -#[async_trait::async_trait] + impl builtins::Command for HelpCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/jobs.rs b/brush-core/src/builtins/jobs.rs index e26b7071..da25fd9a 100644 --- a/brush-core/src/builtins/jobs.rs +++ b/brush-core/src/builtins/jobs.rs @@ -31,7 +31,7 @@ pub(crate) struct JobsCommand { job_specs: Vec, } -#[async_trait::async_trait] + impl builtins::Command for JobsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/kill.rs b/brush-core/src/builtins/kill.rs index 342e3b5c..d4f0883c 100644 --- a/brush-core/src/builtins/kill.rs +++ b/brush-core/src/builtins/kill.rs @@ -25,7 +25,7 @@ pub(crate) struct KillCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for KillCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/let_.rs b/brush-core/src/builtins/let_.rs index 774c6623..9ac8f682 100644 --- a/brush-core/src/builtins/let_.rs +++ b/brush-core/src/builtins/let_.rs @@ -11,7 +11,7 @@ pub(crate) struct LetCommand { exprs: Vec, } -#[async_trait::async_trait] + impl builtins::Command for LetCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/popd.rs b/brush-core/src/builtins/popd.rs index ed6409da..fc7daf58 100644 --- a/brush-core/src/builtins/popd.rs +++ b/brush-core/src/builtins/popd.rs @@ -14,7 +14,7 @@ pub(crate) struct PopdCommand { // } -#[async_trait::async_trait] + impl builtins::Command for PopdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/printf.rs b/brush-core/src/builtins/printf.rs index fcdc712b..5cad9a90 100644 --- a/brush-core/src/builtins/printf.rs +++ b/brush-core/src/builtins/printf.rs @@ -16,7 +16,7 @@ pub(crate) struct PrintfCommand { format_and_args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for PrintfCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/pushd.rs b/brush-core/src/builtins/pushd.rs index 08240b28..69beeb9d 100644 --- a/brush-core/src/builtins/pushd.rs +++ b/brush-core/src/builtins/pushd.rs @@ -16,7 +16,7 @@ pub(crate) struct PushdCommand { // } -#[async_trait::async_trait] + impl builtins::Command for PushdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/pwd.rs b/brush-core/src/builtins/pwd.rs index 72874da3..c3ad8f9f 100644 --- a/brush-core/src/builtins/pwd.rs +++ b/brush-core/src/builtins/pwd.rs @@ -14,7 +14,7 @@ pub(crate) struct PwdCommand { allow_symlinks: bool, } -#[async_trait::async_trait] + impl builtins::Command for PwdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/read.rs b/brush-core/src/builtins/read.rs index b3c045c2..05484dbd 100644 --- a/brush-core/src/builtins/read.rs +++ b/brush-core/src/builtins/read.rs @@ -59,7 +59,7 @@ pub(crate) struct ReadCommand { variable_names: Vec, } -#[async_trait::async_trait] + impl builtins::Command for ReadCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/return_.rs b/brush-core/src/builtins/return_.rs index ebcf9eff..7483e5d8 100644 --- a/brush-core/src/builtins/return_.rs +++ b/brush-core/src/builtins/return_.rs @@ -9,7 +9,7 @@ pub(crate) struct ReturnCommand { code: Option, } -#[async_trait::async_trait] + impl builtins::Command for ReturnCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/set.rs b/brush-core/src/builtins/set.rs index 8504e639..ce7b16e5 100644 --- a/brush-core/src/builtins/set.rs +++ b/brush-core/src/builtins/set.rs @@ -130,7 +130,7 @@ pub(crate) struct SetCommand { positional_args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for SetCommand { fn takes_plus_options() -> bool { true diff --git a/brush-core/src/builtins/shift.rs b/brush-core/src/builtins/shift.rs index e81bf77d..f641ba06 100644 --- a/brush-core/src/builtins/shift.rs +++ b/brush-core/src/builtins/shift.rs @@ -9,7 +9,7 @@ pub(crate) struct ShiftCommand { n: Option, } -#[async_trait::async_trait] + impl builtins::Command for ShiftCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/shopt.rs b/brush-core/src/builtins/shopt.rs index a3d4477c..cd78faa5 100644 --- a/brush-core/src/builtins/shopt.rs +++ b/brush-core/src/builtins/shopt.rs @@ -31,7 +31,7 @@ pub(crate) struct ShoptCommand { options: Vec, } -#[async_trait::async_trait] + impl builtins::Command for ShoptCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/test.rs b/brush-core/src/builtins/test.rs index bb151fb7..1f238a4b 100644 --- a/brush-core/src/builtins/test.rs +++ b/brush-core/src/builtins/test.rs @@ -11,7 +11,7 @@ pub(crate) struct TestCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for TestCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/trap.rs b/brush-core/src/builtins/trap.rs index 69e24e96..768c35b8 100644 --- a/brush-core/src/builtins/trap.rs +++ b/brush-core/src/builtins/trap.rs @@ -17,7 +17,7 @@ pub(crate) struct TrapCommand { args: Vec, } -#[async_trait::async_trait] + impl builtins::Command for TrapCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/true_.rs b/brush-core/src/builtins/true_.rs index 99ca5a0b..fb4be607 100644 --- a/brush-core/src/builtins/true_.rs +++ b/brush-core/src/builtins/true_.rs @@ -6,7 +6,7 @@ use crate::{builtins, commands}; #[derive(Parser)] pub(crate) struct TrueCommand {} -#[async_trait::async_trait] + impl builtins::Command for TrueCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/type_.rs b/brush-core/src/builtins/type_.rs index fded6e27..7d30b853 100644 --- a/brush-core/src/builtins/type_.rs +++ b/brush-core/src/builtins/type_.rs @@ -44,7 +44,7 @@ enum ResolvedType { File(PathBuf), } -#[async_trait::async_trait] + impl builtins::Command for TypeCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/umask.rs b/brush-core/src/builtins/umask.rs index 8a83f3b5..83cee0d1 100644 --- a/brush-core/src/builtins/umask.rs +++ b/brush-core/src/builtins/umask.rs @@ -20,7 +20,7 @@ pub(crate) struct UmaskCommand { mode: Option, } -#[async_trait::async_trait] + impl builtins::Command for UmaskCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unalias.rs b/brush-core/src/builtins/unalias.rs index 6c5c5c37..16aeb7f2 100644 --- a/brush-core/src/builtins/unalias.rs +++ b/brush-core/src/builtins/unalias.rs @@ -14,7 +14,7 @@ pub(crate) struct UnaliasCommand { aliases: Vec, } -#[async_trait::async_trait] + impl builtins::Command for UnaliasCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unimp.rs b/brush-core/src/builtins/unimp.rs index 67afa642..3ffb2226 100644 --- a/brush-core/src/builtins/unimp.rs +++ b/brush-core/src/builtins/unimp.rs @@ -13,7 +13,7 @@ pub(crate) struct UnimplementedCommand { declarations: Vec, } -#[async_trait::async_trait] + impl builtins::Command for UnimplementedCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unset.rs b/brush-core/src/builtins/unset.rs index b212091a..cc0fcd8b 100644 --- a/brush-core/src/builtins/unset.rs +++ b/brush-core/src/builtins/unset.rs @@ -34,7 +34,7 @@ impl UnsetNameInterpretation { } } -#[async_trait::async_trait] + impl builtins::Command for UnsetCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/wait.rs b/brush-core/src/builtins/wait.rs index 049164be..fa254a42 100644 --- a/brush-core/src/builtins/wait.rs +++ b/brush-core/src/builtins/wait.rs @@ -22,7 +22,7 @@ pub(crate) struct WaitCommand { job_specs: Vec, } -#[async_trait::async_trait] + impl builtins::Command for WaitCommand { async fn execute( &self, diff --git a/brush-core/src/error.rs b/brush-core/src/error.rs index a99c25d3..dcbce45f 100644 --- a/brush-core/src/error.rs +++ b/brush-core/src/error.rs @@ -190,3 +190,4 @@ pub enum Error { pub(crate) fn unimp(msg: &'static str) -> Result { Err(Error::Unimplemented(msg)) } + diff --git a/brush-core/src/interp.rs b/brush-core/src/interp.rs index 1b986b0b..65d49855 100644 --- a/brush-core/src/interp.rs +++ b/brush-core/src/interp.rs @@ -18,6 +18,7 @@ use crate::variables::{ ArrayLiteral, ShellValue, ShellValueLiteral, ShellValueUnsetType, ShellVariable, }; use crate::{error, expansion, extendedtests, jobs, openfiles, processes, sys, traps}; +use futures::future::{BoxFuture, FutureExt}; /// Encapsulates the result of executing a command. #[derive(Debug, Default)] @@ -126,16 +127,14 @@ pub enum ProcessGroupPolicy { SameProcessGroup, } -#[async_trait::async_trait] pub trait Execute { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result; + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result>; } -#[async_trait::async_trait] trait ExecuteInPipeline { async fn execute_in_pipeline( &self, @@ -143,68 +142,72 @@ trait ExecuteInPipeline { ) -> Result; } -#[async_trait::async_trait] impl Execute for ast::Program { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let mut result = ExecutionResult::success(); - - for command in &self.complete_commands { - result = command.execute(shell, params).await?; - if result.exit_shell || result.return_from_function_or_script { - break; + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let mut result = ExecutionResult::success(); + + for command in &self.complete_commands { + result = command.execute(shell, params).await?; + if result.exit_shell || result.return_from_function_or_script { + break; + } } - } - shell.last_exit_status = result.exit_code; - Ok(result) + shell.last_exit_status = result.exit_code; + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::CompoundList { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let mut result = ExecutionResult::success(); - - for ast::CompoundListItem(ao_list, sep) in &self.0 { - let run_async = matches!(sep, ast::SeparatorOperator::Async); - - if run_async { - // TODO: Reenable launching in child process? - // let job = spawn_ao_list_in_child(ao_list, shell, params).await?; - - let job = spawn_ao_list_in_task(ao_list, shell, params); - let job_formatted = job.to_pid_style_string(); + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let mut result = ExecutionResult::success(); + + for ast::CompoundListItem(ao_list, sep) in &self.0 { + let run_async = matches!(sep, ast::SeparatorOperator::Async); + + if run_async { + // TODO: Reenable launching in child process? + // let job = spawn_ao_list_in_child(ao_list, shell, params).await?; + + let job = spawn_ao_list_in_task(ao_list, shell, params); + let job_formatted = job.to_pid_style_string(); + + if shell.options.interactive { + writeln!(shell.stderr(), "{job_formatted}")?; + } - if shell.options.interactive { - writeln!(shell.stderr(), "{job_formatted}")?; + result = ExecutionResult::success(); + } else { + result = ao_list.execute(shell, params).await?; } - result = ExecutionResult::success(); - } else { - result = ao_list.execute(shell, params).await?; - } + // Check for early return. + if result.return_from_function_or_script { + break; + } - // Check for early return. - if result.return_from_function_or_script { - break; + // TODO: Check for continue/break being in for/while/until loop. + if result.continue_loop.is_some() || result.break_loop.is_some() { + break; + } } - // TODO: Check for continue/break being in for/while/until loop. - if result.continue_loop.is_some() || result.break_loop.is_some() { - break; - } + shell.last_exit_status = result.exit_code; + Ok(result) } - - shell.last_exit_status = result.exit_code; - Ok(result) + .boxed() } } @@ -236,73 +239,77 @@ fn spawn_ao_list_in_task<'a>( job } -#[async_trait::async_trait] impl Execute for ast::AndOrList { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let mut result = self.first.execute(shell, params).await?; - - for next_ao in &self.additional { - // Check for exit/return - if result.exit_shell || result.return_from_function_or_script { - break; - } + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let mut result = self.first.execute(shell, params).await?; + + for next_ao in &self.additional { + // Check for exit/return + if result.exit_shell || result.return_from_function_or_script { + break; + } - // Check for continue/break - // TODO: Validate that we're in a loop? - if result.continue_loop.is_some() || result.break_loop.is_some() { - break; - } + // Check for continue/break + // TODO: Validate that we're in a loop? + if result.continue_loop.is_some() || result.break_loop.is_some() { + break; + } - let (is_and, pipeline) = match next_ao { - ast::AndOr::And(p) => (true, p), - ast::AndOr::Or(p) => (false, p), - }; + let (is_and, pipeline) = match next_ao { + ast::AndOr::And(p) => (true, p), + ast::AndOr::Or(p) => (false, p), + }; - // If we short-circuit, then we don't break out of the whole loop - // but we skip evaluating the current pipeline. We'll then continue - // on and possibly evaluate a subsequent one (depending on the - // operator before it). - if is_and { - if !result.is_success() { + // If we short-circuit, then we don't break out of the whole loop + // but we skip evaluating the current pipeline. We'll then continue + // on and possibly evaluate a subsequent one (depending on the + // operator before it). + if is_and { + if !result.is_success() { + continue; + } + } else if result.is_success() { continue; } - } else if result.is_success() { - continue; + + result = pipeline.execute(shell, params).await?; } - result = pipeline.execute(shell, params).await?; + Ok(result) } - - Ok(result) + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::Pipeline { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - // Spawn all the processes required for the pipeline, connecting outputs/inputs with pipes - // as needed. - let spawn_results = spawn_pipeline_processes(self, shell, params).await?; - - // Wait for the processes. - let mut result = wait_for_pipeline_processes(self, spawn_results, shell).await?; - - // Invert the exit code if requested. - if self.bang { - result.exit_code = if result.exit_code == 0 { 1 } else { 0 }; - } + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + // Spawn all the processes required for the pipeline, connecting outputs/inputs with + // pipes as needed. + let spawn_results = spawn_pipeline_processes(self, shell, params).await?; + + // Wait for the processes. + let mut result = wait_for_pipeline_processes(self, spawn_results, shell).await?; + + // Invert the exit code if requested. + if self.bang { + result.exit_code = if result.exit_code == 0 { 1 } else { 0 }; + } - shell.last_exit_status = result.exit_code; + shell.last_exit_status = result.exit_code; - Ok(result) + Ok(result) + } + .boxed() } } @@ -401,11 +408,10 @@ async fn wait_for_pipeline_processes( Ok(result) } -#[async_trait::async_trait] impl ExecuteInPipeline for ast::Command { async fn execute_in_pipeline( &self, - pipeline_context: &mut PipelineExecutionContext, + pipeline_context: &mut PipelineExecutionContext<'_>, ) -> Result { if pipeline_context.shell.options.do_not_execute_commands { return Ok(CommandSpawnResult::ImmediateExit(0)); @@ -466,311 +472,327 @@ enum WhileOrUntil { Until, } -#[async_trait::async_trait] impl Execute for ast::CompoundCommand { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - match self { - ast::CompoundCommand::BraceGroup(ast::BraceGroupCommand(g)) => { - g.execute(shell, params).await - } - ast::CompoundCommand::Subshell(ast::SubshellCommand(s)) => { - // Clone off a new subshell, and run the body of the subshell there. - let mut subshell = shell.clone(); - s.execute(&mut subshell, params).await - } - ast::CompoundCommand::ForClause(f) => f.execute(shell, params).await, - ast::CompoundCommand::CaseClause(c) => c.execute(shell, params).await, - ast::CompoundCommand::IfClause(i) => i.execute(shell, params).await, - ast::CompoundCommand::WhileClause(w) => { - (WhileOrUntil::While, w).execute(shell, params).await - } - ast::CompoundCommand::UntilClause(u) => { - (WhileOrUntil::Until, u).execute(shell, params).await + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + match self { + ast::CompoundCommand::BraceGroup(ast::BraceGroupCommand(g)) => { + g.execute(shell, params).await + } + ast::CompoundCommand::Subshell(ast::SubshellCommand(s)) => { + // Clone off a new subshell, and run the body of the subshell there. + let mut subshell = shell.clone(); + s.execute(&mut subshell, params).await + } + ast::CompoundCommand::ForClause(f) => f.execute(shell, params).await, + ast::CompoundCommand::CaseClause(c) => c.execute(shell, params).await, + ast::CompoundCommand::IfClause(i) => i.execute(shell, params).await, + ast::CompoundCommand::WhileClause(w) => { + (WhileOrUntil::While, w).execute(shell, params).await + } + ast::CompoundCommand::UntilClause(u) => { + (WhileOrUntil::Until, u).execute(shell, params).await + } + ast::CompoundCommand::Arithmetic(a) => a.execute(shell, params).await, + ast::CompoundCommand::ArithmeticForClause(a) => a.execute(shell, params).await, } - ast::CompoundCommand::Arithmetic(a) => a.execute(shell, params).await, - ast::CompoundCommand::ArithmeticForClause(a) => a.execute(shell, params).await, } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::ForClauseCommand { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let mut result = ExecutionResult::success(); - - if let Some(unexpanded_values) = &self.values { - // Expand all values, with splitting enabled. - let mut expanded_values = vec![]; - for value in unexpanded_values { - let mut expanded = expansion::full_expand_and_split_word(shell, value).await?; - expanded_values.append(&mut expanded); - } - - for value in expanded_values { - if shell.options.print_commands_and_arguments { - shell.trace_command(std::format!( - "for {} in {}", - self.variable_name, - unexpanded_values.iter().join(" ") - ))?; + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let mut result = ExecutionResult::success(); + + if let Some(unexpanded_values) = &self.values { + // Expand all values, with splitting enabled. + let mut expanded_values = vec![]; + for value in unexpanded_values { + let mut expanded = expansion::full_expand_and_split_word(shell, value).await?; + expanded_values.append(&mut expanded); } - // Update the variable. - shell.env.update_or_add( - &self.variable_name, - ShellValueLiteral::Scalar(value), - |_| Ok(()), - EnvironmentLookup::Anywhere, - EnvironmentScope::Global, - )?; + for value in expanded_values { + if shell.options.print_commands_and_arguments { + shell.trace_command(std::format!( + "for {} in {}", + self.variable_name, + unexpanded_values.iter().join(" ") + ))?; + } - result = self.body.0.execute(shell, params).await?; - if result.return_from_function_or_script { - break; - } + // Update the variable. + shell.env.update_or_add( + &self.variable_name, + ShellValueLiteral::Scalar(value), + |_| Ok(()), + EnvironmentLookup::Anywhere, + EnvironmentScope::Global, + )?; - if let Some(continue_count) = &result.continue_loop { - if *continue_count > 0 { - return error::unimp("continue with count > 0"); + result = self.body.0.execute(shell, params).await?; + if result.return_from_function_or_script { + break; } - result.continue_loop = None; - } - if let Some(break_count) = &result.break_loop { - if *break_count == 0 { - result.break_loop = None; - } else { - result.break_loop = Some(*break_count - 1); + if let Some(continue_count) = &result.continue_loop { + if *continue_count > 0 { + return error::unimp("continue with count > 0"); + } + + result.continue_loop = None; + } + if let Some(break_count) = &result.break_loop { + if *break_count == 0 { + result.break_loop = None; + } else { + result.break_loop = Some(*break_count - 1); + } + break; } - break; } } - } - shell.last_exit_status = result.exit_code; - Ok(result) + shell.last_exit_status = result.exit_code; + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::CaseClauseCommand { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - // N.B. One would think it makes sense to trace the expanded value being switched - // on, but that's not it. - if shell.options.print_commands_and_arguments { - shell.trace_command(std::format!("case {} in", &self.value))?; - } + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + // N.B. One would think it makes sense to trace the expanded value being switched + // on, but that's not it. + if shell.options.print_commands_and_arguments { + shell.trace_command(std::format!("case {} in", &self.value))?; + } - let expanded_value = expansion::basic_expand_word(shell, &self.value).await?; + let expanded_value = expansion::basic_expand_word(shell, &self.value).await?; - for case in &self.cases { - let mut matches = false; + for case in &self.cases { + let mut matches = false; - for pattern in &case.patterns { - let expanded_pattern = expansion::basic_expand_pattern(shell, pattern).await?; - if expanded_pattern - .exactly_matches(expanded_value.as_str(), shell.options.extended_globbing)? - { - matches = true; - break; + for pattern in &case.patterns { + let expanded_pattern = expansion::basic_expand_pattern(shell, pattern).await?; + if expanded_pattern + .exactly_matches(expanded_value.as_str(), shell.options.extended_globbing)? + { + matches = true; + break; + } } - } - if matches { - if let Some(case_cmd) = &case.cmd { - return case_cmd.execute(shell, params).await; - } else { - break; + if matches { + if let Some(case_cmd) = &case.cmd { + return case_cmd.execute(shell, params).await; + } else { + break; + } } } - } - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; - Ok(result) + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::IfClauseCommand { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let condition = self.condition.execute(shell, params).await?; - - if condition.is_success() { - return self.then.execute(shell, params).await; - } + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let condition = self.condition.execute(shell, params).await?; + + if condition.is_success() { + return self.then.execute(shell, params).await; + } - if let Some(elses) = &self.elses { - for else_clause in elses { - match &else_clause.condition { - Some(else_condition) => { - let else_condition_result = else_condition.execute(shell, params).await?; - if else_condition_result.is_success() { + if let Some(elses) = &self.elses { + for else_clause in elses { + match &else_clause.condition { + Some(else_condition) => { + let else_condition_result = + else_condition.execute(shell, params).await?; + if else_condition_result.is_success() { + return else_clause.body.execute(shell, params).await; + } + } + None => { return else_clause.body.execute(shell, params).await; } } - None => { - return else_clause.body.execute(shell, params).await; - } } } - } - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; - Ok(result) + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for (WhileOrUntil, &ast::WhileOrUntilClauseCommand) { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let is_while = match self.0 { - WhileOrUntil::While => true, - WhileOrUntil::Until => false, - }; - let test_condition = &self.1 .0; - let body = &self.1 .1; + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let is_while = match self.0 { + WhileOrUntil::While => true, + WhileOrUntil::Until => false, + }; + let test_condition = &self.1 .0; + let body = &self.1 .1; - let mut result = ExecutionResult::success(); + let mut result = ExecutionResult::success(); - loop { - let condition_result = test_condition.execute(shell, params).await?; + loop { + let condition_result = test_condition.execute(shell, params).await?; - if condition_result.is_success() != is_while { - break; - } - - if condition_result.return_from_function_or_script { - break; - } + if condition_result.is_success() != is_while { + break; + } - result = body.0.execute(shell, params).await?; - if result.return_from_function_or_script { - break; - } + if condition_result.return_from_function_or_script { + break; + } - if let Some(continue_count) = &result.continue_loop { - if *continue_count > 0 { - return error::unimp("continue with count > 0"); + result = body.0.execute(shell, params).await?; + if result.return_from_function_or_script { + break; } - result.continue_loop = None; - } - if let Some(break_count) = &result.break_loop { - if *break_count == 0 { - result.break_loop = None; - } else { - result.break_loop = Some(*break_count - 1); + if let Some(continue_count) = &result.continue_loop { + if *continue_count > 0 { + return error::unimp("continue with count > 0"); + } + + result.continue_loop = None; + } + if let Some(break_count) = &result.break_loop { + if *break_count == 0 { + result.break_loop = None; + } else { + result.break_loop = Some(*break_count - 1); + } + break; } - break; } - } - Ok(result) + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::ArithmeticCommand { - async fn execute( - &self, - shell: &mut Shell, - _params: &ExecutionParameters, - ) -> Result { - let value = self.expr.eval(shell, true).await?; - let result = if value != 0 { - ExecutionResult::success() - } else { - ExecutionResult::new(1) - }; + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + _params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let value = self.expr.eval(shell, true).await?; + let result = if value != 0 { + ExecutionResult::success() + } else { + ExecutionResult::new(1) + }; - shell.last_exit_status = result.exit_code; + shell.last_exit_status = result.exit_code; - Ok(result) + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::ArithmeticForClauseCommand { - async fn execute( - &self, - shell: &mut Shell, - params: &ExecutionParameters, - ) -> Result { - let mut result = ExecutionResult::success(); - if let Some(initializer) = &self.initializer { - initializer.eval(shell, true).await?; - } + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + let mut result = ExecutionResult::success(); + if let Some(initializer) = &self.initializer { + initializer.eval(shell, true).await?; + } - loop { - if let Some(condition) = &self.condition { - if condition.eval(shell, true).await? == 0 { + loop { + if let Some(condition) = &self.condition { + if condition.eval(shell, true).await? == 0 { + break; + } + } + + result = self.body.0.execute(shell, params).await?; + if result.return_from_function_or_script { break; } - } - result = self.body.0.execute(shell, params).await?; - if result.return_from_function_or_script { - break; + if let Some(updater) = &self.updater { + updater.eval(shell, true).await?; + } } - if let Some(updater) = &self.updater { - updater.eval(shell, true).await?; - } + shell.last_exit_status = result.exit_code; + Ok(result) } - - shell.last_exit_status = result.exit_code; - Ok(result) + .boxed() } } -#[async_trait::async_trait] impl Execute for ast::FunctionDefinition { - async fn execute( - &self, - shell: &mut Shell, - _params: &ExecutionParameters, - ) -> Result { - shell - .funcs - .update(self.fname.clone(), Arc::new(self.clone())); - - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; - - Ok(result) + fn execute<'a>( + &'a self, + shell: &'a mut Shell, + _params: &'a ExecutionParameters, + ) -> BoxFuture<'a, Result> { + async move { + shell + .funcs + .update(self.fname.clone(), Arc::new(self.clone())); + + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; + + Ok(result) + } + .boxed() } } -#[async_trait::async_trait] impl ExecuteInPipeline for ast::SimpleCommand { #[allow(clippy::too_many_lines)] // TODO: refactor this function async fn execute_in_pipeline( &self, - context: &mut PipelineExecutionContext, + context: &mut PipelineExecutionContext<'_>, ) -> Result { let default_prefix = ast::CommandPrefix::default(); let prefix_items = self.prefix.as_ref().unwrap_or(&default_prefix); diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index 3d8921c2..86361f11 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -32,7 +32,7 @@ pub struct InteractivePrompt { } /// Represents a shell capable of taking commands from standard input. -#[async_trait::async_trait] + pub trait InteractiveShell { /// Returns an immutable reference to the inner shell object. fn shell(&self) -> impl AsRef + Send; @@ -53,94 +53,100 @@ pub trait 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. - async fn run_interactively(&mut self) -> Result<(), ShellError> { - // TODO: Consider finding a better place for this. - let _ = brush_core::TerminalControl::acquire()?; - - loop { - let result = self.run_interactively_once().await?; - match result { - InteractiveExecutionResult::Executed(brush_core::ExecutionResult { - exit_shell, - return_from_function_or_script, - .. - }) => { - if exit_shell { - break; + fn run_interactively(&mut self) -> impl std::future::Future> { + async { + // TODO: Consider finding a better place for this. + let _ = brush_core::TerminalControl::acquire()?; + + loop { + let result = self.run_interactively_once().await?; + match result { + InteractiveExecutionResult::Executed(brush_core::ExecutionResult { + exit_shell, + return_from_function_or_script, + .. + }) => { + if exit_shell { + break; + } + + if return_from_function_or_script { + tracing::error!("return from non-function/script"); + } } - - if return_from_function_or_script { - tracing::error!("return from non-function/script"); + InteractiveExecutionResult::Failed(e) => { + // Report the error, but continue to execute. + tracing::error!("error: {:#}", e); + } + InteractiveExecutionResult::Eof => { + break; } - } - InteractiveExecutionResult::Failed(e) => { - // Report the error, but continue to execute. - tracing::error!("error: {:#}", e); - } - InteractiveExecutionResult::Eof => { - break; } } - } - if self.shell().as_ref().options.interactive { - writeln!(self.shell().as_ref().stderr(), "exit")?; - } + if self.shell().as_ref().options.interactive { + writeln!(self.shell().as_ref().stderr(), "exit")?; + } - if let Err(e) = self.update_history() { - // N.B. This seems like the sort of thing that's worth being noisy about, - // but bash doesn't do that -- and probably for a reason. - tracing::debug!("couldn't save history: {e}"); - } + if let Err(e) = self.update_history() { + // N.B. This seems like the sort of thing that's worth being noisy about, + // but bash doesn't do that -- and probably for a reason. + tracing::debug!("couldn't save history: {e}"); + } - Ok(()) + Ok(()) + } } /// Runs the interactive shell loop once, reading a single command from standard input. - async fn run_interactively_once(&mut self) -> Result { - let mut shell_mut = self.shell_mut(); - - // Check for any completed jobs. - shell_mut.as_mut().check_for_completed_jobs()?; + fn run_interactively_once( + &mut self, + ) -> impl std::future::Future> { + async { + let mut shell_mut = self.shell_mut(); - // If there's a variable called PROMPT_COMMAND, then run it first. - if let Some((_, prompt_cmd)) = shell_mut.as_mut().env.get("PROMPT_COMMAND") { - let prompt_cmd = prompt_cmd.value().to_cow_string().to_string(); + // Check for any completed jobs. + shell_mut.as_mut().check_for_completed_jobs()?; - // Save (and later restore) the last exit status. - let prev_last_result = shell_mut.as_mut().last_exit_status; + // If there's a variable called PROMPT_COMMAND, then run it first. + if let Some((_, prompt_cmd)) = shell_mut.as_mut().env.get("PROMPT_COMMAND") { + let prompt_cmd = prompt_cmd.value().to_cow_string().to_string(); - let params = shell_mut.as_mut().default_exec_params(); + // Save (and later restore) the last exit status. + let prev_last_result = shell_mut.as_mut().last_exit_status; - shell_mut.as_mut().run_string(prompt_cmd, ¶ms).await?; - shell_mut.as_mut().last_exit_status = prev_last_result; - } - - // Now that we've done that, compose the prompt. - let prompt = InteractivePrompt { - prompt: shell_mut.as_mut().compose_prompt().await?, - alt_side_prompt: shell_mut.as_mut().compose_alt_side_prompt().await?, - continuation_prompt: shell_mut.as_mut().continuation_prompt()?, - }; + let params = shell_mut.as_mut().default_exec_params(); - drop(shell_mut); + shell_mut.as_mut().run_string(prompt_cmd, ¶ms).await?; + shell_mut.as_mut().last_exit_status = prev_last_result; + } - match self.read_line(prompt)? { - ReadResult::Input(read_result) => { - let mut shell_mut = self.shell_mut(); - let params = shell_mut.as_mut().default_exec_params(); - match shell_mut.as_mut().run_string(read_result, ¶ms).await { - Ok(result) => Ok(InteractiveExecutionResult::Executed(result)), - Err(e) => Ok(InteractiveExecutionResult::Failed(e)), + // Now that we've done that, compose the prompt. + let prompt = InteractivePrompt { + prompt: shell_mut.as_mut().compose_prompt().await?, + alt_side_prompt: shell_mut.as_mut().compose_alt_side_prompt().await?, + continuation_prompt: shell_mut.as_mut().continuation_prompt()?, + }; + + drop(shell_mut); + + match self.read_line(prompt)? { + ReadResult::Input(read_result) => { + let mut shell_mut = self.shell_mut(); + let params = shell_mut.as_mut().default_exec_params(); + match shell_mut.as_mut().run_string(read_result, ¶ms).await { + Ok(result) => Ok(InteractiveExecutionResult::Executed(result)), + Err(e) => Ok(InteractiveExecutionResult::Failed(e)), + } + } + ReadResult::Eof => Ok(InteractiveExecutionResult::Eof), + ReadResult::Interrupted => { + let mut shell_mut = self.shell_mut(); + shell_mut.as_mut().last_exit_status = 130; + Ok(InteractiveExecutionResult::Executed( + brush_core::ExecutionResult::new(130), + )) } - } - ReadResult::Eof => Ok(InteractiveExecutionResult::Eof), - ReadResult::Interrupted => { - let mut shell_mut = self.shell_mut(); - shell_mut.as_mut().last_exit_status = 130; - Ok(InteractiveExecutionResult::Executed( - brush_core::ExecutionResult::new(130), - )) } } } diff --git a/brush-shell/src/brushctl.rs b/brush-shell/src/brushctl.rs index c50a2f91..e03fc9ee 100644 --- a/brush-shell/src/brushctl.rs +++ b/brush-shell/src/brushctl.rs @@ -42,7 +42,7 @@ enum EventsCommand { }, } -#[async_trait::async_trait] + impl brush_core::builtins::Command for BrushCtlCommand { async fn execute( &self, @@ -54,6 +54,7 @@ impl brush_core::builtins::Command for BrushCtlCommand { } } + impl EventsCommand { fn execute( &self, diff --git a/brush-shell/src/shell_factory.rs b/brush-shell/src/shell_factory.rs index bab0215b..e5c034c7 100644 --- a/brush-shell/src/shell_factory.rs +++ b/brush-shell/src/shell_factory.rs @@ -1,4 +1,4 @@ -#[async_trait::async_trait] + pub(crate) trait ShellFactory { type ShellType: brush_interactive::InteractiveShell + Send; @@ -53,7 +53,7 @@ impl AsMut for StubShell { pub(crate) struct RustylineShellFactory; -#[async_trait::async_trait] + impl ShellFactory for RustylineShellFactory { #[cfg(all(feature = "rustyline", any(windows, unix)))] type ShellType = brush_interactive::RustylineShell; @@ -78,7 +78,7 @@ impl ShellFactory for RustylineShellFactory { pub(crate) struct ReedlineShellFactory; -#[async_trait::async_trait] + impl ShellFactory for ReedlineShellFactory { #[cfg(all(feature = "reedline", any(windows, unix)))] type ShellType = brush_interactive::ReedlineShell; @@ -103,7 +103,7 @@ impl ShellFactory for ReedlineShellFactory { pub(crate) struct BasicShellFactory; -#[async_trait::async_trait] + impl ShellFactory for BasicShellFactory { #[cfg(feature = "basic")] type ShellType = brush_interactive::BasicShell; From 8fdba4d77377cf1dee8c1a387eb075cc921bee1b Mon Sep 17 00:00:00 2001 From: Igor Date: Fri, 11 Oct 2024 13:02:06 +0400 Subject: [PATCH 2/6] revert: recursive async functions --- Cargo.lock | 1 + brush-core/Cargo.toml | 1 + brush-core/src/arithmetic.rs | 88 +++-- brush-core/src/interp.rs | 682 +++++++++++++++++------------------ 4 files changed, 375 insertions(+), 397 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 998f6c22..9c914c9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -252,6 +252,7 @@ version = "0.2.10" dependencies = [ "anyhow", "async-recursion", + "async-trait", "brush-parser", "cached", "cfg-if", diff --git a/brush-core/Cargo.toml b/brush-core/Cargo.toml index 0d1ffb74..7531a41e 100644 --- a/brush-core/Cargo.toml +++ b/brush-core/Cargo.toml @@ -18,6 +18,7 @@ workspace = true [dependencies] async-recursion = "1.1.0" +async-trait = "0.1.83" brush-parser = { version = "^0.2.8", path = "../brush-parser" } cached = "0.53.0" cfg-if = "1.0.0" diff --git a/brush-core/src/arithmetic.rs b/brush-core/src/arithmetic.rs index 9c80d387..4fd975a5 100644 --- a/brush-core/src/arithmetic.rs +++ b/brush-core/src/arithmetic.rs @@ -2,7 +2,6 @@ use std::borrow::Cow; use crate::{env, expansion, variables, Shell}; use brush_parser::ast; -use futures::future::{BoxFuture, FutureExt}; /// Represents an error that occurs during evaluation of an arithmetic expression. #[derive(Debug, thiserror::Error)] @@ -41,7 +40,7 @@ pub enum EvalError { } /// Trait implemented by arithmetic expressions that can be evaluated. - +#[async_trait::async_trait] pub trait ExpandAndEvaluate { /// Evaluate the given expression, returning the resulting numeric value. /// @@ -52,6 +51,7 @@ pub trait ExpandAndEvaluate { async fn eval(&self, shell: &mut Shell, trace_if_needed: bool) -> Result; } +#[async_trait::async_trait] impl ExpandAndEvaluate for ast::UnexpandedArithmeticExpr { async fn eval(&self, shell: &mut Shell, trace_if_needed: bool) -> Result { // Per documentation, first shell-expand it. @@ -76,60 +76,58 @@ impl ExpandAndEvaluate for ast::UnexpandedArithmeticExpr { } /// Trait implemented by evaluatable arithmetic expressions. - +#[async_trait::async_trait] pub trait Evaluatable { /// Evaluate the given arithmetic expression, returning the resulting numeric value. /// /// # Arguments /// /// * `shell` - The shell to use for evaluation. - fn eval<'a>(&'a self, shell: &'a mut Shell) -> BoxFuture<'a, Result>; + async fn eval(&self, shell: &mut Shell) -> Result; } +#[async_trait::async_trait] impl Evaluatable for ast::ArithmeticExpr { - fn eval<'a>(&'a self, shell: &'a mut Shell) -> BoxFuture<'a, Result> { - async move { - let value = match self { - ast::ArithmeticExpr::Literal(l) => *l, - ast::ArithmeticExpr::Reference(lvalue) => deref_lvalue(shell, lvalue).await?, - ast::ArithmeticExpr::UnaryOp(op, operand) => { - apply_unary_op(shell, *op, operand).await? - } - ast::ArithmeticExpr::BinaryOp(op, left, right) => { - apply_binary_op(shell, *op, left, right).await? - } - ast::ArithmeticExpr::Conditional(condition, then_expr, else_expr) => { - let conditional_eval = condition.eval(shell).await?; - - // Ensure we only evaluate the branch indicated by the condition. - if conditional_eval != 0 { - then_expr.eval(shell).await? - } else { - else_expr.eval(shell).await? - } - } - ast::ArithmeticExpr::Assignment(lvalue, expr) => { - let expr_eval = expr.eval(shell).await?; - assign(shell, lvalue, expr_eval).await? - } - ast::ArithmeticExpr::UnaryAssignment(op, lvalue) => { - apply_unary_assignment_op(shell, lvalue, *op).await? - } - ast::ArithmeticExpr::BinaryAssignment(op, lvalue, operand) => { - let value = apply_binary_op( - shell, - *op, - &ast::ArithmeticExpr::Reference(lvalue.clone()), - operand, - ) - .await?; - assign(shell, lvalue, value).await? + async fn eval(&self, shell: &mut Shell) -> Result { + let value = match self { + ast::ArithmeticExpr::Literal(l) => *l, + ast::ArithmeticExpr::Reference(lvalue) => deref_lvalue(shell, lvalue).await?, + ast::ArithmeticExpr::UnaryOp(op, operand) => { + apply_unary_op(shell, *op, operand).await? + } + ast::ArithmeticExpr::BinaryOp(op, left, right) => { + apply_binary_op(shell, *op, left, right).await? + } + ast::ArithmeticExpr::Conditional(condition, then_expr, else_expr) => { + let conditional_eval = condition.eval(shell).await?; + + // Ensure we only evaluate the branch indicated by the condition. + if conditional_eval != 0 { + then_expr.eval(shell).await? + } else { + else_expr.eval(shell).await? } - }; + } + ast::ArithmeticExpr::Assignment(lvalue, expr) => { + let expr_eval = expr.eval(shell).await?; + assign(shell, lvalue, expr_eval).await? + } + ast::ArithmeticExpr::UnaryAssignment(op, lvalue) => { + apply_unary_assignment_op(shell, lvalue, *op).await? + } + ast::ArithmeticExpr::BinaryAssignment(op, lvalue, operand) => { + let value = apply_binary_op( + shell, + *op, + &ast::ArithmeticExpr::Reference(lvalue.clone()), + operand, + ) + .await?; + assign(shell, lvalue, value).await? + } + }; - Ok(value) - } - .boxed() + Ok(value) } } diff --git a/brush-core/src/interp.rs b/brush-core/src/interp.rs index 65d49855..1b986b0b 100644 --- a/brush-core/src/interp.rs +++ b/brush-core/src/interp.rs @@ -18,7 +18,6 @@ use crate::variables::{ ArrayLiteral, ShellValue, ShellValueLiteral, ShellValueUnsetType, ShellVariable, }; use crate::{error, expansion, extendedtests, jobs, openfiles, processes, sys, traps}; -use futures::future::{BoxFuture, FutureExt}; /// Encapsulates the result of executing a command. #[derive(Debug, Default)] @@ -127,14 +126,16 @@ pub enum ProcessGroupPolicy { SameProcessGroup, } +#[async_trait::async_trait] pub trait Execute { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result>; + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result; } +#[async_trait::async_trait] trait ExecuteInPipeline { async fn execute_in_pipeline( &self, @@ -142,72 +143,68 @@ trait ExecuteInPipeline { ) -> Result; } +#[async_trait::async_trait] impl Execute for ast::Program { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let mut result = ExecutionResult::success(); - - for command in &self.complete_commands { - result = command.execute(shell, params).await?; - if result.exit_shell || result.return_from_function_or_script { - break; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let mut result = ExecutionResult::success(); + + for command in &self.complete_commands { + result = command.execute(shell, params).await?; + if result.exit_shell || result.return_from_function_or_script { + break; } - - shell.last_exit_status = result.exit_code; - Ok(result) } - .boxed() + + shell.last_exit_status = result.exit_code; + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::CompoundList { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let mut result = ExecutionResult::success(); - - for ast::CompoundListItem(ao_list, sep) in &self.0 { - let run_async = matches!(sep, ast::SeparatorOperator::Async); - - if run_async { - // TODO: Reenable launching in child process? - // let job = spawn_ao_list_in_child(ao_list, shell, params).await?; - - let job = spawn_ao_list_in_task(ao_list, shell, params); - let job_formatted = job.to_pid_style_string(); - - if shell.options.interactive { - writeln!(shell.stderr(), "{job_formatted}")?; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let mut result = ExecutionResult::success(); - result = ExecutionResult::success(); - } else { - result = ao_list.execute(shell, params).await?; - } + for ast::CompoundListItem(ao_list, sep) in &self.0 { + let run_async = matches!(sep, ast::SeparatorOperator::Async); - // Check for early return. - if result.return_from_function_or_script { - break; - } + if run_async { + // TODO: Reenable launching in child process? + // let job = spawn_ao_list_in_child(ao_list, shell, params).await?; - // TODO: Check for continue/break being in for/while/until loop. - if result.continue_loop.is_some() || result.break_loop.is_some() { - break; + let job = spawn_ao_list_in_task(ao_list, shell, params); + let job_formatted = job.to_pid_style_string(); + + if shell.options.interactive { + writeln!(shell.stderr(), "{job_formatted}")?; } + + result = ExecutionResult::success(); + } else { + result = ao_list.execute(shell, params).await?; } - shell.last_exit_status = result.exit_code; - Ok(result) + // Check for early return. + if result.return_from_function_or_script { + break; + } + + // TODO: Check for continue/break being in for/while/until loop. + if result.continue_loop.is_some() || result.break_loop.is_some() { + break; + } } - .boxed() + + shell.last_exit_status = result.exit_code; + Ok(result) } } @@ -239,77 +236,73 @@ fn spawn_ao_list_in_task<'a>( job } +#[async_trait::async_trait] impl Execute for ast::AndOrList { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let mut result = self.first.execute(shell, params).await?; - - for next_ao in &self.additional { - // Check for exit/return - if result.exit_shell || result.return_from_function_or_script { - break; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let mut result = self.first.execute(shell, params).await?; + + for next_ao in &self.additional { + // Check for exit/return + if result.exit_shell || result.return_from_function_or_script { + break; + } - // Check for continue/break - // TODO: Validate that we're in a loop? - if result.continue_loop.is_some() || result.break_loop.is_some() { - break; - } + // Check for continue/break + // TODO: Validate that we're in a loop? + if result.continue_loop.is_some() || result.break_loop.is_some() { + break; + } - let (is_and, pipeline) = match next_ao { - ast::AndOr::And(p) => (true, p), - ast::AndOr::Or(p) => (false, p), - }; + let (is_and, pipeline) = match next_ao { + ast::AndOr::And(p) => (true, p), + ast::AndOr::Or(p) => (false, p), + }; - // If we short-circuit, then we don't break out of the whole loop - // but we skip evaluating the current pipeline. We'll then continue - // on and possibly evaluate a subsequent one (depending on the - // operator before it). - if is_and { - if !result.is_success() { - continue; - } - } else if result.is_success() { + // If we short-circuit, then we don't break out of the whole loop + // but we skip evaluating the current pipeline. We'll then continue + // on and possibly evaluate a subsequent one (depending on the + // operator before it). + if is_and { + if !result.is_success() { continue; } - - result = pipeline.execute(shell, params).await?; + } else if result.is_success() { + continue; } - Ok(result) + result = pipeline.execute(shell, params).await?; } - .boxed() + + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::Pipeline { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - // Spawn all the processes required for the pipeline, connecting outputs/inputs with - // pipes as needed. - let spawn_results = spawn_pipeline_processes(self, shell, params).await?; - - // Wait for the processes. - let mut result = wait_for_pipeline_processes(self, spawn_results, shell).await?; - - // Invert the exit code if requested. - if self.bang { - result.exit_code = if result.exit_code == 0 { 1 } else { 0 }; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + // Spawn all the processes required for the pipeline, connecting outputs/inputs with pipes + // as needed. + let spawn_results = spawn_pipeline_processes(self, shell, params).await?; + + // Wait for the processes. + let mut result = wait_for_pipeline_processes(self, spawn_results, shell).await?; + + // Invert the exit code if requested. + if self.bang { + result.exit_code = if result.exit_code == 0 { 1 } else { 0 }; + } - shell.last_exit_status = result.exit_code; + shell.last_exit_status = result.exit_code; - Ok(result) - } - .boxed() + Ok(result) } } @@ -408,10 +401,11 @@ async fn wait_for_pipeline_processes( Ok(result) } +#[async_trait::async_trait] impl ExecuteInPipeline for ast::Command { async fn execute_in_pipeline( &self, - pipeline_context: &mut PipelineExecutionContext<'_>, + pipeline_context: &mut PipelineExecutionContext, ) -> Result { if pipeline_context.shell.options.do_not_execute_commands { return Ok(CommandSpawnResult::ImmediateExit(0)); @@ -472,327 +466,311 @@ enum WhileOrUntil { Until, } +#[async_trait::async_trait] impl Execute for ast::CompoundCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - match self { - ast::CompoundCommand::BraceGroup(ast::BraceGroupCommand(g)) => { - g.execute(shell, params).await - } - ast::CompoundCommand::Subshell(ast::SubshellCommand(s)) => { - // Clone off a new subshell, and run the body of the subshell there. - let mut subshell = shell.clone(); - s.execute(&mut subshell, params).await - } - ast::CompoundCommand::ForClause(f) => f.execute(shell, params).await, - ast::CompoundCommand::CaseClause(c) => c.execute(shell, params).await, - ast::CompoundCommand::IfClause(i) => i.execute(shell, params).await, - ast::CompoundCommand::WhileClause(w) => { - (WhileOrUntil::While, w).execute(shell, params).await - } - ast::CompoundCommand::UntilClause(u) => { - (WhileOrUntil::Until, u).execute(shell, params).await - } - ast::CompoundCommand::Arithmetic(a) => a.execute(shell, params).await, - ast::CompoundCommand::ArithmeticForClause(a) => a.execute(shell, params).await, + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + match self { + ast::CompoundCommand::BraceGroup(ast::BraceGroupCommand(g)) => { + g.execute(shell, params).await + } + ast::CompoundCommand::Subshell(ast::SubshellCommand(s)) => { + // Clone off a new subshell, and run the body of the subshell there. + let mut subshell = shell.clone(); + s.execute(&mut subshell, params).await } + ast::CompoundCommand::ForClause(f) => f.execute(shell, params).await, + ast::CompoundCommand::CaseClause(c) => c.execute(shell, params).await, + ast::CompoundCommand::IfClause(i) => i.execute(shell, params).await, + ast::CompoundCommand::WhileClause(w) => { + (WhileOrUntil::While, w).execute(shell, params).await + } + ast::CompoundCommand::UntilClause(u) => { + (WhileOrUntil::Until, u).execute(shell, params).await + } + ast::CompoundCommand::Arithmetic(a) => a.execute(shell, params).await, + ast::CompoundCommand::ArithmeticForClause(a) => a.execute(shell, params).await, } - .boxed() } } +#[async_trait::async_trait] impl Execute for ast::ForClauseCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let mut result = ExecutionResult::success(); - - if let Some(unexpanded_values) = &self.values { - // Expand all values, with splitting enabled. - let mut expanded_values = vec![]; - for value in unexpanded_values { - let mut expanded = expansion::full_expand_and_split_word(shell, value).await?; - expanded_values.append(&mut expanded); - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let mut result = ExecutionResult::success(); - for value in expanded_values { - if shell.options.print_commands_and_arguments { - shell.trace_command(std::format!( - "for {} in {}", - self.variable_name, - unexpanded_values.iter().join(" ") - ))?; - } + if let Some(unexpanded_values) = &self.values { + // Expand all values, with splitting enabled. + let mut expanded_values = vec![]; + for value in unexpanded_values { + let mut expanded = expansion::full_expand_and_split_word(shell, value).await?; + expanded_values.append(&mut expanded); + } - // Update the variable. - shell.env.update_or_add( - &self.variable_name, - ShellValueLiteral::Scalar(value), - |_| Ok(()), - EnvironmentLookup::Anywhere, - EnvironmentScope::Global, - )?; + for value in expanded_values { + if shell.options.print_commands_and_arguments { + shell.trace_command(std::format!( + "for {} in {}", + self.variable_name, + unexpanded_values.iter().join(" ") + ))?; + } - result = self.body.0.execute(shell, params).await?; - if result.return_from_function_or_script { - break; - } + // Update the variable. + shell.env.update_or_add( + &self.variable_name, + ShellValueLiteral::Scalar(value), + |_| Ok(()), + EnvironmentLookup::Anywhere, + EnvironmentScope::Global, + )?; - if let Some(continue_count) = &result.continue_loop { - if *continue_count > 0 { - return error::unimp("continue with count > 0"); - } + result = self.body.0.execute(shell, params).await?; + if result.return_from_function_or_script { + break; + } - result.continue_loop = None; + if let Some(continue_count) = &result.continue_loop { + if *continue_count > 0 { + return error::unimp("continue with count > 0"); } - if let Some(break_count) = &result.break_loop { - if *break_count == 0 { - result.break_loop = None; - } else { - result.break_loop = Some(*break_count - 1); - } - break; + + result.continue_loop = None; + } + if let Some(break_count) = &result.break_loop { + if *break_count == 0 { + result.break_loop = None; + } else { + result.break_loop = Some(*break_count - 1); } + break; } } - - shell.last_exit_status = result.exit_code; - Ok(result) } - .boxed() + + shell.last_exit_status = result.exit_code; + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::CaseClauseCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - // N.B. One would think it makes sense to trace the expanded value being switched - // on, but that's not it. - if shell.options.print_commands_and_arguments { - shell.trace_command(std::format!("case {} in", &self.value))?; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + // N.B. One would think it makes sense to trace the expanded value being switched + // on, but that's not it. + if shell.options.print_commands_and_arguments { + shell.trace_command(std::format!("case {} in", &self.value))?; + } - let expanded_value = expansion::basic_expand_word(shell, &self.value).await?; + let expanded_value = expansion::basic_expand_word(shell, &self.value).await?; - for case in &self.cases { - let mut matches = false; + for case in &self.cases { + let mut matches = false; - for pattern in &case.patterns { - let expanded_pattern = expansion::basic_expand_pattern(shell, pattern).await?; - if expanded_pattern - .exactly_matches(expanded_value.as_str(), shell.options.extended_globbing)? - { - matches = true; - break; - } + for pattern in &case.patterns { + let expanded_pattern = expansion::basic_expand_pattern(shell, pattern).await?; + if expanded_pattern + .exactly_matches(expanded_value.as_str(), shell.options.extended_globbing)? + { + matches = true; + break; } + } - if matches { - if let Some(case_cmd) = &case.cmd { - return case_cmd.execute(shell, params).await; - } else { - break; - } + if matches { + if let Some(case_cmd) = &case.cmd { + return case_cmd.execute(shell, params).await; + } else { + break; } } + } - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; - Ok(result) - } - .boxed() + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::IfClauseCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let condition = self.condition.execute(shell, params).await?; - - if condition.is_success() { - return self.then.execute(shell, params).await; - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let condition = self.condition.execute(shell, params).await?; - if let Some(elses) = &self.elses { - for else_clause in elses { - match &else_clause.condition { - Some(else_condition) => { - let else_condition_result = - else_condition.execute(shell, params).await?; - if else_condition_result.is_success() { - return else_clause.body.execute(shell, params).await; - } - } - None => { + if condition.is_success() { + return self.then.execute(shell, params).await; + } + + if let Some(elses) = &self.elses { + for else_clause in elses { + match &else_clause.condition { + Some(else_condition) => { + let else_condition_result = else_condition.execute(shell, params).await?; + if else_condition_result.is_success() { return else_clause.body.execute(shell, params).await; } } + None => { + return else_clause.body.execute(shell, params).await; + } } } + } - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; - Ok(result) - } - .boxed() + Ok(result) } } +#[async_trait::async_trait] impl Execute for (WhileOrUntil, &ast::WhileOrUntilClauseCommand) { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let is_while = match self.0 { - WhileOrUntil::While => true, - WhileOrUntil::Until => false, - }; - let test_condition = &self.1 .0; - let body = &self.1 .1; - - let mut result = ExecutionResult::success(); + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let is_while = match self.0 { + WhileOrUntil::While => true, + WhileOrUntil::Until => false, + }; + let test_condition = &self.1 .0; + let body = &self.1 .1; - loop { - let condition_result = test_condition.execute(shell, params).await?; + let mut result = ExecutionResult::success(); - if condition_result.is_success() != is_while { - break; - } + loop { + let condition_result = test_condition.execute(shell, params).await?; - if condition_result.return_from_function_or_script { - break; - } + if condition_result.is_success() != is_while { + break; + } - result = body.0.execute(shell, params).await?; - if result.return_from_function_or_script { - break; - } + if condition_result.return_from_function_or_script { + break; + } - if let Some(continue_count) = &result.continue_loop { - if *continue_count > 0 { - return error::unimp("continue with count > 0"); - } + result = body.0.execute(shell, params).await?; + if result.return_from_function_or_script { + break; + } - result.continue_loop = None; + if let Some(continue_count) = &result.continue_loop { + if *continue_count > 0 { + return error::unimp("continue with count > 0"); } - if let Some(break_count) = &result.break_loop { - if *break_count == 0 { - result.break_loop = None; - } else { - result.break_loop = Some(*break_count - 1); - } - break; + + result.continue_loop = None; + } + if let Some(break_count) = &result.break_loop { + if *break_count == 0 { + result.break_loop = None; + } else { + result.break_loop = Some(*break_count - 1); } + break; } - - Ok(result) } - .boxed() + + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::ArithmeticCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - _params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let value = self.expr.eval(shell, true).await?; - let result = if value != 0 { - ExecutionResult::success() - } else { - ExecutionResult::new(1) - }; + async fn execute( + &self, + shell: &mut Shell, + _params: &ExecutionParameters, + ) -> Result { + let value = self.expr.eval(shell, true).await?; + let result = if value != 0 { + ExecutionResult::success() + } else { + ExecutionResult::new(1) + }; - shell.last_exit_status = result.exit_code; + shell.last_exit_status = result.exit_code; - Ok(result) - } - .boxed() + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::ArithmeticForClauseCommand { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - let mut result = ExecutionResult::success(); - if let Some(initializer) = &self.initializer { - initializer.eval(shell, true).await?; - } - - loop { - if let Some(condition) = &self.condition { - if condition.eval(shell, true).await? == 0 { - break; - } - } + async fn execute( + &self, + shell: &mut Shell, + params: &ExecutionParameters, + ) -> Result { + let mut result = ExecutionResult::success(); + if let Some(initializer) = &self.initializer { + initializer.eval(shell, true).await?; + } - result = self.body.0.execute(shell, params).await?; - if result.return_from_function_or_script { + loop { + if let Some(condition) = &self.condition { + if condition.eval(shell, true).await? == 0 { break; } + } - if let Some(updater) = &self.updater { - updater.eval(shell, true).await?; - } + result = self.body.0.execute(shell, params).await?; + if result.return_from_function_or_script { + break; } - shell.last_exit_status = result.exit_code; - Ok(result) + if let Some(updater) = &self.updater { + updater.eval(shell, true).await?; + } } - .boxed() + + shell.last_exit_status = result.exit_code; + Ok(result) } } +#[async_trait::async_trait] impl Execute for ast::FunctionDefinition { - fn execute<'a>( - &'a self, - shell: &'a mut Shell, - _params: &'a ExecutionParameters, - ) -> BoxFuture<'a, Result> { - async move { - shell - .funcs - .update(self.fname.clone(), Arc::new(self.clone())); - - let result = ExecutionResult::success(); - shell.last_exit_status = result.exit_code; - - Ok(result) - } - .boxed() + async fn execute( + &self, + shell: &mut Shell, + _params: &ExecutionParameters, + ) -> Result { + shell + .funcs + .update(self.fname.clone(), Arc::new(self.clone())); + + let result = ExecutionResult::success(); + shell.last_exit_status = result.exit_code; + + Ok(result) } } +#[async_trait::async_trait] impl ExecuteInPipeline for ast::SimpleCommand { #[allow(clippy::too_many_lines)] // TODO: refactor this function async fn execute_in_pipeline( &self, - context: &mut PipelineExecutionContext<'_>, + context: &mut PipelineExecutionContext, ) -> Result { let default_prefix = ast::CommandPrefix::default(); let prefix_items = self.prefix.as_ref().unwrap_or(&default_prefix); From 46eeec42af498dc1de493a878b5d7ae2e8905ec6 Mon Sep 17 00:00:00 2001 From: Igor Date: Sun, 13 Oct 2024 11:09:54 +0400 Subject: [PATCH 3/6] docs: add a note about `impl Future` --- brush-core/src/builtins.rs | 1 + brush-interactive/src/interactive_shell.rs | 1 + 2 files changed, 2 insertions(+) diff --git a/brush-core/src/builtins.rs b/brush-core/src/builtins.rs index a8b90769..ffc7c97f 100644 --- a/brush-core/src/builtins.rs +++ b/brush-core/src/builtins.rs @@ -208,6 +208,7 @@ pub trait Command: Parser { /// # Arguments /// /// * `context` - The context in which the command is being executed. + // NOTE: we use desugared async here because we need a Send marker fn execute( &self, context: commands::ExecutionContext<'_>, diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index 86361f11..84b0c7db 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -53,6 +53,7 @@ pub trait 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. + // NOTE: we use desugared async here because [async_fn_in_trait] "warning: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified" fn run_interactively(&mut self) -> impl std::future::Future> { async { // TODO: Consider finding a better place for this. From cca73cec7de2e6339990b56b6e572ae137ce3668 Mon Sep 17 00:00:00 2001 From: Igor Date: Sun, 13 Oct 2024 11:17:58 +0400 Subject: [PATCH 4/6] style: fix cargo fmt --- brush-core/src/builtins/alias.rs | 1 - brush-core/src/builtins/bg.rs | 1 - brush-core/src/builtins/break_.rs | 1 - brush-core/src/builtins/brushinfo.rs | 1 - brush-core/src/builtins/builtin_.rs | 1 - brush-core/src/builtins/cd.rs | 1 - brush-core/src/builtins/colon.rs | 1 - brush-core/src/builtins/command.rs | 1 - brush-core/src/builtins/complete.rs | 3 --- brush-core/src/builtins/continue_.rs | 1 - brush-core/src/builtins/declare.rs | 1 - brush-core/src/builtins/dirs.rs | 1 - brush-core/src/builtins/dot.rs | 1 - brush-core/src/builtins/echo.rs | 5 ++--- brush-core/src/builtins/enable.rs | 1 - brush-core/src/builtins/eval.rs | 1 - brush-core/src/builtins/exec.rs | 1 - brush-core/src/builtins/exit.rs | 1 - brush-core/src/builtins/export.rs | 1 - brush-core/src/builtins/false_.rs | 1 - brush-core/src/builtins/fg.rs | 1 - brush-core/src/builtins/getopts.rs | 1 - brush-core/src/builtins/help.rs | 1 - brush-core/src/builtins/jobs.rs | 1 - brush-core/src/builtins/kill.rs | 2 -- brush-core/src/builtins/let_.rs | 1 - brush-core/src/builtins/popd.rs | 2 -- brush-core/src/builtins/printf.rs | 1 - brush-core/src/builtins/pushd.rs | 2 -- brush-core/src/builtins/pwd.rs | 1 - brush-core/src/builtins/read.rs | 1 - brush-core/src/builtins/return_.rs | 1 - brush-core/src/builtins/set.rs | 1 - brush-core/src/builtins/shift.rs | 1 - brush-core/src/builtins/shopt.rs | 1 - brush-core/src/builtins/test.rs | 1 - brush-core/src/builtins/trap.rs | 1 - brush-core/src/builtins/true_.rs | 1 - brush-core/src/builtins/type_.rs | 1 - brush-core/src/builtins/umask.rs | 1 - brush-core/src/builtins/unalias.rs | 1 - brush-core/src/builtins/unimp.rs | 1 - brush-core/src/builtins/unset.rs | 1 - brush-core/src/builtins/wait.rs | 1 - brush-interactive/src/interactive_shell.rs | 3 ++- 45 files changed, 4 insertions(+), 52 deletions(-) diff --git a/brush-core/src/builtins/alias.rs b/brush-core/src/builtins/alias.rs index 99177ff3..7b9f7c25 100644 --- a/brush-core/src/builtins/alias.rs +++ b/brush-core/src/builtins/alias.rs @@ -15,7 +15,6 @@ pub(crate) struct AliasCommand { aliases: Vec, } - impl builtins::Command for AliasCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/bg.rs b/brush-core/src/builtins/bg.rs index 98d7d7bd..7faca92b 100644 --- a/brush-core/src/builtins/bg.rs +++ b/brush-core/src/builtins/bg.rs @@ -10,7 +10,6 @@ pub(crate) struct BgCommand { job_specs: Vec, } - impl builtins::Command for BgCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/break_.rs b/brush-core/src/builtins/break_.rs index 83cdbf1e..246c15b2 100644 --- a/brush-core/src/builtins/break_.rs +++ b/brush-core/src/builtins/break_.rs @@ -10,7 +10,6 @@ pub(crate) struct BreakCommand { which_loop: i8, } - impl builtins::Command for BreakCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/brushinfo.rs b/brush-core/src/builtins/brushinfo.rs index d7d8a9d8..9db907c0 100644 --- a/brush-core/src/builtins/brushinfo.rs +++ b/brush-core/src/builtins/brushinfo.rs @@ -53,7 +53,6 @@ enum CompleteCommand { }, } - impl builtins::Command for BrushInfoCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/builtin_.rs b/brush-core/src/builtins/builtin_.rs index be522627..a6fbd425 100644 --- a/brush-core/src/builtins/builtin_.rs +++ b/brush-core/src/builtins/builtin_.rs @@ -14,7 +14,6 @@ pub(crate) struct BuiltinCommand { args: Vec, } - impl builtins::Command for BuiltinCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/cd.rs b/brush-core/src/builtins/cd.rs index b505f5c9..63a67d98 100644 --- a/brush-core/src/builtins/cd.rs +++ b/brush-core/src/builtins/cd.rs @@ -28,7 +28,6 @@ pub(crate) struct CdCommand { target_dir: Option, } - impl builtins::Command for CdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/colon.rs b/brush-core/src/builtins/colon.rs index 85662e4a..371d1ed0 100644 --- a/brush-core/src/builtins/colon.rs +++ b/brush-core/src/builtins/colon.rs @@ -10,7 +10,6 @@ pub(crate) struct ColonCommand { args: Vec, } - impl builtins::Command for ColonCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/command.rs b/brush-core/src/builtins/command.rs index 990445e0..eb6ed8b6 100644 --- a/brush-core/src/builtins/command.rs +++ b/brush-core/src/builtins/command.rs @@ -26,7 +26,6 @@ pub(crate) struct CommandCommand { args: Vec, } - impl builtins::Command for CommandCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/complete.rs b/brush-core/src/builtins/complete.rs index 1fca5b45..dcbf23cd 100644 --- a/brush-core/src/builtins/complete.rs +++ b/brush-core/src/builtins/complete.rs @@ -210,7 +210,6 @@ pub(crate) struct CompleteCommand { names: Vec, } - impl builtins::Command for CompleteCommand { async fn execute( &self, @@ -435,7 +434,6 @@ pub(crate) struct CompGenCommand { word: Option, } - impl builtins::Command for CompGenCommand { async fn execute( &self, @@ -514,7 +512,6 @@ pub(crate) struct CompOptCommand { names: Vec, } - impl builtins::Command for CompOptCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/continue_.rs b/brush-core/src/builtins/continue_.rs index 673d0c36..79631189 100644 --- a/brush-core/src/builtins/continue_.rs +++ b/brush-core/src/builtins/continue_.rs @@ -10,7 +10,6 @@ pub(crate) struct ContinueCommand { which_loop: i8, } - impl builtins::Command for ContinueCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/declare.rs b/brush-core/src/builtins/declare.rs index 82e13378..15153381 100644 --- a/brush-core/src/builtins/declare.rs +++ b/brush-core/src/builtins/declare.rs @@ -110,7 +110,6 @@ impl builtins::DeclarationCommand for DeclareCommand { } #[allow(clippy::too_many_lines)] - impl builtins::Command for DeclareCommand { fn takes_plus_options() -> bool { true diff --git a/brush-core/src/builtins/dirs.rs b/brush-core/src/builtins/dirs.rs index 4e871263..9024f72c 100644 --- a/brush-core/src/builtins/dirs.rs +++ b/brush-core/src/builtins/dirs.rs @@ -25,7 +25,6 @@ pub(crate) struct DirsCommand { // TODO: implement +N and -N } - impl builtins::Command for DirsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/dot.rs b/brush-core/src/builtins/dot.rs index 2069ab34..34b0dac2 100644 --- a/brush-core/src/builtins/dot.rs +++ b/brush-core/src/builtins/dot.rs @@ -15,7 +15,6 @@ pub(crate) struct DotCommand { pub script_args: Vec, } - impl builtins::Command for DotCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/echo.rs b/brush-core/src/builtins/echo.rs index a98a2fbd..a4df5b7d 100644 --- a/brush-core/src/builtins/echo.rs +++ b/brush-core/src/builtins/echo.rs @@ -24,10 +24,9 @@ pub(crate) struct EchoCommand { args: Vec, } - impl builtins::Command for EchoCommand { - /// Override the default [builtins::Command::new] function to handle clap's limitation related to `--`. - /// See [crate::builtins::parse_known] for more information + /// Override the default [builtins::Command::new] function to handle clap's limitation related + /// to `--`. See [crate::builtins::parse_known] for more information /// TODO: we can safely remove this after the issue is resolved fn new(args: I) -> Result where diff --git a/brush-core/src/builtins/enable.rs b/brush-core/src/builtins/enable.rs index ada10a47..6bc0da63 100644 --- a/brush-core/src/builtins/enable.rs +++ b/brush-core/src/builtins/enable.rs @@ -37,7 +37,6 @@ pub(crate) struct EnableCommand { names: Vec, } - impl builtins::Command for EnableCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/eval.rs b/brush-core/src/builtins/eval.rs index fc653dac..c970a323 100644 --- a/brush-core/src/builtins/eval.rs +++ b/brush-core/src/builtins/eval.rs @@ -9,7 +9,6 @@ pub(crate) struct EvalCommand { pub args: Vec, } - impl builtins::Command for EvalCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/exec.rs b/brush-core/src/builtins/exec.rs index e9ca0b81..0137e99d 100644 --- a/brush-core/src/builtins/exec.rs +++ b/brush-core/src/builtins/exec.rs @@ -23,7 +23,6 @@ pub(crate) struct ExecCommand { args: Vec, } - impl builtins::Command for ExecCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/exit.rs b/brush-core/src/builtins/exit.rs index 58b853b6..9c5b9d02 100644 --- a/brush-core/src/builtins/exit.rs +++ b/brush-core/src/builtins/exit.rs @@ -9,7 +9,6 @@ pub(crate) struct ExitCommand { code: Option, } - impl builtins::Command for ExitCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/export.rs b/brush-core/src/builtins/export.rs index 2211a619..19541f4a 100644 --- a/brush-core/src/builtins/export.rs +++ b/brush-core/src/builtins/export.rs @@ -37,7 +37,6 @@ impl builtins::DeclarationCommand for ExportCommand { } } - impl builtins::Command for ExportCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/false_.rs b/brush-core/src/builtins/false_.rs index 73bdbdee..a2be7aa3 100644 --- a/brush-core/src/builtins/false_.rs +++ b/brush-core/src/builtins/false_.rs @@ -6,7 +6,6 @@ use crate::{builtins, commands}; #[derive(Parser)] pub(crate) struct FalseCommand {} - impl builtins::Command for FalseCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/fg.rs b/brush-core/src/builtins/fg.rs index 6fedd30a..231d38c8 100644 --- a/brush-core/src/builtins/fg.rs +++ b/brush-core/src/builtins/fg.rs @@ -10,7 +10,6 @@ pub(crate) struct FgCommand { job_spec: Option, } - impl builtins::Command for FgCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/getopts.rs b/brush-core/src/builtins/getopts.rs index 9beaca4a..3d971604 100644 --- a/brush-core/src/builtins/getopts.rs +++ b/brush-core/src/builtins/getopts.rs @@ -18,7 +18,6 @@ pub(crate) struct GetOptsCommand { args: Vec, } - impl builtins::Command for GetOptsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/help.rs b/brush-core/src/builtins/help.rs index 93fb2b6c..32d0fdef 100644 --- a/brush-core/src/builtins/help.rs +++ b/brush-core/src/builtins/help.rs @@ -22,7 +22,6 @@ pub(crate) struct HelpCommand { topic_patterns: Vec, } - impl builtins::Command for HelpCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/jobs.rs b/brush-core/src/builtins/jobs.rs index da25fd9a..b0ef597c 100644 --- a/brush-core/src/builtins/jobs.rs +++ b/brush-core/src/builtins/jobs.rs @@ -31,7 +31,6 @@ pub(crate) struct JobsCommand { job_specs: Vec, } - impl builtins::Command for JobsCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/kill.rs b/brush-core/src/builtins/kill.rs index d4f0883c..61887102 100644 --- a/brush-core/src/builtins/kill.rs +++ b/brush-core/src/builtins/kill.rs @@ -16,7 +16,6 @@ pub(crate) struct KillCommand { // // TODO: implement -sigspec syntax - // /// List known signal names. #[arg(short = 'l', short_alias = 'L')] list_signals: bool, @@ -25,7 +24,6 @@ pub(crate) struct KillCommand { args: Vec, } - impl builtins::Command for KillCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/let_.rs b/brush-core/src/builtins/let_.rs index 9ac8f682..9f2cb4ae 100644 --- a/brush-core/src/builtins/let_.rs +++ b/brush-core/src/builtins/let_.rs @@ -11,7 +11,6 @@ pub(crate) struct LetCommand { exprs: Vec, } - impl builtins::Command for LetCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/popd.rs b/brush-core/src/builtins/popd.rs index fc7daf58..d525febe 100644 --- a/brush-core/src/builtins/popd.rs +++ b/brush-core/src/builtins/popd.rs @@ -11,10 +11,8 @@ pub(crate) struct PopdCommand { no_directory_change: bool, // // TODO: implement +N and -N - // } - impl builtins::Command for PopdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/printf.rs b/brush-core/src/builtins/printf.rs index 5cad9a90..a5390999 100644 --- a/brush-core/src/builtins/printf.rs +++ b/brush-core/src/builtins/printf.rs @@ -16,7 +16,6 @@ pub(crate) struct PrintfCommand { format_and_args: Vec, } - impl builtins::Command for PrintfCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/pushd.rs b/brush-core/src/builtins/pushd.rs index 69beeb9d..595dedd6 100644 --- a/brush-core/src/builtins/pushd.rs +++ b/brush-core/src/builtins/pushd.rs @@ -13,10 +13,8 @@ pub(crate) struct PushdCommand { dir: String, // // TODO: implement +N and -N - // } - impl builtins::Command for PushdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/pwd.rs b/brush-core/src/builtins/pwd.rs index c3ad8f9f..8ad06db9 100644 --- a/brush-core/src/builtins/pwd.rs +++ b/brush-core/src/builtins/pwd.rs @@ -14,7 +14,6 @@ pub(crate) struct PwdCommand { allow_symlinks: bool, } - impl builtins::Command for PwdCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/read.rs b/brush-core/src/builtins/read.rs index 05484dbd..ab70f84e 100644 --- a/brush-core/src/builtins/read.rs +++ b/brush-core/src/builtins/read.rs @@ -59,7 +59,6 @@ pub(crate) struct ReadCommand { variable_names: Vec, } - impl builtins::Command for ReadCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/return_.rs b/brush-core/src/builtins/return_.rs index 7483e5d8..f52e3d45 100644 --- a/brush-core/src/builtins/return_.rs +++ b/brush-core/src/builtins/return_.rs @@ -9,7 +9,6 @@ pub(crate) struct ReturnCommand { code: Option, } - impl builtins::Command for ReturnCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/set.rs b/brush-core/src/builtins/set.rs index ce7b16e5..9fa64485 100644 --- a/brush-core/src/builtins/set.rs +++ b/brush-core/src/builtins/set.rs @@ -130,7 +130,6 @@ pub(crate) struct SetCommand { positional_args: Vec, } - impl builtins::Command for SetCommand { fn takes_plus_options() -> bool { true diff --git a/brush-core/src/builtins/shift.rs b/brush-core/src/builtins/shift.rs index f641ba06..df38628a 100644 --- a/brush-core/src/builtins/shift.rs +++ b/brush-core/src/builtins/shift.rs @@ -9,7 +9,6 @@ pub(crate) struct ShiftCommand { n: Option, } - impl builtins::Command for ShiftCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/shopt.rs b/brush-core/src/builtins/shopt.rs index cd78faa5..d1604b2e 100644 --- a/brush-core/src/builtins/shopt.rs +++ b/brush-core/src/builtins/shopt.rs @@ -31,7 +31,6 @@ pub(crate) struct ShoptCommand { options: Vec, } - impl builtins::Command for ShoptCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/test.rs b/brush-core/src/builtins/test.rs index 1f238a4b..532552c5 100644 --- a/brush-core/src/builtins/test.rs +++ b/brush-core/src/builtins/test.rs @@ -11,7 +11,6 @@ pub(crate) struct TestCommand { args: Vec, } - impl builtins::Command for TestCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/trap.rs b/brush-core/src/builtins/trap.rs index 768c35b8..1a9cc71f 100644 --- a/brush-core/src/builtins/trap.rs +++ b/brush-core/src/builtins/trap.rs @@ -17,7 +17,6 @@ pub(crate) struct TrapCommand { args: Vec, } - impl builtins::Command for TrapCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/true_.rs b/brush-core/src/builtins/true_.rs index fb4be607..c70c34c9 100644 --- a/brush-core/src/builtins/true_.rs +++ b/brush-core/src/builtins/true_.rs @@ -6,7 +6,6 @@ use crate::{builtins, commands}; #[derive(Parser)] pub(crate) struct TrueCommand {} - impl builtins::Command for TrueCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/type_.rs b/brush-core/src/builtins/type_.rs index 7d30b853..87239811 100644 --- a/brush-core/src/builtins/type_.rs +++ b/brush-core/src/builtins/type_.rs @@ -44,7 +44,6 @@ enum ResolvedType { File(PathBuf), } - impl builtins::Command for TypeCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/umask.rs b/brush-core/src/builtins/umask.rs index 83cee0d1..8a2ce743 100644 --- a/brush-core/src/builtins/umask.rs +++ b/brush-core/src/builtins/umask.rs @@ -20,7 +20,6 @@ pub(crate) struct UmaskCommand { mode: Option, } - impl builtins::Command for UmaskCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unalias.rs b/brush-core/src/builtins/unalias.rs index 16aeb7f2..50992a91 100644 --- a/brush-core/src/builtins/unalias.rs +++ b/brush-core/src/builtins/unalias.rs @@ -14,7 +14,6 @@ pub(crate) struct UnaliasCommand { aliases: Vec, } - impl builtins::Command for UnaliasCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unimp.rs b/brush-core/src/builtins/unimp.rs index 3ffb2226..db60c69c 100644 --- a/brush-core/src/builtins/unimp.rs +++ b/brush-core/src/builtins/unimp.rs @@ -13,7 +13,6 @@ pub(crate) struct UnimplementedCommand { declarations: Vec, } - impl builtins::Command for UnimplementedCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/unset.rs b/brush-core/src/builtins/unset.rs index cc0fcd8b..94ac0052 100644 --- a/brush-core/src/builtins/unset.rs +++ b/brush-core/src/builtins/unset.rs @@ -34,7 +34,6 @@ impl UnsetNameInterpretation { } } - impl builtins::Command for UnsetCommand { async fn execute( &self, diff --git a/brush-core/src/builtins/wait.rs b/brush-core/src/builtins/wait.rs index fa254a42..c8dbf0b1 100644 --- a/brush-core/src/builtins/wait.rs +++ b/brush-core/src/builtins/wait.rs @@ -22,7 +22,6 @@ pub(crate) struct WaitCommand { job_specs: Vec, } - impl builtins::Command for WaitCommand { async fn execute( &self, diff --git a/brush-interactive/src/interactive_shell.rs b/brush-interactive/src/interactive_shell.rs index 84b0c7db..914c09a4 100644 --- a/brush-interactive/src/interactive_shell.rs +++ b/brush-interactive/src/interactive_shell.rs @@ -53,7 +53,8 @@ pub trait 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. - // NOTE: we use desugared async here because [async_fn_in_trait] "warning: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified" + // NOTE: we use desugared async here because [async_fn_in_trait] "warning: use of `async fn` in + // public traits is discouraged as auto trait bounds cannot be specified" fn run_interactively(&mut self) -> impl std::future::Future> { async { // TODO: Consider finding a better place for this. From ef8094c745507801fc2b535b1b1e1f55c386e95d Mon Sep 17 00:00:00 2001 From: Igor Date: Sun, 13 Oct 2024 12:00:41 +0400 Subject: [PATCH 5/6] style: cargo fmt again --- brush-core/src/builtins.rs | 10 +++++----- brush-core/src/error.rs | 1 - brush-core/src/variables.rs | 20 ++++++++++--------- brush-interactive/src/reedline/highlighter.rs | 3 ++- brush-shell/src/brushctl.rs | 2 -- brush-shell/src/shell_factory.rs | 4 ---- 6 files changed, 18 insertions(+), 22 deletions(-) diff --git a/brush-core/src/builtins.rs b/brush-core/src/builtins.rs index ffc7c97f..7ba09126 100644 --- a/brush-core/src/builtins.rs +++ b/brush-core/src/builtins.rs @@ -396,8 +396,7 @@ fn brush_help_styles() -> clap::builder::Styles { /// # Returns /// /// * a parsed struct T from [`clap::Parser::parse_from`] -/// * the remain iterator `args` with `--` and the rest arguments if they present -/// othervise None +/// * the remain iterator `args` with `--` and the rest arguments if they present othervise None /// /// # Examples /// ``` @@ -421,8 +420,8 @@ where S: Into + Clone + PartialEq<&'static str>, { let mut args = args.into_iter(); - // the best way to save `--` is to get it out with a side effect while `clap` iterates over the args - // this way we can be 100% sure that we have '--' and the remaining args + // the best way to save `--` is to get it out with a side effect while `clap` iterates over the + // args this way we can be 100% sure that we have '--' and the remaining args // and we will iterate only once let mut hyphen = None; let args_before_hyphen = args.by_ref().take_while(|a| { @@ -438,7 +437,8 @@ where } /// Similar to [`parse_known`] but with [`clap::Parser::try_parse_from`] -/// This function is used to parse arguments in builtins such as [`crate::builtins::echo::EchoCommand`] +/// This function is used to parse arguments in builtins such as +/// [`crate::builtins::echo::EchoCommand`] pub fn try_parse_known( args: impl IntoIterator, ) -> Result<(T, Option>), clap::Error> { diff --git a/brush-core/src/error.rs b/brush-core/src/error.rs index dcbce45f..a99c25d3 100644 --- a/brush-core/src/error.rs +++ b/brush-core/src/error.rs @@ -190,4 +190,3 @@ pub enum Error { pub(crate) fn unimp(msg: &'static str) -> Result { Err(Error::Unimplemented(msg)) } - diff --git a/brush-core/src/variables.rs b/brush-core/src/variables.rs index ec3848ff..1d5470c3 100644 --- a/brush-core/src/variables.rs +++ b/brush-core/src/variables.rs @@ -215,8 +215,8 @@ impl ShellVariable { if append { match (&self.value, &value) { - // If we're appending an array to a declared-but-unset variable (or appending anything to a declared-but-unset array), - // then fill it out first. + // If we're appending an array to a declared-but-unset variable (or appending + // anything to a declared-but-unset array), then fill it out first. (ShellValue::Unset(_), ShellValueLiteral::Array(_)) | ( ShellValue::Unset( @@ -226,8 +226,8 @@ impl ShellVariable { ) => { self.assign(ShellValueLiteral::Array(ArrayLiteral(vec![])), false)?; } - // If we're trying to append an array to a string, we first promote the string to be an array - // with the string being present at index 0. + // If we're trying to append an array to a string, we first promote the string to be + // an array with the string being present at index 0. (ShellValue::String(_), ShellValueLiteral::Array(_)) => { self.convert_to_indexed_array()?; } @@ -289,9 +289,9 @@ impl ShellVariable { ShellValueLiteral::Scalar(s), ) => self.assign_at_index(String::from("0"), s, false), - // If we're updating an indexed array value with an array, then preserve the array type. - // We also default to using an indexed array if we are assigning an array to a previously - // string-holding variable. + // If we're updating an indexed array value with an array, then preserve the array + // type. We also default to using an indexed array if we are + // assigning an array to a previously string-holding variable. ( ShellValue::IndexedArray(_) | ShellValue::Unset( @@ -305,7 +305,8 @@ impl ShellVariable { Ok(()) } - // If we're updating an associative array value with an array, then preserve the array type. + // If we're updating an associative array value with an array, then preserve the + // array type. ( ShellValue::AssociativeArray(_) | ShellValue::Unset(ShellValueUnsetType::AssociativeArray), @@ -334,7 +335,8 @@ impl ShellVariable { /// /// * `array_index` - The index at which to assign the value. /// * `value` - The value to assign to the variable at the given index. - /// * `append` - Whether or not to append the value to the preexisting value stored at the given index. + /// * `append` - Whether or not to append the value to the preexisting value stored at the given + /// index. #[allow(clippy::needless_pass_by_value)] pub fn assign_at_index( &mut self, diff --git a/brush-interactive/src/reedline/highlighter.rs b/brush-interactive/src/reedline/highlighter.rs index 5f9036f9..6b2f04d9 100644 --- a/brush-interactive/src/reedline/highlighter.rs +++ b/brush-interactive/src/reedline/highlighter.rs @@ -244,7 +244,8 @@ impl<'a> StyledInputLine<'a> { } fn skip_ahead(&mut self, dest: usize) { - // Append a no-op style to make sure we cover any trailing gaps in the input line not otherwise styled. + // Append a no-op style to make sure we cover any trailing gaps in the input line not + // otherwise styled. self.append_style(Style::new(), dest, dest); } diff --git a/brush-shell/src/brushctl.rs b/brush-shell/src/brushctl.rs index e03fc9ee..07627d98 100644 --- a/brush-shell/src/brushctl.rs +++ b/brush-shell/src/brushctl.rs @@ -42,7 +42,6 @@ enum EventsCommand { }, } - impl brush_core::builtins::Command for BrushCtlCommand { async fn execute( &self, @@ -54,7 +53,6 @@ impl brush_core::builtins::Command for BrushCtlCommand { } } - impl EventsCommand { fn execute( &self, diff --git a/brush-shell/src/shell_factory.rs b/brush-shell/src/shell_factory.rs index e5c034c7..58d70c04 100644 --- a/brush-shell/src/shell_factory.rs +++ b/brush-shell/src/shell_factory.rs @@ -1,4 +1,3 @@ - pub(crate) trait ShellFactory { type ShellType: brush_interactive::InteractiveShell + Send; @@ -53,7 +52,6 @@ impl AsMut for StubShell { pub(crate) struct RustylineShellFactory; - impl ShellFactory for RustylineShellFactory { #[cfg(all(feature = "rustyline", any(windows, unix)))] type ShellType = brush_interactive::RustylineShell; @@ -78,7 +76,6 @@ impl ShellFactory for RustylineShellFactory { pub(crate) struct ReedlineShellFactory; - impl ShellFactory for ReedlineShellFactory { #[cfg(all(feature = "reedline", any(windows, unix)))] type ShellType = brush_interactive::ReedlineShell; @@ -103,7 +100,6 @@ impl ShellFactory for ReedlineShellFactory { pub(crate) struct BasicShellFactory; - impl ShellFactory for BasicShellFactory { #[cfg(feature = "basic")] type ShellType = brush_interactive::BasicShell; From 17e09dc953b878b7437ff4c37593186034d5b26a Mon Sep 17 00:00:00 2001 From: Igor Date: Sun, 13 Oct 2024 12:11:56 +0400 Subject: [PATCH 6/6] fix: clippy lints please --- brush-core/src/builtins/echo.rs | 6 +++--- brush-core/src/builtins/kill.rs | 2 +- brush-core/src/builtins/printf.rs | 2 +- brush-core/src/builtins/umask.rs | 11 ++++++----- brush-core/src/interp.rs | 22 +++++++++------------- 5 files changed, 20 insertions(+), 23 deletions(-) diff --git a/brush-core/src/builtins/echo.rs b/brush-core/src/builtins/echo.rs index a4df5b7d..d3f08d46 100644 --- a/brush-core/src/builtins/echo.rs +++ b/brush-core/src/builtins/echo.rs @@ -25,8 +25,8 @@ pub(crate) struct EchoCommand { } impl builtins::Command for EchoCommand { - /// Override the default [builtins::Command::new] function to handle clap's limitation related - /// to `--`. See [crate::builtins::parse_known] for more information + /// Override the default [`builtins::Command::new`] function to handle clap's limitation related + /// to `--`. See [`builtins::parse_known`] for more information /// TODO: we can safely remove this after the issue is resolved fn new(args: I) -> Result where @@ -74,6 +74,6 @@ impl builtins::Command for EchoCommand { write!(context.stdout(), "{s}")?; context.stdout().flush()?; - return Ok(builtins::ExitCode::Success); + Ok(builtins::ExitCode::Success) } } diff --git a/brush-core/src/builtins/kill.rs b/brush-core/src/builtins/kill.rs index 61887102..f94b978f 100644 --- a/brush-core/src/builtins/kill.rs +++ b/brush-core/src/builtins/kill.rs @@ -37,7 +37,7 @@ impl builtins::Command for KillCommand { } if self.list_signals { - return error::unimp("kill -l"); + error::unimp("kill -l") } else { if self.args.len() != 1 { writeln!(context.stderr(), "{}: invalid usage", context.command_name)?; diff --git a/brush-core/src/builtins/printf.rs b/brush-core/src/builtins/printf.rs index a5390999..248d24ac 100644 --- a/brush-core/src/builtins/printf.rs +++ b/brush-core/src/builtins/printf.rs @@ -30,7 +30,7 @@ impl builtins::Command for PrintfCommand { context.stdout().flush()?; } - return Ok(builtins::ExitCode::Success); + Ok(builtins::ExitCode::Success) } } diff --git a/brush-core/src/builtins/umask.rs b/brush-core/src/builtins/umask.rs index 8a2ce743..3b8c45df 100644 --- a/brush-core/src/builtins/umask.rs +++ b/brush-core/src/builtins/umask.rs @@ -27,7 +27,7 @@ impl builtins::Command for UmaskCommand { ) -> 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)?; + let parsed = nix::sys::stat::mode_t::from_str_radix(mode.as_str(), 8)?; set_umask(parsed)?; } else { return crate::error::unimp("umask setting mode from symbolic value"); @@ -63,17 +63,18 @@ cfg_if! { status.umask.ok_or_else(|| error::Error::InvalidUmask) } } else { + #[allow(clippy::unnecessary_wraps)] fn get_umask() -> Result { let u = nix::sys::stat::umask(Mode::empty()); nix::sys::stat::umask(u); - Ok(u.bits() as u32) + Ok(u32::from(u.bits())) } } } -fn set_umask(value: u32) -> Result<(), error::Error> { - let mode = - nix::sys::stat::Mode::from_bits(value as _).ok_or_else(|| error::Error::InvalidUmask)?; +fn set_umask(value: nix::sys::stat::mode_t) -> Result<(), error::Error> { + // value of mode_t can be platform dependent + let mode = nix::sys::stat::Mode::from_bits(value).ok_or_else(|| error::Error::InvalidUmask)?; nix::sys::stat::umask(mode); Ok(()) } diff --git a/brush-core/src/interp.rs b/brush-core/src/interp.rs index 1b986b0b..bacdf0e6 100644 --- a/brush-core/src/interp.rs +++ b/brush-core/src/interp.rs @@ -1048,19 +1048,15 @@ async fn expand_assignment_value( ast::AssignmentValue::Array(arr) => { let mut expanded_values = vec![]; for (key, value) in arr { - match key { - Some(k) => { - let expanded_key = expansion::basic_expand_word(shell, k).await?.into(); - let expanded_value = - expansion::basic_expand_word(shell, value).await?.into(); - expanded_values.push((Some(expanded_key), expanded_value)); - } - None => { - let split_expanded_value = - expansion::full_expand_and_split_word(shell, value).await?; - for expanded_value in split_expanded_value { - expanded_values.push((None, expanded_value.into())); - } + if let Some(k) = key { + let expanded_key = expansion::basic_expand_word(shell, k).await?.into(); + let expanded_value = expansion::basic_expand_word(shell, value).await?.into(); + expanded_values.push((Some(expanded_key), expanded_value)); + } else { + let split_expanded_value = + expansion::full_expand_and_split_word(shell, value).await?; + for expanded_value in split_expanded_value { + expanded_values.push((None, expanded_value.into())); } } }