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