use std::fmt;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct Span {
pub offset: usize,
pub line: u32,
pub col: u32,
}
impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}:{}", self.line, self.col)
}
}
#[derive(Debug)]
pub enum ShellError {
Exit(ExitStatus),
Return(ExitStatus),
Break(usize),
Continue(usize),
Syntax { msg: String, span: Span },
CommandNotFound(String),
Io(std::io::Error),
Runtime { msg: String, span: Span },
Cancelled,
TimedOut,
Stopped {
pid: i32,
pgid: i32,
},
}
impl ShellError {
pub fn is_interrupted(&self) -> bool {
matches!(self, ShellError::Cancelled | ShellError::TimedOut)
}
pub fn is_cancelled(&self) -> bool {
matches!(self, ShellError::Cancelled)
}
pub fn is_timed_out(&self) -> bool {
matches!(self, ShellError::TimedOut)
}
pub fn is_stopped(&self) -> bool {
matches!(self, ShellError::Stopped { .. })
}
pub fn exit_code(&self) -> Option<ExitStatus> {
match self {
ShellError::Exit(s) | ShellError::Return(s) => Some(*s),
_ => None,
}
}
}
impl fmt::Display for ShellError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ShellError::Exit(n) => write!(f, "exit {n}"),
ShellError::Return(n) => write!(f, "return {n}"),
ShellError::Break(n) => write!(f, "break {n}"),
ShellError::Continue(n) => write!(f, "continue {n}"),
ShellError::Syntax { msg, span } => write!(f, "{span}: syntax error: {msg}"),
ShellError::CommandNotFound(name) => write!(f, "{name}: not found"),
ShellError::Io(e) => write!(f, "{e}"),
ShellError::Runtime { msg, span } => write!(f, "{span}: {msg}"),
ShellError::Cancelled => write!(f, "cancelled"),
ShellError::TimedOut => write!(f, "timed out"),
ShellError::Stopped { pid, pgid } => write!(f, "stopped (pid={pid}, pgid={pgid})"),
}
}
}
impl std::error::Error for ShellError {
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
ShellError::Io(e) => Some(e),
_ => None,
}
}
}
impl From<std::io::Error> for ShellError {
fn from(e: std::io::Error) -> Self {
ShellError::Io(e)
}
}
pub type Result<T> = std::result::Result<T, ShellError>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
pub struct ExitStatus(i32);
impl ExitStatus {
pub const SUCCESS: ExitStatus = ExitStatus(0);
pub const FAILURE: ExitStatus = ExitStatus(1);
pub const MISUSE: ExitStatus = ExitStatus(2);
pub const NOT_FOUND: ExitStatus = ExitStatus(127);
pub const NOT_EXECUTABLE: ExitStatus = ExitStatus(126);
pub fn code(self) -> i32 {
self.0
}
pub fn success(self) -> bool {
self.0 == 0
}
pub fn from_bool(ok: bool) -> Self {
if ok { Self::SUCCESS } else { Self::FAILURE }
}
pub fn inverted(self) -> Self {
Self::from_bool(!self.success())
}
pub fn from_wait(status: i32) -> Self {
if crate::sys::wifexited(status) {
ExitStatus(crate::sys::wexitstatus(status))
} else {
ExitStatus(128 + crate::sys::wtermsig(status))
}
}
pub fn from_signal(sig: i32) -> Self {
ExitStatus(128 + sig)
}
}
impl From<i32> for ExitStatus {
fn from(n: i32) -> Self {
ExitStatus(n)
}
}
impl From<ExitStatus> for i32 {
fn from(s: ExitStatus) -> i32 {
s.0
}
}
impl fmt::Display for ExitStatus {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}