use std::{borrow::Cow, io, ffi::OsString};
use crate::{
fmt::{self, Display},
io::FileDescriptor,
term::color,
symbol::{self, Symbol},
};
use super::{Value, SourcePos};
#[derive(Debug)]
pub enum Panic {
StackOverflow { pos: SourcePos },
IntegerOverflow { pos: SourcePos },
DivisionByZero { pos: SourcePos },
IndexOutOfBounds {
index: Value,
pos: SourcePos,
},
EmptyCollection { pos: SourcePos },
InvalidCall {
function: Value,
pos: SourcePos
},
InvalidArgs {
supplied: u32,
expected: u32,
pos: SourcePos
},
InvalidCondition {
value: Value,
pos: SourcePos,
},
TypeError {
value: Value,
expected: Cow<'static, str>,
pos: SourcePos,
},
ValueError {
value: Value,
message: Cow<'static, str>,
pos: SourcePos,
},
AssignToReadonlyField {
field: Value,
pos: SourcePos,
},
InvalidCommandArgs {
object: &'static str,
items: u32,
pos: SourcePos,
},
Io {
error: io::Error,
pos: SourcePos,
},
UnsupportedFileDescriptor {
fd: FileDescriptor,
pos: SourcePos,
},
InvalidPattern {
pattern: OsString,
pos: SourcePos,
},
AssertionFailed { pos: SourcePos },
ImportFailed {
pos: SourcePos,
path: Symbol,
},
InvalidJoin { pos: SourcePos },
User {
context: Value,
pos: SourcePos,
},
}
impl Panic {
pub fn stack_overflow(pos: SourcePos) -> Self {
Self::StackOverflow { pos }
}
pub fn assertion_failed(pos: SourcePos) -> Self {
Self::AssertionFailed { pos }
}
pub fn integer_overflow(pos: SourcePos) -> Self {
Self::IntegerOverflow { pos }
}
pub fn division_by_zero(pos: SourcePos) -> Self {
Self::DivisionByZero { pos }
}
pub fn index_out_of_bounds(index: Value, pos: SourcePos) -> Self {
Self::IndexOutOfBounds { index, pos }
}
pub fn empty_collection(pos: SourcePos) -> Self {
Self::EmptyCollection { pos }
}
pub fn invalid_call(function: Value, pos: SourcePos) -> Self {
Self::InvalidCall { function, pos }
}
pub fn invalid_args(supplied: u32, expected: u32, pos: SourcePos) -> Self {
Self::InvalidArgs { supplied, expected, pos }
}
pub fn invalid_condition(value: Value, pos: SourcePos) -> Self {
Self::InvalidCondition { value, pos }
}
pub fn type_error<E>(value: Value, expected: E, pos: SourcePos) -> Self
where
E: Into<Cow<'static, str>>,
{
Self::TypeError {
value,
expected: expected.into(),
pos,
}
}
pub fn value_error<E>(value: Value, message: E, pos: SourcePos) -> Self
where
E: Into<Cow<'static, str>>,
{
Self::ValueError {
value,
message: message.into(),
pos,
}
}
pub fn invalid_command_args(object: &'static str, items: u32, pos: SourcePos) -> Self {
Self::InvalidCommandArgs { object, items, pos }
}
pub fn io(error: io::Error, pos: SourcePos) -> Self {
Self::Io { error, 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 }
}
pub fn assign_to_readonly_field(field: Value, pos: SourcePos) -> Self {
Self::AssignToReadonlyField { field, pos }
}
pub fn import_failed(path: Symbol, pos: SourcePos) -> Self {
Self::ImportFailed { path, pos }
}
pub fn invalid_join(pos: SourcePos) -> Self {
Self::InvalidJoin { pos }
}
pub fn user(context: Value, pos: SourcePos) -> Self {
Self::User { context, pos }
}
}
impl<'a> Display<'a> for Panic {
type Context = &'a symbol::Interner;
fn fmt(&self, f: &mut std::fmt::Formatter, context: Self::Context) -> std::fmt::Result {
let panic = color::Fg(color::Red, "Panic");
match self {
Self::StackOverflow { pos } =>
write!(f, "{} in {}: stack overflow", panic, fmt::Show(pos, context)),
Self::IntegerOverflow { pos } =>
write!(f, "{} in {}: integer overflow", panic, fmt::Show(pos, context)),
Self::DivisionByZero { pos } =>
write!(f, "{} in {}: division by zero", panic, fmt::Show(pos, context)),
Self::IndexOutOfBounds { index, pos } =>
write!(
f,
"{} in {}: index ({}) out of bounds",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(index, context))
),
Self::EmptyCollection { pos } =>
write!(f, "{} in {}: collection is empty", panic, fmt::Show(pos, context)),
Self::InvalidCall { function, pos } =>
write!(
f,
"{} in {}: attempt to call ({}), which is not a function",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(function, context))
),
Self::InvalidArgs { supplied, expected, pos } =>
write!(
f,
"{} in {}: incorrect amount of function parameters -- supplied {}, expected {}",
panic,
fmt::Show(pos, context),
supplied,
expected
),
Self::InvalidCondition { value, pos } =>
write!(
f,
"{} in {}: condition ({}) is not a boolean",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(value, context))
),
Self::TypeError { value, expected, pos } =>
write!(
f,
"{} in {}: value ({}) has unexpected type, expected {}",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(value, context)),
expected,
),
Self::ValueError { value, message, pos } =>
write!(
f,
"{} in {}: invalid value ({}), expected {}",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(value, context)),
message,
),
Self::InvalidCommandArgs { object, items, pos } =>
write!(
f,
"{} in {}: {} expansion resulted in {} items",
panic,
fmt::Show(pos, context),
object,
items
),
Self::Io { error, pos } =>
write!(f, "{} in {}: {}", panic, fmt::Show(pos, context), error),
Self::UnsupportedFileDescriptor { fd, pos } =>
write!(
f,
"{} in {}: unsupported file descriptor ({})",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fd)
),
Self::InvalidPattern { pattern, pos } =>
write!(
f,
"{} in {}: pattern ({:?}) has invalid UTF-8",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, pattern)
),
Self::AssignToReadonlyField { field, pos } => write!(
f,
"{} in {}: attempt to assign field ({}), which is readonly",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(field, context))
),
Self::AssertionFailed { pos } =>
write!(f, "{} in {}: assertion failed", panic, fmt::Show(pos, context)),
Self::ImportFailed { path, pos } =>
write!(
f,
"{} in {}: failed to import module ({})",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(path, context))
),
Self::InvalidJoin { pos } =>
write!(f, "{} in {}: attempt to call join more than once", panic, fmt::Show(pos, context)),
Self::User { context: value, pos } =>
write!(
f,
"{} in {}: std.panic({})",
panic,
fmt::Show(pos, context),
color::Fg(color::Yellow, fmt::Show(value, context))
),
}
}
}
impl std::fmt::Display for Panic {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
Display::fmt(self, f, &symbol::Interner::new())
}
}
impl std::error::Error for Panic { }