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::test_helper::assert_stdout;
99 use yash_env::trap::Action;
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 &env.system,
110 SIGUSR1,
111 Action::Command("echo USR1".into()),
112 Location::dummy(""),
113 false,
114 )
115 .now_or_never()
116 .unwrap()
117 .unwrap();
118 let _ = system
119 .state
120 .borrow_mut()
121 .processes
122 .get_mut(&system.process_id)
123 .unwrap()
124 .raise_signal(SIGUSR1);
125
126 let command: syntax::Command = "echo main".parse().unwrap();
127 let result = command.execute(&mut env).now_or_never().unwrap();
128 assert_eq!(result, Continue(()));
129 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
130
131 assert_stdout(&system.state, |stdout| assert_eq!(stdout, "main\nUSR1\n"));
132 }
133
134 #[test]
135 fn list_execute_no_divert() {
136 let mut env = Env::new_virtual();
137 env.builtins.insert("return", return_builtin());
138 let list: syntax::List = "return -n 1; return -n 2; return -n 4".parse().unwrap();
139 let result = list.execute(&mut env).now_or_never().unwrap();
140 assert_eq!(result, Continue(()));
141 assert_eq!(env.exit_status, ExitStatus(4));
142 }
143
144 #[test]
145 fn list_execute_divert() {
146 let mut env = Env::new_virtual();
147 env.builtins.insert("return", return_builtin());
148 let list: syntax::List = "return -n 1; return 2; return -n 4".parse().unwrap();
149 let result = list.execute(&mut env).now_or_never().unwrap();
150 assert_eq!(result, Break(Divert::Return(Some(ExitStatus(2)))));
151 assert_eq!(env.exit_status, ExitStatus(1));
152 }
153}