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