use std::{fmt, ops::Deref};
use crate::{convert::Coerced, qjs, Ctx, Error, FromJs, IntoJs, Object, Result, Value};
#[repr(transparent)]
pub struct Exception<'js>(Object<'js>);
impl fmt::Debug for Exception<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Exception")
.field("object", &self.0)
.field("message", &self.message())
.field("file", &self.file())
.field("line", &self.line())
.field("stack", &self.stack())
.finish()
}
}
impl<'js> Exception<'js> {
pub fn into_object(self) -> Object<'js> {
self.0
}
pub fn as_object(&self) -> &Object<'js> {
&self.0
}
pub fn into_value(self) -> Value<'js> {
self.0.into_value()
}
pub fn from_object(obj: Object<'js>) -> Option<Self> {
if obj.is_error() {
Some(Self(obj))
} else {
None
}
}
pub fn from_message(ctx: Ctx<'js>, message: &str) -> Result<Self> {
let obj = unsafe {
let value = ctx.handle_exception(qjs::JS_NewError(ctx.as_ptr()))?;
Value::from_js_value(ctx, value)
.into_object()
.expect("`JS_NewError` did not return an object")
};
obj.set("message", message)?;
Ok(Exception(obj))
}
pub fn message(&self) -> Option<String> {
self.get::<_, Option<Coerced<String>>>("message")
.ok()
.and_then(|x| x)
.map(|x| x.0)
}
pub fn file(&self) -> Option<String> {
self.get::<_, Option<Coerced<String>>>("fileName")
.ok()
.and_then(|x| x)
.map(|x| x.0)
}
pub fn line(&self) -> Option<i32> {
self.get::<_, Option<Coerced<i32>>>("lineNumber")
.ok()
.and_then(|x| x)
.map(|x| x.0)
}
pub fn stack(&self) -> Option<String> {
self.get::<_, Option<Coerced<String>>>("stack")
.ok()
.and_then(|x| x)
.map(|x| x.0)
}
pub fn throw(self) -> Error {
self.0.ctx.throw(self.0.into_value())
}
}
impl fmt::Display for Exception<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
"Exception generated by quickjs: ".fmt(f)?;
if let Some(file) = self.file() {
'['.fmt(f)?;
file.fmt(f)?;
']'.fmt(f)?;
}
if let Some(line) = self.line() {
':'.fmt(f)?;
line.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)?;
}
Ok(())
}
}
impl<'js> Deref for Exception<'js> {
type Target = Object<'js>;
fn deref(&self) -> &Self::Target {
self.as_object()
}
}
impl<'js> FromJs<'js> for Exception<'js> {
fn from_js(_ctx: crate::Ctx<'js>, value: Value<'js>) -> Result<Self> {
if let Some(obj) = value.as_object() {
if obj.is_error() {
return Ok(Exception(obj.clone()));
} else {
return Err(Error::FromJs {
from: value.type_name(),
to: "Exception",
message: Some("object was not an instance of error".to_string()),
});
}
}
return Err(Error::FromJs {
from: value.type_name(),
to: "Exception",
message: Some("value was not a type".to_string()),
});
}
}
impl<'js> IntoJs<'js> for Exception<'js> {
fn into_js(self, _ctx: crate::Ctx<'js>) -> Result<Value<'js>> {
Ok(self.0.into_value())
}
}