Skip to main content

only_engine/
error.rs

1use std::fmt;
2use std::process::ExitCode;
3
4/// Engine-level runtime and host execution errors.
5///
6/// Args:
7/// None.
8///
9/// Returns:
10/// Stable typed error values for planner/runtime consumers.
11#[derive(Debug)]
12pub enum EngineError {
13    CommandFailed {
14        task: String,
15        step: usize,
16        total: usize,
17        command: String,
18        code: ExitCode,
19    },
20    CommandBlockFailed {
21        task: String,
22        step: usize,
23        total: usize,
24        code: ExitCode,
25    },
26    CommandBlockStartFailed {
27        shell: String,
28        source: Box<EngineError>,
29    },
30    Interpolation(String),
31    ShellNotFound(String),
32    UnsupportedShell(String),
33    Runtime(String),
34    Io {
35        message: &'static str,
36        path: String,
37        source: std::io::Error,
38    },
39}
40
41impl fmt::Display for EngineError {
42    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
43        match self {
44            Self::CommandFailed {
45                task,
46                step,
47                total,
48                command,
49                code,
50            } => write!(
51                f,
52                "task '{task}' failed at step [{step}/{total}]\ncommand: `{command}`\nexit code: {code:?}"
53            ),
54            Self::CommandBlockFailed {
55                task,
56                step,
57                total,
58                code,
59            } => write!(
60                f,
61                "task '{task}' failed at step [{step}/{total}]\ncommand block failed\nexit code: {code:?}"
62            ),
63            Self::CommandBlockStartFailed { shell, source } => {
64                write!(
65                    f,
66                    "could not start command block with shell '{shell}'\n{source}"
67                )
68            }
69            Self::Interpolation(message) => f.write_str(message),
70            Self::ShellNotFound(message) => f.write_str(message),
71            Self::UnsupportedShell(shell) => write!(f, "shell '{shell}' is not supported"),
72            Self::Runtime(message) => f.write_str(message),
73            Self::Io {
74                message,
75                path,
76                source,
77            } => write!(f, "{message}: {path}: {source}"),
78        }
79    }
80}
81
82pub(crate) fn command_block_failed(
83    task: &str,
84    step_index: usize,
85    step_total: usize,
86    code: ExitCode,
87) -> EngineError {
88    EngineError::CommandBlockFailed {
89        task: task.to_string(),
90        step: step_index,
91        total: step_total,
92        code,
93    }
94}
95
96impl std::error::Error for EngineError {
97    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
98        match self {
99            Self::Io { source, .. } => Some(source),
100            Self::CommandBlockStartFailed { source, .. } => Some(source.as_ref()),
101            _ => None,
102        }
103    }
104}
105
106pub(crate) fn command_block_start_failed(shell: &str, source: EngineError) -> EngineError {
107    match source {
108        EngineError::ShellNotFound(_)
109        | EngineError::UnsupportedShell(_)
110        | EngineError::Io { .. } => EngineError::CommandBlockStartFailed {
111            shell: shell.to_string(),
112            source: Box::new(source),
113        },
114        other => other,
115    }
116}
117
118pub(crate) fn command_failed(
119    task: &str,
120    step_index: usize,
121    step_total: usize,
122    command: &str,
123    code: ExitCode,
124) -> EngineError {
125    EngineError::CommandFailed {
126        task: task.to_string(),
127        step: step_index,
128        total: step_total,
129        command: command.to_string(),
130        code,
131    }
132}