use colored::Colorize;
use serde::{Deserialize, Serialize};
use crate::{SData, SDataRef, SDoc, SFunc, SGraph, SVal};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum ErrorType {
TypeError,
CallError,
ParseError,
StdLibError(String),
ArrayLibError(String),
BlobLibError(String),
DataLibError(String),
BoolLibError(String),
FuncLibError(String),
MapLibError(String),
NumberLibError(String),
ObjectLibError(String),
SetLibError(String),
StringLibError(String),
TupleLibError(String),
FileSystemLibError(String),
TimeLibError(String),
FormatError(String),
ThrownError(String), ValueError(String),
Custom(String),
}
impl ErrorType {
pub fn to_string(&self) -> String {
match self {
Self::Custom(error) => error.clone(),
Self::ThrownError(error) => error.clone(),
_ => format!("{:?}", self),
}
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct SError {
pub pid: String,
pub error_type: ErrorType,
pub message: String,
pub call_stack: Vec<SDataRef>,
}
impl SError {
pub fn new(pid: &str, doc: &SDoc, etype: ErrorType, message: &str) -> Self {
let mut call_stack = Vec::new();
if let Some(process) = doc.processes.get(pid) {
call_stack = process.call_stack.clone();
}
Self {
pid: pid.to_owned(),
error_type: etype,
message: message.to_owned(),
call_stack,
}
}
pub fn type_error(pid: &str, doc: &SDoc, message: &str) -> Self {
Self::new(pid, doc, ErrorType::TypeError, message)
}
pub fn parse(pid: &str, doc: &SDoc, message: &str) -> Self {
Self::new(pid, doc, ErrorType::ParseError, message)
}
pub fn call(pid: &str, doc: &SDoc, message: &str) -> Self {
Self::new(pid, doc, ErrorType::CallError, message)
}
pub fn std(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::StdLibError(func.to_owned()), message)
}
pub fn array(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::ArrayLibError(func.to_owned()), message)
}
pub fn blob(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::BlobLibError(func.to_owned()), message)
}
pub fn data(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::DataLibError(func.to_owned()), message)
}
pub fn bool(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::BoolLibError(func.to_owned()), message)
}
pub fn func(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::FuncLibError(func.to_owned()), message)
}
pub fn map(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::MapLibError(func.to_owned()), message)
}
pub fn set(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::SetLibError(func.to_owned()), message)
}
pub fn string(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::StringLibError(func.to_owned()), message)
}
pub fn num(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::NumberLibError(func.to_owned()), message)
}
pub fn tup(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::TupleLibError(func.to_owned()), message)
}
pub fn obj(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::ObjectLibError(func.to_owned()), message)
}
pub fn filesys(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::FileSystemLibError(func.to_owned()), message)
}
pub fn time(pid: &str, doc: &SDoc, func: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::TimeLibError(func.to_owned()), message)
}
pub fn thrown(pid: &str, doc: &SDoc, etype: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::ThrownError(etype.to_owned()), message)
}
pub fn val(pid: &str, doc: &SDoc, op: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::ValueError(op.to_owned()), message)
}
pub fn custom(pid: &str, doc: &SDoc, error: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::Custom(error.to_owned()), message)
}
pub fn fmt(pid: &str, doc: &SDoc, format: &str, message: &str) -> Self {
Self::new(pid, doc, ErrorType::FormatError(format.to_owned()), message)
}
pub fn empty_fmt(format: &str, message: &str) -> Self {
Self {
pid: "main".to_string(),
error_type: ErrorType::FormatError(format.to_string()),
message: message.to_owned(),
call_stack: Default::default(),
}
}
pub fn to_value(&self) -> SVal {
let call_stack = self.call_stack.iter().map(|dref| SVal::FnPtr(dref.clone())).collect::<Vec<SVal>>();
SVal::Tuple(vec![SVal::String(self.error_type.to_string()), SVal::String(self.message.to_owned()), SVal::Array(call_stack)])
}
pub fn to_string(&self, graph: &SGraph) -> String {
let mut res = String::default();
for dref in &self.call_stack {
if let Some(func) = SData::get::<SFunc>(&graph, dref) {
let func_nodes = dref.nodes(&graph);
let func_path;
if func_nodes.len() > 0 {
func_path = func_nodes.first().unwrap().path(&graph);
} else {
func_path = String::from("<unknown>");
}
res.push_str(&format!("\t{} {} {} {} ...\n", "unwind".red(), func.name.blue(), "@".dimmed(), func_path.italic().bright_cyan()));
}
}
res.push_str(&format!("\t{}: {}", self.error_type.to_string().purple(), self.message.dimmed()));
res
}
}