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, reason = "we don't support Send")]
42 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 std::rc::Rc;
91
92 use super::*;
93 use crate::tests::echo_builtin;
94 use crate::tests::return_builtin;
95 use futures_util::FutureExt as _;
96 use yash_env::semantics::Divert;
97 use yash_env::semantics::ExitStatus;
98 use yash_env::system::Concurrent;
99 use yash_env::system::r#virtual::SIGUSR1;
100 use yash_env::system::r#virtual::VirtualSystem;
101 use yash_env::test_helper::assert_stdout;
102 use yash_env::trap::Action;
103 use yash_syntax::source::Location;
104
105 #[test]
106 fn command_handles_traps() {
107 let system = VirtualSystem::new();
108 let mut env = Env::with_system(Rc::new(Concurrent::new(system.clone())));
109 env.builtins.insert("echo", echo_builtin());
110 env.traps
111 .set_action(
112 &env.system,
113 SIGUSR1,
114 Action::Command("echo USR1".into()),
115 Location::dummy(""),
116 false,
117 )
118 .now_or_never()
119 .unwrap()
120 .unwrap();
121 let _ = system
122 .state
123 .borrow_mut()
124 .processes
125 .get_mut(&system.process_id)
126 .unwrap()
127 .raise_signal(SIGUSR1);
128
129 let command: syntax::Command = "echo main".parse().unwrap();
130 let result = command.execute(&mut env).now_or_never().unwrap();
131 assert_eq!(result, Continue(()));
132 assert_eq!(env.exit_status, ExitStatus::SUCCESS);
133
134 assert_stdout(&system.state, |stdout| assert_eq!(stdout, "main\nUSR1\n"));
135 }
136
137 #[test]
138 fn list_execute_no_divert() {
139 let mut env = Env::new_virtual();
140 env.builtins.insert("return", return_builtin());
141 let list: syntax::List = "return -n 1; return -n 2; return -n 4".parse().unwrap();
142 let result = list.execute(&mut env).now_or_never().unwrap();
143 assert_eq!(result, Continue(()));
144 assert_eq!(env.exit_status, ExitStatus(4));
145 }
146
147 #[test]
148 fn list_execute_divert() {
149 let mut env = Env::new_virtual();
150 env.builtins.insert("return", return_builtin());
151 let list: syntax::List = "return -n 1; return 2; return -n 4".parse().unwrap();
152 let result = list.execute(&mut env).now_or_never().unwrap();
153 assert_eq!(result, Break(Divert::Return(Some(ExitStatus(2)))));
154 assert_eq!(env.exit_status, ExitStatus(1));
155 }
156}