1use std::path::PathBuf;
4
5use crate::{Shell, ShellFd, results, sys};
6
7#[derive(thiserror::Error, Debug)]
10#[error(transparent)]
11pub struct Error {
12 kind: ErrorKind,
14}
15
16#[derive(thiserror::Error, Debug)]
18pub enum ErrorKind {
19 #[error("cannot expand tilde expression with HOME not set")]
21 TildeWithoutValidHome,
22
23 #[error("cannot assign list to array member")]
25 AssigningListToArrayMember,
26
27 #[error("cannot convert associative array to indexed array")]
29 ConvertingAssociativeArrayToIndexedArray,
30
31 #[error("cannot convert indexed array to associative array")]
33 ConvertingIndexedArrayToAssociativeArray,
34
35 #[error("failed to source file: {0}")]
37 FailedSourcingFile(PathBuf, #[source] std::io::Error),
38
39 #[error("failed to send signal to process")]
41 FailedToSendSignal,
42
43 #[error("cannot assign in this way")]
45 CannotAssignToSpecialParameter,
46
47 #[error("expansion error: {0}")]
49 CheckedExpansionError(String),
50
51 #[error("function not found: {0}")]
53 FunctionNotFound(String),
54
55 #[error("command not found: {0}")]
57 CommandNotFound(String),
58
59 #[error("not a shell builtin: {0}")]
61 BuiltinNotFound(String),
62
63 #[error("working directory does not exist: {0}")]
65 WorkingDirMissing(PathBuf),
66
67 #[error("failed to execute command '{0}': {1}")]
69 FailedToExecuteCommand(String, #[source] std::io::Error),
70
71 #[error("history item not found")]
73 HistoryItemNotFound,
74
75 #[error("not yet implemented: {0}")]
77 Unimplemented(&'static str),
78
79 #[error("not yet implemented: {0}; see https://github.com/reubeno/brush/issues/{1}")]
82 UnimplementedAndTracked(&'static str, u32),
83
84 #[error("missing scope")]
86 MissingScope,
87
88 #[error("not a directory: {0}")]
90 NotADirectory(PathBuf),
91
92 #[error("path is a directory")]
94 IsADirectory,
95
96 #[error("variable is not an array")]
98 NotArray,
99
100 #[error("no current user")]
102 NoCurrentUser,
103
104 #[error("invalid redirection")]
106 InvalidRedirection,
107
108 #[error("failed to redirect to {0}: {1}")]
110 RedirectionFailure(String, String),
111
112 #[error("arithmetic evaluation error: {0}")]
114 EvalError(#[from] crate::arithmetic::EvalError),
115
116 #[error("failed to parse integer")]
118 IntParseError(#[from] std::num::ParseIntError),
119
120 #[error("failed to parse integer")]
122 TryIntParseError(#[from] std::num::TryFromIntError),
123
124 #[error("failed to decode utf-8")]
126 FromUtf8Error(#[from] std::string::FromUtf8Error),
127
128 #[error("failed to decode utf-8")]
130 Utf8Error(#[from] std::str::Utf8Error),
131
132 #[error("cannot mutate readonly variable")]
134 ReadonlyVariable,
135
136 #[error("invalid pattern: '{0}'")]
138 InvalidPattern(String),
139
140 #[error("regex error: {0}")]
142 RegexError(#[from] fancy_regex::Error),
143
144 #[error("invalid regex: {0}; expression: '{1}'")]
146 InvalidRegexError(fancy_regex::Error, String),
147
148 #[error("i/o error: {0}")]
150 IoError(#[from] std::io::Error),
151
152 #[error("bad substitution: {0}")]
154 BadSubstitution(String),
155
156 #[error("failed to create child process")]
158 ChildCreationFailure,
159
160 #[error(transparent)]
162 FormattingError(#[from] std::fmt::Error),
163
164 #[error("{1}: {0}")]
166 ParseError(brush_parser::ParseError, brush_parser::SourceInfo),
167
168 #[error("{0}: {1}")]
170 FunctionParseError(String, brush_parser::ParseError),
171
172 #[error(transparent)]
174 WordParseError(#[from] brush_parser::WordParseError),
175
176 #[error(transparent)]
178 TestCommandParseError(#[from] brush_parser::TestCommandParseError),
179
180 #[error(transparent)]
182 BindingParseError(#[from] brush_parser::BindingParseError),
183
184 #[error("threading error")]
186 ThreadingError(#[from] tokio::task::JoinError),
187
188 #[error("{0}: invalid signal specification")]
190 InvalidSignal(String),
191
192 #[error("platform error: {0}")]
194 PlatformError(#[from] sys::PlatformError),
195
196 #[error("invalid umask value")]
198 InvalidUmask,
199
200 #[error("cannot read from {0}")]
202 OpenFileNotReadable(&'static str),
203
204 #[error("cannot write to {0}")]
206 OpenFileNotWritable(&'static str),
207
208 #[error("bad file descriptor: {0}")]
210 BadFileDescriptor(ShellFd),
211
212 #[error("printf failure: {0}")]
214 PrintfFailure(i32),
215
216 #[error("printf: {0}")]
218 PrintfInvalidUsage(String),
219
220 #[error("interrupted")]
222 Interrupted,
223
224 #[error("maximum function call depth exceeded")]
226 MaxFunctionCallDepthExceeded,
227
228 #[error("system time error: {0}")]
230 TimeError(#[from] std::time::SystemTimeError),
231
232 #[error("array index out of range: {0}")]
234 ArrayIndexOutOfRange(i64),
235
236 #[error("unhandled key code: {0:?}")]
238 UnhandledKeyCode(Vec<u8>),
239
240 #[error("{1}: {0}")]
242 BuiltinError(Box<dyn BuiltinError>, String),
243
244 #[error("operation not supported on this platform: {0}")]
246 NotSupportedOnThisPlatform(&'static str),
247
248 #[error("command history is not enabled in this shell")]
250 HistoryNotEnabled,
251
252 #[error("unknown key binding function: {0}")]
254 UnknownKeyBindingFunction(String),
255}
256
257impl BuiltinError for Error {}
258
259pub trait BuiltinError: std::error::Error + ConvertibleToExitCode + Send + Sync {}
261
262pub trait ConvertibleToExitCode {
264 fn as_exit_code(&self) -> results::ExecutionExitCode;
266}
267
268impl<T> ConvertibleToExitCode for T
269where
270 results::ExecutionExitCode: for<'a> From<&'a T>,
271{
272 fn as_exit_code(&self) -> results::ExecutionExitCode {
273 self.into()
274 }
275}
276
277impl From<&ErrorKind> for results::ExecutionExitCode {
278 fn from(value: &ErrorKind) -> Self {
279 match value {
280 ErrorKind::CommandNotFound(..) => Self::NotFound,
281 ErrorKind::Unimplemented(..) | ErrorKind::UnimplementedAndTracked(..) => {
282 Self::Unimplemented
283 }
284 ErrorKind::ParseError(..) => Self::InvalidUsage,
285 ErrorKind::FunctionParseError(..) => Self::InvalidUsage,
286 ErrorKind::FailedToExecuteCommand(..) => Self::CannotExecute,
287 ErrorKind::BuiltinError(inner, ..) => inner.as_exit_code(),
288 _ => Self::GeneralError,
289 }
290 }
291}
292
293impl From<&Error> for results::ExecutionExitCode {
294 fn from(error: &Error) -> Self {
295 Self::from(&error.kind)
296 }
297}
298
299impl<T> From<T> for Error
300where
301 ErrorKind: From<T>,
302{
303 fn from(convertible_to_kind: T) -> Self {
304 Self {
305 kind: convertible_to_kind.into(),
306 }
307 }
308}
309
310pub trait ErrorFormatter: Send {
313 fn format_error(&self, error: &Error, shell: &Shell) -> String;
320}
321
322pub(crate) struct DefaultErrorFormatter {}
324
325impl DefaultErrorFormatter {
326 pub const fn new() -> Self {
327 Self {}
328 }
329}
330
331impl ErrorFormatter for DefaultErrorFormatter {
332 fn format_error(&self, err: &Error, _shell: &Shell) -> String {
333 std::format!("error: {err:#}\n")
334 }
335}
336
337pub fn unimp<T>(msg: &'static str) -> Result<T, Error> {
343 Err(ErrorKind::Unimplemented(msg).into())
344}
345
346#[allow(unused)]
353pub fn unimp_with_issue<T>(msg: &'static str, project_issue_id: u32) -> Result<T, Error> {
354 Err(ErrorKind::UnimplementedAndTracked(msg, project_issue_id).into())
355}