use super::{JSError, JSErrorFactory};
use crate::{
FromJSValue, HostError, IntoJSValue, JSContext, JSContextImpl, JSObject, JSObjectOps, JSResult,
JSTypeOf, JSValue, JSValueImpl,
};
use std::fmt;
use std::ops::Deref;
use std::string::String;
pub struct JSException<V: JSValueImpl>(JSObject<V>);
impl<V: JSValueImpl> Deref for JSException<V> {
type Target = JSObject<V>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<V> IntoJSValue<V> for JSException<V>
where
V: JSValueImpl,
{
fn into_js_value(self, _ctx: &JSContext<V::Context>) -> JSValue<V> {
self.0.into_js_value()
}
}
impl<V> JSException<V>
where
V: JSValueImpl + JSTypeOf,
{
pub fn from_object(value: JSObject<V>) -> Option<Self> {
if value.is_exception() {
return Some(Self(value));
}
None
}
}
impl<V> FromJSValue<V> for JSException<V>
where
V: JSTypeOf,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
if value.is_exception() {
Ok(Self(JSObject::from_js_value(ctx, value)?))
} else {
Err(HostError::not_exception().into())
}
}
}
impl<V> JSException<V>
where
V: JSObjectOps,
{
pub fn message(&self) -> Option<String> {
self.get("message").ok()
}
pub fn stack(&self) -> Option<String> {
self.get("stack").ok()
}
pub fn into_error(self) -> JSError {
let ctx = self.context().clone();
if self.is_error() {
JSError {
message: self.message(),
stack: self.stack(),
}
} else {
let js_value = self.into_js_value(&ctx);
JSError {
message: String::from_js_value(&ctx, js_value).ok(),
stack: None,
}
}
}
}
pub trait JSExceptionThrower: JSContextImpl {
fn throw(&self, value: Self::Value) -> Self::Value;
}
impl<C> JSContext<C>
where
C: JSContextImpl + JSExceptionThrower,
C::Value: JSValueImpl,
{
pub fn throw(&self, value: JSValue<C::Value>) -> JSValue<C::Value> {
let raw = self.as_ref().throw(value.into_value());
JSValue::from_raw(self, raw)
}
}
impl<C> JSContext<C>
where
C: JSContextImpl + JSExceptionThrower + JSErrorFactory,
C::Value: JSValueImpl,
{
pub fn throw_named_error(
&self,
name: &str,
message: impl AsRef<str>,
code: Option<&str>,
) -> JSValue<C::Value> {
let raw = self.as_ref().new_error(name, message, code);
let raw = self.as_ref().throw(raw);
JSValue::from_raw(self, raw)
}
pub fn throw_syntax_error(&self, message: impl AsRef<str>) -> JSValue<C::Value> {
self.throw_named_error("SyntaxError", message, None)
}
pub fn throw_type_error(&self, message: impl AsRef<str>) -> JSValue<C::Value> {
self.throw_named_error("TypeError", message, None)
}
pub fn throw_reference_error(&self, message: impl AsRef<str>) -> JSValue<C::Value> {
self.throw_named_error("ReferenceError", message, None)
}
pub fn throw_range_error(&self, message: impl AsRef<str>) -> JSValue<C::Value> {
self.throw_named_error("RangeError", message, None)
}
pub fn throw_error(&self, message: impl AsRef<str>) -> JSValue<C::Value> {
self.throw_named_error("Error", message, None)
}
}
impl<V: JSObjectOps> fmt::Debug for JSException<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Exception")
.field("message", &self.message())
.field("stack", &self.stack())
.finish()
}
}
impl<V: JSObjectOps> fmt::Display for JSException<V> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
if self.is_error() {
"Error:".fmt(f)?;
if let Some(message) = &self.message() {
' '.fmt(f)?;
message.fmt(f)?;
}
if let Some(stack) = &self.stack() {
'\n'.fmt(f)?;
stack.fmt(f)?;
}
} else {
let ctx = self.context();
let value = self.as_js_value().clone();
String::from_js_value(&ctx, value).unwrap().fmt(f)?;
}
Ok(())
}
}
impl<V: JSValueImpl> crate::function::JSParameterType for JSException<V> {}