Skip to main content

only_engine/
error.rs

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