From 97edf61e3f05acb08114de1b11e21c1af1df905a Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Tue, 10 Sep 2024 11:18:40 -0400 Subject: [PATCH 1/4] start implementing source command --- crates/shell/src/commands.rs | 31 ++++++++++++++++++++-- crates/shell/src/execute.rs | 39 ++++++++++++++++++++++++++++ crates/shell/src/main.rs | 50 ++++++------------------------------ 3 files changed, 76 insertions(+), 44 deletions(-) create mode 100644 crates/shell/src/execute.rs diff --git a/crates/shell/src/commands.rs b/crates/shell/src/commands.rs index 66c8f24..da56047 100644 --- a/crates/shell/src/commands.rs +++ b/crates/shell/src/commands.rs @@ -1,15 +1,19 @@ -use std::ffi::OsString; +use std::{ffi::OsString, fs}; use deno_task_shell::{EnvChange, ExecuteResult, ShellCommand, ShellCommandContext}; -use futures::future::LocalBoxFuture; +use futures::{future::LocalBoxFuture, FutureExt}; use uu_ls::uumain as uu_ls; + +use crate::execute; pub struct LsCommand; pub struct AliasCommand; pub struct UnAliasCommand; +pub struct SourceCommand; + impl ShellCommand for AliasCommand { fn execute(&self, context: ShellCommandContext) -> LocalBoxFuture<'static, ExecuteResult> { if context.args.len() != 1 { @@ -61,3 +65,26 @@ fn execute_ls(context: ShellCommandContext) -> ExecuteResult { let exit_code = uu_ls(args.into_iter()); ExecuteResult::from_exit_code(exit_code) } + +impl ShellCommand for SourceCommand { + fn execute(&self, context: ShellCommandContext) -> LocalBoxFuture<'static, ExecuteResult> { + if context.args.len() != 1 { + return Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1))); + } + + let script = context.args[0].clone(); + // read the script + let script_file = context.state.cwd().join(script); + if script_file.exists() { + // TODO turn into execute result + let content = fs::read_to_string(script_file).unwrap(); + let mut state = context.state.clone(); + async move { + execute::execute(&content, &mut state).await.unwrap(); + ExecuteResult::from_exit_code(0) + }.boxed_local() + } + + Box::pin(futures::future::ready(ExecuteResult::from_exit_code(0))) + } +} diff --git a/crates/shell/src/execute.rs b/crates/shell/src/execute.rs new file mode 100644 index 0000000..0183fa1 --- /dev/null +++ b/crates/shell/src/execute.rs @@ -0,0 +1,39 @@ +use anyhow::Context; +use deno_task_shell::{ + execute_sequential_list, AsyncCommandBehavior, ExecuteResult, ShellPipeReader, ShellPipeWriter, + ShellState, +}; + +pub async fn execute(text: &str, state: &mut ShellState) -> anyhow::Result { + let list = deno_task_shell::parser::parse(text); + + let mut stderr = ShellPipeWriter::stderr(); + let stdout = ShellPipeWriter::stdout(); + let stdin = ShellPipeReader::stdin(); + + if let Err(e) = list { + let _ = stderr.write_line(&format!("Syntax error: {}", e)); + return Ok(1); + } + + // spawn a sequential list and pipe its output to the environment + let result = execute_sequential_list( + list.unwrap(), + state.clone(), + stdin, + stdout, + stderr, + AsyncCommandBehavior::Wait, + ) + .await; + + match result { + ExecuteResult::Continue(exit_code, changes, _) => { + // set CWD to the last command's CWD + state.apply_changes(&changes); + std::env::set_current_dir(state.cwd()).context("Failed to set CWD")?; + Ok(exit_code) + } + ExecuteResult::Exit(_, _) => Ok(0), + } +} diff --git a/crates/shell/src/main.rs b/crates/shell/src/main.rs index cdb2e69..95bf422 100644 --- a/crates/shell/src/main.rs +++ b/crates/shell/src/main.rs @@ -5,17 +5,17 @@ use std::rc::Rc; use anyhow::Context; use clap::Parser; use deno_task_shell::parser::debug_parse; -use deno_task_shell::{ - execute_sequential_list, AsyncCommandBehavior, ExecuteResult, ShellCommand, ShellPipeReader, - ShellPipeWriter, ShellState, -}; +use deno_task_shell::{ShellCommand, ShellState}; use rustyline::error::ReadlineError; use rustyline::{CompletionType, Config, Editor}; mod commands; mod completion; +mod execute; mod helper; +pub use execute::execute; + fn commands() -> HashMap> { HashMap::from([ ( @@ -30,43 +30,13 @@ fn commands() -> HashMap> { "unalias".to_string(), Rc::new(commands::AliasCommand) as Rc, ), + ( + "source".to_string(), + Rc::new(commands::SourceCommand) as Rc, + ), ]) } -async fn execute(text: &str, state: &mut ShellState) -> anyhow::Result { - let list = deno_task_shell::parser::parse(text); - - let mut stderr = ShellPipeWriter::stderr(); - let stdout = ShellPipeWriter::stdout(); - let stdin = ShellPipeReader::stdin(); - - if let Err(e) = list { - let _ = stderr.write_line(&format!("Syntax error: {}", e)); - return Ok(1); - } - - // spawn a sequential list and pipe its output to the environment - let result = execute_sequential_list( - list.unwrap(), - state.clone(), - stdin, - stdout, - stderr, - AsyncCommandBehavior::Wait, - ) - .await; - - match result { - ExecuteResult::Continue(exit_code, changes, _) => { - // set CWD to the last command's CWD - state.apply_changes(&changes); - std::env::set_current_dir(state.cwd()).context("Failed to set CWD")?; - Ok(exit_code) - } - ExecuteResult::Exit(_, _) => Ok(0), - } -} - #[derive(Parser)] struct Options { /// The path to the file that should be executed @@ -93,10 +63,6 @@ async fn interactive() -> anyhow::Result<()> { let helper = helper::ShellPromptHelper::default(); rl.set_helper(Some(helper)); - // let h = ShellCompleter {}; - - // rl.set_helper(Some(h)); - let mut state = init_state(); let home = dirs::home_dir().context("Couldn't get home directory")?; From 16ca6ab342ae4db0566cd500a22d7f423dd20bf5 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Tue, 10 Sep 2024 13:53:00 -0700 Subject: [PATCH 2/4] implement `source` command --- crates/shell/src/commands.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/shell/src/commands.rs b/crates/shell/src/commands.rs index da56047..0bd022b 100644 --- a/crates/shell/src/commands.rs +++ b/crates/shell/src/commands.rs @@ -79,6 +79,7 @@ impl ShellCommand for SourceCommand { // TODO turn into execute result let content = fs::read_to_string(script_file).unwrap(); let mut state = context.state.clone(); + async move { execute::execute(&content, &mut state).await.unwrap(); ExecuteResult::from_exit_code(0) From 4e56b0fb9ad5333441d174f57dcc09fb3494a920 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Tue, 10 Sep 2024 14:08:35 -0700 Subject: [PATCH 3/4] implement source --- crates/shell/src/commands.rs | 12 ++++-------- crates/shell/src/execute.rs | 15 ++++++++++----- 2 files changed, 14 insertions(+), 13 deletions(-) diff --git a/crates/shell/src/commands.rs b/crates/shell/src/commands.rs index 0bd022b..22c760a 100644 --- a/crates/shell/src/commands.rs +++ b/crates/shell/src/commands.rs @@ -78,14 +78,10 @@ impl ShellCommand for SourceCommand { if script_file.exists() { // TODO turn into execute result let content = fs::read_to_string(script_file).unwrap(); - let mut state = context.state.clone(); - - async move { - execute::execute(&content, &mut state).await.unwrap(); - ExecuteResult::from_exit_code(0) - }.boxed_local() + let state = context.state.clone(); + async move { execute::execute_inner(&content, state).await.unwrap() }.boxed_local() + } else { + Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1))) } - - Box::pin(futures::future::ready(ExecuteResult::from_exit_code(0))) } } diff --git a/crates/shell/src/execute.rs b/crates/shell/src/execute.rs index 0183fa1..6295992 100644 --- a/crates/shell/src/execute.rs +++ b/crates/shell/src/execute.rs @@ -4,22 +4,21 @@ use deno_task_shell::{ ShellState, }; -pub async fn execute(text: &str, state: &mut ShellState) -> anyhow::Result { +pub async fn execute_inner(text: &str, state: ShellState) -> anyhow::Result { let list = deno_task_shell::parser::parse(text); - let mut stderr = ShellPipeWriter::stderr(); + let stderr = ShellPipeWriter::stderr(); let stdout = ShellPipeWriter::stdout(); let stdin = ShellPipeReader::stdin(); if let Err(e) = list { - let _ = stderr.write_line(&format!("Syntax error: {}", e)); - return Ok(1); + anyhow::bail!("Syntax error: {}", e); } // spawn a sequential list and pipe its output to the environment let result = execute_sequential_list( list.unwrap(), - state.clone(), + state, stdin, stdout, stderr, @@ -27,6 +26,12 @@ pub async fn execute(text: &str, state: &mut ShellState) -> anyhow::Result ) .await; + Ok(result) +} + +pub async fn execute(text: &str, state: &mut ShellState) -> anyhow::Result { + let result = execute_inner(text, state.clone()).await?; + match result { ExecuteResult::Continue(exit_code, changes, _) => { // set CWD to the last command's CWD From 0ce9c0d64d47ccd6b44b3a7bfda5f204ce4fdfa6 Mon Sep 17 00:00:00 2001 From: Wolf Vollprecht Date: Tue, 10 Sep 2024 18:05:06 -0700 Subject: [PATCH 4/4] better errors --- crates/shell/src/commands.rs | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/crates/shell/src/commands.rs b/crates/shell/src/commands.rs index 22c760a..5304be2 100644 --- a/crates/shell/src/commands.rs +++ b/crates/shell/src/commands.rs @@ -73,15 +73,25 @@ impl ShellCommand for SourceCommand { } let script = context.args[0].clone(); - // read the script let script_file = context.state.cwd().join(script); - if script_file.exists() { - // TODO turn into execute result - let content = fs::read_to_string(script_file).unwrap(); - let state = context.state.clone(); - async move { execute::execute_inner(&content, state).await.unwrap() }.boxed_local() - } else { - Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1))) + match fs::read_to_string(&script_file) { + Ok(content) => { + let state = context.state.clone(); + async move { + execute::execute_inner(&content, state) + .await + .unwrap_or_else(|e| { + eprintln!("Could not source script: {:?}", script_file); + eprintln!("Error: {}", e); + ExecuteResult::from_exit_code(1) + }) + } + .boxed_local() + } + Err(e) => { + eprintln!("Could not read file: {:?} ({})", script_file, e); + Box::pin(futures::future::ready(ExecuteResult::from_exit_code(1))) + } } } }