yash_semantics/
command.rs1mod and_or;
20mod compound_command;
21mod function_definition;
22mod item;
23mod pipeline;
24pub mod simple_command;
25
26use crate::trap::run_traps_for_caught_signals;
27use std::ops::ControlFlow::{Break, Continue};
28use yash_env::Env;
29use yash_env::semantics::Result;
30use yash_syntax::syntax;
31
32pub trait Command {
34 #[allow(async_fn_in_trait)] async fn execute(&self, env: &mut Env) -> Result;
40}
41
42impl Command for syntax::Command {
48 async fn execute(&self, env: &mut Env) -> Result {
49 use syntax::Command::*;
50 let main_result = match self {
51 Simple(command) => command.execute(env).await,
52 Compound(command) => command.execute(env).await,
53 Function(definition) => definition.execute(env).await,
54 };
55
56 let trap_result = run_traps_for_caught_signals(env).await;
57 env.update_all_subshell_statuses();
58
59 match (main_result, trap_result) {
60 (_, Continue(())) => main_result,
61 (Continue(()), _) => trap_result,
62 (Break(main_divert), Break(trap_divert)) => Break(main_divert.max(trap_divert)),
63 }
64 }
65}
66
67impl Command for syntax::List {
73 async fn execute(&self, env: &mut Env) -> Result {
74 Box::pin(async move {
76 for item in &self.0 {
77 item.execute(env).await?
78 }
79 Continue(())
80 })
81 .await
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::tests::echo_builtin;
89 use crate::tests::return_builtin;
90 use futures_util::FutureExt;
91 use yash_env::semantics::Divert;
92 use yash_env::semantics::ExitStatus;
93 use yash_env::system::r#virtual::SIGUSR1;
94 use yash_env::system::r#virtual::VirtualSystem;
95 use yash_env::trap::Action;
96 use yash_env_test_helper::assert_stdout;
97 use yash_syntax::source::Location;
98
99 #[test]
100 fn command_handles_traps() {
101 let system = VirtualSystem::new();
102 let mut env = Env::with_system(Box::new(system.clone()));
103 env.builtins.insert("echo", echo_builtin());
104 env.traps
105 .set_action(
106 &mut env.system,
107 SIGUSR1,
108 Action::Command("echo USR1".into()),
109 Location::dummy(""),
110 false,
111 )
112 .unwrap();
113 let _ = system
114 .state
115 .borrow_mut()
116 .processes
117 .get_mut(&system.process_id)
118 .unwrap()
119 .raise_signal(SIGUSR1);
120
121 let command: syntax::Command = "echo main".parse().unwrap();
122 let result = command.execute(&mut env).now_or_never().unwrap();
123 assert_eq!(result, Continue(()));
124 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
125
126 assert_stdout(&system.state, |stdout| assert_eq!(stdout, "main\nUSR1\n"));
127 }
128
129 #[test]
130 fn list_execute_no_divert() {
131 let mut env = Env::new_virtual();
132 env.builtins.insert("return", return_builtin());
133 let list: syntax::List = "return -n 1; return -n 2; return -n 4".parse().unwrap();
134 let result = list.execute(&mut env).now_or_never().unwrap();
135 assert_eq!(result, Continue(()));
136 assert_eq!(env.exit_status, ExitStatus(4));
137 }
138
139 #[test]
140 fn list_execute_divert() {
141 let mut env = Env::new_virtual();
142 env.builtins.insert("return", return_builtin());
143 let list: syntax::List = "return -n 1; return 2; return -n 4".parse().unwrap();
144 let result = list.execute(&mut env).now_or_never().unwrap();
145 assert_eq!(result, Break(Divert::Return(Some(ExitStatus(2)))));
146 assert_eq!(env.exit_status, ExitStatus(1));
147 }
148}