use std::{
io,
collections::HashMap, ffi::OsString,
};
use crate::{
io::FileDescriptor,
term::color,
symbol, runtime::value::{self, Value}, fmt::Show,
};
use super::{SourcePos, ErrorStatus};
#[derive(Debug)]
pub enum Panic {
InvalidArgs {
object: &'static str,
items: u32,
pos: SourcePos,
},
UnsupportedFileDescriptor {
fd: FileDescriptor,
pos: SourcePos,
},
InvalidPattern {
pattern: OsString,
pos: SourcePos,
}
}
impl Panic {
pub fn invalid_args(object: &'static str, items: u32, pos: SourcePos) -> Self {
Self::InvalidArgs { object, items, pos }
}
pub fn unsupported_fd(fd: FileDescriptor, pos: SourcePos) -> Self {
Self::UnsupportedFileDescriptor { fd, pos }
}
pub fn invalid_pattern(pattern: OsString, pos: SourcePos) -> Self {
Self::InvalidPattern { pattern, pos }
}
}
impl std::fmt::Display for Panic {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let panic = color::Fg(color::Red, "Panic");
match self {
Self::InvalidArgs { object, items, .. } =>
write!(
f,
"{}: {} expansion resulted in {} items",
panic,
object,
items
),
Self::UnsupportedFileDescriptor { fd, .. } =>
write!(
f,
"{}: unsupported file descriptor ({})",
panic,
color::Fg(color::Yellow, fd)
),
Self::InvalidPattern { pattern, .. } =>
write!(
f,
"{}: pattern ({:?}) has invalid UTF-8",
panic,
color::Fg(color::Yellow, pattern)
),
}
}
}
impl std::error::Error for Panic { }
impl From<Panic> for crate::runtime::Panic {
fn from(panic: Panic) -> Self {
use crate::runtime::Panic as P;
match panic {
Panic::InvalidArgs { object, items, pos } => P::invalid_command_args(object, items, pos),
Panic::UnsupportedFileDescriptor { fd, pos } => P::unsupported_fd(fd, pos),
Panic::InvalidPattern { pattern, pos } => P::invalid_pattern(pattern, pos),
}
}
}
pub trait IntoValue {
fn into_value(self, interner: & symbol::Interner) -> Value;
}
impl IntoValue for ErrorStatus {
fn into_value(mut self, interner: & symbol::Interner) -> Value {
thread_local! {
pub static STATUS: Value = "status".into();
pub static POS: Value = "pos".into();
}
let description = std::mem::take(&mut self.description).into();
let mut context = HashMap::new();
STATUS.with(
|status| context.insert(status.copy(), Value::Int(self.status as i64))
);
POS.with(
|pos| context.insert(pos.copy(), Show(self.pos, interner).to_string().into())
);
let context = value::Dict::new(context).into();
value::Error::new(description, context).into()
}
}
#[derive(Debug)]
pub enum Error {
Io {
error: io::Error,
pos: SourcePos,
},
Panic(Panic),
}
impl Error {
pub fn io(error: io::Error, pos: SourcePos) -> Self {
Self::Io { error, pos }
}
}
impl From<Panic> for Error {
fn from(panic: Panic) -> Self {
Self::Panic(panic)
}
}
#[derive(Debug)]
pub struct PipelineErrors(Box<[ErrorStatus]>);
impl PipelineErrors {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl IntoValue for PipelineErrors {
fn into_value(self, interner: & symbol::Interner) -> Value {
let mut iter = self.0
.into_vec() .into_iter()
.map(|status| status.into_value(interner));
let first = match iter.next() {
None => return Value::default(),
Some(error) => error,
};
if iter.len() == 0 {
first
} else {
let mut errors = vec![first];
errors.extend(iter);
value::Error::new(
"Some commands failed in the pipeline".into(),
errors.into()
).into()
}
}
}
impl IntoValue for Box<[PipelineErrors]> {
fn into_value(self, interner: & symbol::Interner) -> Value {
let mut iter = self
.into_vec() .into_iter()
.map(|error| error.into_value(interner));
let first = match iter.next() {
None => return Value::default(),
Some(error) => error,
};
if iter.len() == 0 {
first
} else {
let mut errors = vec![first];
errors.extend(iter);
value::Error::new(
"Some commands failed in the block".into(),
errors.into()
).into()
}
}
}
impl From<ErrorStatus> for PipelineErrors {
fn from(error: ErrorStatus) -> Self {
Self([error].into())
}
}
impl From<Option<ErrorStatus>> for PipelineErrors {
fn from(error: Option<ErrorStatus>) -> Self {
match error {
Some(error) => error.into(),
None => Self(Default::default()),
}
}
}
impl From<Vec<ErrorStatus>> for PipelineErrors {
fn from(errors: Vec<ErrorStatus>) -> Self {
Self(errors.into())
}
}