mod and_or;
mod compound_command;
mod function_definition;
mod item;
mod pipeline;
pub mod simple_command;
use crate::Runtime;
use crate::trap::run_traps_for_caught_signals;
use std::ops::ControlFlow::{Break, Continue};
use yash_env::Env;
use yash_env::semantics::Result;
#[doc(no_inline)]
pub use yash_env::semantics::command::search;
use yash_syntax::syntax;
pub trait Command<S> {
#[allow(async_fn_in_trait)] async fn execute(&self, env: &mut Env<S>) -> Result;
}
impl<S: Runtime + 'static> Command<S> for syntax::Command {
async fn execute(&self, env: &mut Env<S>) -> Result {
use syntax::Command::*;
let main_result = match self {
Simple(command) => command.execute(env).await,
Compound(command) => command.execute(env).await,
Function(definition) => definition.execute(env).await,
};
let trap_result = run_traps_for_caught_signals(env).await;
env.update_all_subshell_statuses();
match (main_result, trap_result) {
(_, Continue(())) => main_result,
(Continue(()), _) => trap_result,
(Break(main_divert), Break(trap_divert)) => Break(main_divert.max(trap_divert)),
}
}
}
impl<S: Runtime + 'static> Command<S> for syntax::List {
async fn execute(&self, env: &mut Env<S>) -> Result {
Box::pin(async move {
for item in &self.0 {
item.execute(env).await?
}
Continue(())
})
.await
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::echo_builtin;
use crate::tests::return_builtin;
use futures_util::FutureExt;
use yash_env::semantics::Divert;
use yash_env::semantics::ExitStatus;
use yash_env::system::r#virtual::SIGUSR1;
use yash_env::system::r#virtual::VirtualSystem;
use yash_env::test_helper::assert_stdout;
use yash_env::trap::Action;
use yash_syntax::source::Location;
#[test]
fn command_handles_traps() {
let system = VirtualSystem::new();
let mut env = Env::with_system(system.clone());
env.builtins.insert("echo", echo_builtin());
env.traps
.set_action(
&env.system,
SIGUSR1,
Action::Command("echo USR1".into()),
Location::dummy(""),
false,
)
.now_or_never()
.unwrap()
.unwrap();
let _ = system
.state
.borrow_mut()
.processes
.get_mut(&system.process_id)
.unwrap()
.raise_signal(SIGUSR1);
let command: syntax::Command = "echo main".parse().unwrap();
let result = command.execute(&mut env).now_or_never().unwrap();
assert_eq!(result, Continue(()));
assert_eq!(env.exit_status, ExitStatus::SUCCESS);
assert_stdout(&system.state, |stdout| assert_eq!(stdout, "main\nUSR1\n"));
}
#[test]
fn list_execute_no_divert() {
let mut env = Env::new_virtual();
env.builtins.insert("return", return_builtin());
let list: syntax::List = "return -n 1; return -n 2; return -n 4".parse().unwrap();
let result = list.execute(&mut env).now_or_never().unwrap();
assert_eq!(result, Continue(()));
assert_eq!(env.exit_status, ExitStatus(4));
}
#[test]
fn list_execute_divert() {
let mut env = Env::new_virtual();
env.builtins.insert("return", return_builtin());
let list: syntax::List = "return -n 1; return 2; return -n 4".parse().unwrap();
let result = list.execute(&mut env).now_or_never().unwrap();
assert_eq!(result, Break(Divert::Return(Some(ExitStatus(2)))));
assert_eq!(env.exit_status, ExitStatus(1));
}
}