1#![allow(unused_qualifications)] use crate::io::Permissions;
6use crate::Fd;
7use std::convert::From;
8use std::error::Error;
9use std::fmt::{self, Display, Formatter};
10use std::io::Error as IoError;
11
12pub trait IsFatalError: 'static + Send + Sync + Error {
23 fn is_fatal(&self) -> bool;
25}
26
27impl IsFatalError for void::Void {
28 fn is_fatal(&self) -> bool {
29 void::unreachable(*self)
30 }
31}
32
33#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
35pub enum ExpansionError {
36 #[error("attempted to divide by zero")]
38 DivideByZero,
39 #[error("attempted to raise to a negative power")]
41 NegativeExponent,
42 #[error("{0}: cannot assign in this way")]
44 BadAssig(String),
45 #[error("{0}: {1}")]
47 EmptyParameter(String , String ),
48}
49
50impl IsFatalError for ExpansionError {
51 fn is_fatal(&self) -> bool {
52 match *self {
54 ExpansionError::DivideByZero
55 | ExpansionError::NegativeExponent
56 | ExpansionError::BadAssig(_)
57 | ExpansionError::EmptyParameter(_, _) => true,
58 }
59 }
60}
61
62#[derive(Debug, thiserror::Error)]
64pub enum RedirectionError {
65 Ambiguous(Vec<String>),
67 BadFdSrc(String),
69 BadFdPerms(Fd, Permissions ),
72 Io(#[source] IoError, Option<String>),
75}
76
77impl Eq for RedirectionError {}
78impl PartialEq for RedirectionError {
79 fn eq(&self, other: &Self) -> bool {
80 use self::RedirectionError::*;
81
82 match (self, other) {
83 (&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
84 (&Ambiguous(ref a), &Ambiguous(ref b)) => a == b,
85 (&BadFdSrc(ref a), &BadFdSrc(ref b)) => a == b,
86 (&BadFdPerms(fd_a, perms_a), &BadFdPerms(fd_b, perms_b)) => {
87 fd_a == fd_b && perms_a == perms_b
88 }
89 _ => false,
90 }
91 }
92}
93
94impl Display for RedirectionError {
95 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
96 match *self {
97 RedirectionError::Ambiguous(ref v) => {
98 write!(fmt, "a redirect path evaluated to multiple fields: ")?;
99 let mut iter = v.iter();
100 if let Some(s) = iter.next() {
101 write!(fmt, "{}", s)?;
102 }
103 for s in iter {
104 write!(fmt, " {}", s)?;
105 }
106 Ok(())
107 }
108
109 RedirectionError::BadFdSrc(ref fd) => {
110 let description = "attempted to duplicate an invalid file descriptor";
111 write!(fmt, "{}: {}", description, fd)
112 }
113
114 RedirectionError::BadFdPerms(fd, perms) => {
115 let description = "attmpted to duplicate a file descritpor with Read/Write access that differs from the original";
116 write!(
117 fmt,
118 "{}: {}, desired permissions: {}",
119 description, fd, perms
120 )
121 }
122
123 RedirectionError::Io(ref e, None) => write!(fmt, "{}", e),
124 RedirectionError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
125 }
126 }
127}
128
129impl IsFatalError for RedirectionError {
130 fn is_fatal(&self) -> bool {
131 match *self {
132 RedirectionError::Ambiguous(_)
133 | RedirectionError::BadFdSrc(_)
134 | RedirectionError::BadFdPerms(_, _)
135 | RedirectionError::Io(_, _) => false,
136 }
137 }
138}
139
140#[derive(Debug, thiserror::Error)]
142pub enum CommandError {
143 NotFound(String),
145 NotExecutable(String),
147 Io(#[source] IoError, Option<String>),
150}
151
152impl Eq for CommandError {}
153impl PartialEq for CommandError {
154 fn eq(&self, other: &Self) -> bool {
155 use self::CommandError::*;
156
157 match (self, other) {
158 (&NotFound(ref a), &NotFound(ref b))
159 | (&NotExecutable(ref a), &NotExecutable(ref b)) => a == b,
160 (&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
161 _ => false,
162 }
163 }
164}
165
166impl Display for CommandError {
167 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
168 match *self {
169 CommandError::NotFound(ref c) => write!(fmt, "{}: command not found", c),
170 CommandError::NotExecutable(ref c) => write!(fmt, "{}: command not executable", c),
171 CommandError::Io(ref e, None) => write!(fmt, "{}", e),
172 CommandError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
173 }
174 }
175}
176
177impl IsFatalError for CommandError {
178 fn is_fatal(&self) -> bool {
179 match *self {
180 CommandError::NotFound(_) | CommandError::NotExecutable(_) | CommandError::Io(_, _) => {
181 false
182 }
183 }
184 }
185}
186
187#[derive(Debug, thiserror::Error)]
189pub enum RuntimeError {
190 Io(#[source] IoError, Option<String>),
193 Expansion(#[from] ExpansionError),
195 Redirection(#[from] RedirectionError),
197 Command(#[from] CommandError),
199 Unimplemented(&'static str),
201}
202
203impl Eq for RuntimeError {}
204impl PartialEq for RuntimeError {
205 fn eq(&self, other: &Self) -> bool {
206 use self::RuntimeError::*;
207
208 match (self, other) {
209 (&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
210 (&Expansion(ref a), &Expansion(ref b)) => a == b,
211 (&Redirection(ref a), &Redirection(ref b)) => a == b,
212 (&Command(ref a), &Command(ref b)) => a == b,
213 (&Unimplemented(a), &Unimplemented(b)) => a == b,
214 _ => false,
215 }
216 }
217}
218
219impl Display for RuntimeError {
220 fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
221 match *self {
222 RuntimeError::Expansion(ref e) => write!(fmt, "{}", e),
223 RuntimeError::Redirection(ref e) => write!(fmt, "{}", e),
224 RuntimeError::Command(ref e) => write!(fmt, "{}", e),
225 RuntimeError::Unimplemented(e) => write!(fmt, "{}", e),
226 RuntimeError::Io(ref e, None) => write!(fmt, "{}", e),
227 RuntimeError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
228 }
229 }
230}
231
232impl IsFatalError for RuntimeError {
233 fn is_fatal(&self) -> bool {
234 match *self {
235 RuntimeError::Expansion(ref e) => e.is_fatal(),
236 RuntimeError::Redirection(ref e) => e.is_fatal(),
237 RuntimeError::Command(ref e) => e.is_fatal(),
238 RuntimeError::Io(_, _) | RuntimeError::Unimplemented(_) => false,
239 }
240 }
241}
242
243impl From<IoError> for RuntimeError {
244 fn from(err: IoError) -> Self {
245 RuntimeError::Io(err, None)
246 }
247}
248
249impl From<void::Void> for RuntimeError {
250 fn from(err: void::Void) -> Self {
251 void::unreachable(err)
252 }
253}
254
255#[cfg(test)]
256mod tests {
257 use super::*;
258
259 #[test]
260 fn ensure_runtime_errors_are_send_and_sync() {
261 fn send_and_sync<T: Send + Sync>() {}
262
263 send_and_sync::<ExpansionError>();
264 send_and_sync::<RedirectionError>();
265 send_and_sync::<CommandError>();
266 send_and_sync::<RuntimeError>();
267 }
268}