use ffi;
use std::any::TypeId;
use std::error::Error as StdError;
use std::{fmt, result};
pub type Result<T> = result::Result<T, Error>;
#[derive(Debug)]
pub struct Error {
pub kind: ErrorKind,
pub context: Vec<String>,
}
#[derive(Debug)]
pub enum ErrorKind {
ToJsConversionError {
from: &'static str,
to: &'static str,
},
FromJsConversionError {
from: &'static str,
to: &'static str,
},
RuntimeError {
code: RuntimeErrorCode,
name: String,
},
RecursiveMutCallback,
ExternalError(Box<dyn RuntimeError + 'static>),
NotAFunction,
}
impl Error {
pub fn external<T: RuntimeError + 'static>(error: T) -> Error {
Error {
kind: ErrorKind::ExternalError(Box::new(error)),
context: vec![],
}
}
pub fn from_js_conversion(from: &'static str, to: &'static str) -> Error {
Error {
kind: ErrorKind::FromJsConversionError { from, to },
context: vec![],
}
}
pub fn to_js_conversion(from: &'static str, to: &'static str) -> Error {
Error {
kind: ErrorKind::ToJsConversionError { from, to },
context: vec![],
}
}
pub fn recursive_mut_callback() -> Error {
Error { kind: ErrorKind::RecursiveMutCallback, context: vec![] }
}
pub fn not_a_function() -> Error {
Error { kind: ErrorKind::NotAFunction, context: vec![] }
}
pub(crate) fn into_runtime_error_desc(self) -> RuntimeErrorDesc {
RuntimeErrorDesc {
code: self.runtime_code(),
name: self.runtime_name(),
message: self.runtime_message(),
cause: Box::new(self),
}
}
fn runtime_code(&self) -> RuntimeErrorCode {
match &self.kind {
ErrorKind::ToJsConversionError { .. } => RuntimeErrorCode::TypeError,
ErrorKind::FromJsConversionError { .. } => RuntimeErrorCode::TypeError,
ErrorKind::NotAFunction => RuntimeErrorCode::TypeError,
ErrorKind::ExternalError(err) => err.code(),
_ => RuntimeErrorCode::Error
}
}
fn runtime_name(&self) -> String {
match &self.kind {
ErrorKind::ExternalError(err) => err.name(),
_ => self.runtime_code().to_string()
}
}
fn runtime_message(&self) -> Option<String> {
let mut message = String::new();
for context in self.context.iter().rev() {
if !message.is_empty() {
message.push_str(": ");
}
message.push_str(context);
}
if let ErrorKind::ExternalError(ref error) = self.kind {
if let Some(ref ext_message) = error.message() {
if !message.is_empty() {
message.push_str(": ");
}
message.push_str(ext_message);
}
}
if !message.is_empty() {
Some(message)
} else {
None
}
}
}
impl StdError for Error {
fn description(&self) -> &'static str {
"JavaScript execution error"
}
}
impl fmt::Display for Error {
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
for context in self.context.iter().rev() {
write!(fmt, "{}: ", context)?;
}
match self.kind {
ErrorKind::ToJsConversionError { from, to } => {
write!(fmt, "error converting {} to JavaScript {}", from, to)
},
ErrorKind::FromJsConversionError { from, to } => {
write!(fmt, "error converting JavaScript {} to {}", from, to)
},
ErrorKind::RuntimeError { ref name, .. } => {
write!(fmt, "JavaScript runtime error ({})", name)
},
ErrorKind::RecursiveMutCallback => write!(fmt, "mutable callback called recursively"),
ErrorKind::NotAFunction => write!(fmt, "tried to a call a non-function"),
ErrorKind::ExternalError(ref err) => err.fmt(fmt),
}
}
}
pub trait ResultExt {
fn js_err_context<D: fmt::Display>(self, context: D) -> Self;
fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(self, op: F) -> Self;
}
impl<T> ResultExt for result::Result<T, Error> {
fn js_err_context<D: fmt::Display>(self, context: D) -> Self {
match self {
Err(mut err) => {
err.context.push(context.to_string());
Err(err)
},
result => result,
}
}
fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(self, op: F) -> Self {
match self {
Err(mut err) => {
let context = op(&err).to_string();
err.context.push(context);
Err(err)
},
result => result,
}
}
}
impl ResultExt for Error {
fn js_err_context<D: fmt::Display>(mut self, context: D) -> Self {
self.context.push(context.to_string());
self
}
fn js_err_context_with<D: fmt::Display, F: FnOnce(&Error) -> D>(mut self, op: F) -> Self {
let context = op(&self).to_string();
self.context.push(context);
self
}
}
pub(crate) struct RuntimeErrorDesc {
pub code: RuntimeErrorCode,
pub name: String,
pub message: Option<String>,
pub cause: Box<Error>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum RuntimeErrorCode {
Error,
EvalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
UriError,
}
impl RuntimeErrorCode {
pub(crate) fn from_duk_errcode(code: ffi::duk_errcode_t) -> RuntimeErrorCode {
match code as u32 {
ffi::DUK_ERR_ERROR => RuntimeErrorCode::Error,
ffi::DUK_ERR_EVAL_ERROR => RuntimeErrorCode::EvalError,
ffi::DUK_ERR_RANGE_ERROR => RuntimeErrorCode::RangeError,
ffi::DUK_ERR_REFERENCE_ERROR => RuntimeErrorCode::ReferenceError,
ffi::DUK_ERR_SYNTAX_ERROR => RuntimeErrorCode::SyntaxError,
ffi::DUK_ERR_TYPE_ERROR => RuntimeErrorCode::TypeError,
ffi::DUK_ERR_URI_ERROR => RuntimeErrorCode::UriError,
_ => RuntimeErrorCode::Error,
}
}
pub(crate) fn to_duk_errcode(&self) -> ffi::duk_errcode_t {
(match *self {
RuntimeErrorCode::Error => ffi::DUK_ERR_ERROR,
RuntimeErrorCode::EvalError => ffi::DUK_ERR_EVAL_ERROR,
RuntimeErrorCode::RangeError => ffi::DUK_ERR_RANGE_ERROR,
RuntimeErrorCode::ReferenceError => ffi::DUK_ERR_REFERENCE_ERROR,
RuntimeErrorCode::SyntaxError => ffi::DUK_ERR_SYNTAX_ERROR,
RuntimeErrorCode::TypeError => ffi::DUK_ERR_TYPE_ERROR,
RuntimeErrorCode::UriError => ffi::DUK_ERR_URI_ERROR,
}) as ffi::duk_errcode_t
}
}
impl fmt::Display for RuntimeErrorCode {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match *self {
RuntimeErrorCode::Error => write!(f, "Error"),
RuntimeErrorCode::EvalError => write!(f, "EvalError"),
RuntimeErrorCode::RangeError => write!(f, "RangeError"),
RuntimeErrorCode::ReferenceError => write!(f, "ReferenceError"),
RuntimeErrorCode::SyntaxError => write!(f, "SyntaxError"),
RuntimeErrorCode::TypeError => write!(f, "TypeError"),
RuntimeErrorCode::UriError => write!(f, "URIError"),
}
}
}
pub trait RuntimeError: fmt::Debug {
fn code(&self) -> RuntimeErrorCode {
RuntimeErrorCode::Error
}
fn name(&self) -> String {
self.code().to_string()
}
fn message(&self) -> Option<String> {
None
}
#[doc(hidden)]
fn __private_get_type_id__(&self) -> TypeId where Self: 'static {
TypeId::of::<Self>()
}
}
impl dyn RuntimeError {
pub fn downcast_ref<T: RuntimeError + 'static>(&self) -> Option<&T> {
if self.__private_get_type_id__() == TypeId::of::<T>() {
unsafe { Some(&*(self as *const dyn RuntimeError as *const T)) }
} else {
None
}
}
}
impl RuntimeError for () {
}
impl RuntimeError for String {
fn message(&self) -> Option<String> {
Some(self.clone())
}
}
impl<'a> RuntimeError for &'a str {
fn message(&self) -> Option<String> {
Some(self.to_string())
}
}
impl<T: RuntimeError + 'static> From<T> for Error {
fn from(error: T) -> Error {
Error::external(error)
}
}
impl From<ErrorKind> for Error {
fn from(error: ErrorKind) -> Error {
Error {
kind: error,
context: vec![],
}
}
}