1use std::fmt;
2use std::process::ExitCode;
3
4#[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}