#![allow(unused_qualifications)]
use crate::io::Permissions;
use crate::Fd;
use std::convert::From;
use std::error::Error;
use std::fmt::{self, Display, Formatter};
use std::io::Error as IoError;
pub trait IsFatalError: 'static + Send + Sync + Error {
fn is_fatal(&self) -> bool;
}
impl IsFatalError for void::Void {
fn is_fatal(&self) -> bool {
void::unreachable(*self)
}
}
#[derive(PartialEq, Eq, Clone, Debug, thiserror::Error)]
pub enum ExpansionError {
#[error("attempted to divide by zero")]
DivideByZero,
#[error("attempted to raise to a negative power")]
NegativeExponent,
#[error("{0}: cannot assign in this way")]
BadAssig(String),
#[error("{0}: {1}")]
EmptyParameter(String , String ),
}
impl IsFatalError for ExpansionError {
fn is_fatal(&self) -> bool {
match *self {
ExpansionError::DivideByZero
| ExpansionError::NegativeExponent
| ExpansionError::BadAssig(_)
| ExpansionError::EmptyParameter(_, _) => true,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum RedirectionError {
Ambiguous(Vec<String>),
BadFdSrc(String),
BadFdPerms(Fd, Permissions ),
Io(#[source] IoError, Option<String>),
}
impl Eq for RedirectionError {}
impl PartialEq for RedirectionError {
fn eq(&self, other: &Self) -> bool {
use self::RedirectionError::*;
match (self, other) {
(&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
(&Ambiguous(ref a), &Ambiguous(ref b)) => a == b,
(&BadFdSrc(ref a), &BadFdSrc(ref b)) => a == b,
(&BadFdPerms(fd_a, perms_a), &BadFdPerms(fd_b, perms_b)) => {
fd_a == fd_b && perms_a == perms_b
}
_ => false,
}
}
}
impl Display for RedirectionError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
RedirectionError::Ambiguous(ref v) => {
write!(fmt, "a redirect path evaluated to multiple fields: ")?;
let mut iter = v.iter();
if let Some(s) = iter.next() {
write!(fmt, "{}", s)?;
}
for s in iter {
write!(fmt, " {}", s)?;
}
Ok(())
}
RedirectionError::BadFdSrc(ref fd) => {
let description = "attempted to duplicate an invalid file descriptor";
write!(fmt, "{}: {}", description, fd)
}
RedirectionError::BadFdPerms(fd, perms) => {
let description = "attmpted to duplicate a file descritpor with Read/Write access that differs from the original";
write!(
fmt,
"{}: {}, desired permissions: {}",
description, fd, perms
)
}
RedirectionError::Io(ref e, None) => write!(fmt, "{}", e),
RedirectionError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
}
}
}
impl IsFatalError for RedirectionError {
fn is_fatal(&self) -> bool {
match *self {
RedirectionError::Ambiguous(_)
| RedirectionError::BadFdSrc(_)
| RedirectionError::BadFdPerms(_, _)
| RedirectionError::Io(_, _) => false,
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum CommandError {
NotFound(String),
NotExecutable(String),
Io(#[source] IoError, Option<String>),
}
impl Eq for CommandError {}
impl PartialEq for CommandError {
fn eq(&self, other: &Self) -> bool {
use self::CommandError::*;
match (self, other) {
(&NotFound(ref a), &NotFound(ref b))
| (&NotExecutable(ref a), &NotExecutable(ref b)) => a == b,
(&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
_ => false,
}
}
}
impl Display for CommandError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
CommandError::NotFound(ref c) => write!(fmt, "{}: command not found", c),
CommandError::NotExecutable(ref c) => write!(fmt, "{}: command not executable", c),
CommandError::Io(ref e, None) => write!(fmt, "{}", e),
CommandError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
}
}
}
impl IsFatalError for CommandError {
fn is_fatal(&self) -> bool {
match *self {
CommandError::NotFound(_) | CommandError::NotExecutable(_) | CommandError::Io(_, _) => {
false
}
}
}
}
#[derive(Debug, thiserror::Error)]
pub enum RuntimeError {
Io(#[source] IoError, Option<String>),
Expansion(#[from] ExpansionError),
Redirection(#[from] RedirectionError),
Command(#[from] CommandError),
Unimplemented(&'static str),
}
impl Eq for RuntimeError {}
impl PartialEq for RuntimeError {
fn eq(&self, other: &Self) -> bool {
use self::RuntimeError::*;
match (self, other) {
(&Io(ref e1, ref a), &Io(ref e2, ref b)) => e1.kind() == e2.kind() && a == b,
(&Expansion(ref a), &Expansion(ref b)) => a == b,
(&Redirection(ref a), &Redirection(ref b)) => a == b,
(&Command(ref a), &Command(ref b)) => a == b,
(&Unimplemented(a), &Unimplemented(b)) => a == b,
_ => false,
}
}
}
impl Display for RuntimeError {
fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result {
match *self {
RuntimeError::Expansion(ref e) => write!(fmt, "{}", e),
RuntimeError::Redirection(ref e) => write!(fmt, "{}", e),
RuntimeError::Command(ref e) => write!(fmt, "{}", e),
RuntimeError::Unimplemented(e) => write!(fmt, "{}", e),
RuntimeError::Io(ref e, None) => write!(fmt, "{}", e),
RuntimeError::Io(ref e, Some(ref path)) => write!(fmt, "{}: {}", e, path),
}
}
}
impl IsFatalError for RuntimeError {
fn is_fatal(&self) -> bool {
match *self {
RuntimeError::Expansion(ref e) => e.is_fatal(),
RuntimeError::Redirection(ref e) => e.is_fatal(),
RuntimeError::Command(ref e) => e.is_fatal(),
RuntimeError::Io(_, _) | RuntimeError::Unimplemented(_) => false,
}
}
}
impl From<IoError> for RuntimeError {
fn from(err: IoError) -> Self {
RuntimeError::Io(err, None)
}
}
impl From<void::Void> for RuntimeError {
fn from(err: void::Void) -> Self {
void::unreachable(err)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ensure_runtime_errors_are_send_and_sync() {
fn send_and_sync<T: Send + Sync>() {}
send_and_sync::<ExpansionError>();
send_and_sync::<RedirectionError>();
send_and_sync::<CommandError>();
send_and_sync::<RuntimeError>();
}
}