1use crate::{error, processes};
4
5#[derive(Default)]
7pub struct ExecutionResult {
8 pub next_control_flow: ExecutionControlFlow,
10 pub exit_code: ExecutionExitCode,
12}
13
14impl ExecutionResult {
15 pub fn new(exit_code: u8) -> Self {
21 Self {
22 exit_code: exit_code.into(),
23 ..Self::default()
24 }
25 }
26
27 pub fn stopped() -> Self {
29 const SIGTSTP: std::os::raw::c_int = 20;
31
32 #[expect(clippy::cast_possible_truncation)]
33 Self::new(128 + SIGTSTP as u8)
34 }
35
36 pub const fn success() -> Self {
38 Self {
39 next_control_flow: ExecutionControlFlow::Normal,
40 exit_code: ExecutionExitCode::Success,
41 }
42 }
43
44 pub const fn general_error() -> Self {
46 Self {
47 next_control_flow: ExecutionControlFlow::Normal,
48 exit_code: ExecutionExitCode::GeneralError,
49 }
50 }
51
52 pub const fn is_success(&self) -> bool {
54 self.exit_code.is_success()
55 }
56
57 pub const fn is_normal_flow(&self) -> bool {
60 matches!(self.next_control_flow, ExecutionControlFlow::Normal)
61 }
62
63 pub const fn is_break(&self) -> bool {
65 matches!(
66 self.next_control_flow,
67 ExecutionControlFlow::BreakLoop { .. }
68 )
69 }
70
71 pub const fn is_continue(&self) -> bool {
73 matches!(
74 self.next_control_flow,
75 ExecutionControlFlow::ContinueLoop { .. }
76 )
77 }
78
79 pub const fn is_return_or_exit(&self) -> bool {
83 matches!(
84 self.next_control_flow,
85 ExecutionControlFlow::ReturnFromFunctionOrScript | ExecutionControlFlow::ExitShell
86 )
87 }
88}
89
90impl From<ExecutionExitCode> for ExecutionResult {
91 fn from(exit_code: ExecutionExitCode) -> Self {
92 Self {
93 next_control_flow: ExecutionControlFlow::Normal,
94 exit_code,
95 }
96 }
97}
98
99#[derive(Clone, Copy, Default)]
101pub enum ExecutionExitCode {
102 #[default]
104 Success,
105 GeneralError,
107 InvalidUsage,
109 CannotExecute,
111 NotFound,
113 Interrupted,
115 Unimplemented,
117 Custom(u8),
119}
120
121impl ExecutionExitCode {
122 pub const fn is_success(&self) -> bool {
124 matches!(self, Self::Success)
125 }
126}
127
128impl From<u8> for ExecutionExitCode {
129 fn from(code: u8) -> Self {
130 match code {
131 0 => Self::Success,
132 1 => Self::GeneralError,
133 2 => Self::InvalidUsage,
134 99 => Self::Unimplemented,
135 126 => Self::CannotExecute,
136 127 => Self::NotFound,
137 130 => Self::Interrupted,
138 code => Self::Custom(code),
139 }
140 }
141}
142
143impl From<ExecutionExitCode> for u8 {
144 fn from(code: ExecutionExitCode) -> Self {
145 Self::from(&code)
146 }
147}
148
149impl From<&ExecutionExitCode> for u8 {
150 fn from(code: &ExecutionExitCode) -> Self {
151 match code {
152 ExecutionExitCode::Success => 0,
153 ExecutionExitCode::GeneralError => 1,
154 ExecutionExitCode::InvalidUsage => 2,
155 ExecutionExitCode::Unimplemented => 99,
156 ExecutionExitCode::CannotExecute => 126,
157 ExecutionExitCode::NotFound => 127,
158 ExecutionExitCode::Interrupted => 130,
159 ExecutionExitCode::Custom(code) => *code,
160 }
161 }
162}
163
164#[derive(Clone, Copy, Default)]
166pub enum ExecutionControlFlow {
167 #[default]
169 Normal,
170 BreakLoop {
172 levels: usize,
175 },
176 ContinueLoop {
178 levels: usize,
181 },
182 ReturnFromFunctionOrScript,
184 ExitShell,
186}
187
188impl ExecutionControlFlow {
189 #[must_use]
193 pub const fn try_decrement_loop_levels(&self) -> Self {
194 match self {
195 Self::BreakLoop { levels: 0 } | Self::ContinueLoop { levels: 0 } => Self::Normal,
196 Self::BreakLoop { levels } => Self::BreakLoop {
197 levels: *levels - 1,
198 },
199 Self::ContinueLoop { levels } => Self::ContinueLoop {
200 levels: *levels - 1,
201 },
202 control_flow => *control_flow,
203 }
204 }
205}
206
207pub enum ExecutionSpawnResult {
211 Completed(ExecutionResult),
213 StartedProcess(processes::ChildProcess),
215}
216
217impl From<ExecutionResult> for ExecutionSpawnResult {
218 fn from(result: ExecutionResult) -> Self {
219 Self::Completed(result)
220 }
221}
222
223impl ExecutionSpawnResult {
224 pub async fn wait(self, no_wait: bool) -> Result<ExecutionWaitResult, error::Error> {
230 match self {
231 Self::StartedProcess(mut child) => {
232 let process_wait_result = if !no_wait {
233 child.wait().await?
236 } else {
237 processes::ProcessWaitResult::Stopped
238 };
239
240 let wait_result = match process_wait_result {
241 processes::ProcessWaitResult::Completed(output) => {
242 ExecutionWaitResult::Completed(ExecutionResult::from(output))
243 }
244 processes::ProcessWaitResult::Stopped => ExecutionWaitResult::Stopped(child),
245 };
246
247 Ok(wait_result)
248 }
249 Self::Completed(result) => Ok(ExecutionWaitResult::Completed(result)),
250 }
251 }
252}
253
254pub enum ExecutionWaitResult {
256 Completed(ExecutionResult),
258 Stopped(processes::ChildProcess),
260}