use crate::{qjs, Ctx, Error, Result, String, Value};
use std::{ffi::CStr, hash::Hash, string::String as StdString};
mod predefined;
pub use predefined::PredefinedAtom;
#[derive(Debug)]
pub struct Atom<'js> {
pub(crate) atom: qjs::JSAtom,
ctx: Ctx<'js>,
}
impl<'js> PartialEq for Atom<'js> {
fn eq(&self, other: &Self) -> bool {
self.atom == other.atom
}
}
impl<'js> Eq for Atom<'js> {}
impl<'js> Hash for Atom<'js> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u32(self.atom)
}
}
impl<'js> Atom<'js> {
pub fn from_value(ctx: Ctx<'js>, val: &Value<'js>) -> Result<Atom<'js>> {
let atom = unsafe { qjs::JS_ValueToAtom(ctx.as_ptr(), val.as_js_value()) };
if atom == qjs::JS_ATOM_NULL {
return Err(ctx.raise_exception());
}
Ok(Atom { atom, ctx })
}
pub fn from_u32(ctx: Ctx<'js>, val: u32) -> Result<Atom<'js>> {
let atom = unsafe { qjs::JS_NewAtomUInt32(ctx.as_ptr(), val) };
if atom == qjs::JS_ATOM_NULL {
return Err(Error::Exception);
}
Ok(Atom { atom, ctx })
}
pub fn from_i32(ctx: Ctx<'js>, val: i32) -> Result<Atom<'js>> {
let atom =
unsafe { qjs::JS_ValueToAtom(ctx.as_ptr(), qjs::JS_MKVAL(qjs::JS_TAG_INT, val)) };
if atom == qjs::JS_ATOM_NULL {
return Err(Error::Exception);
}
Ok(Atom { atom, ctx })
}
pub fn from_bool(ctx: Ctx<'js>, val: bool) -> Result<Atom<'js>> {
let val = if val { qjs::JS_TRUE } else { qjs::JS_FALSE };
let atom = unsafe { qjs::JS_ValueToAtom(ctx.as_ptr(), val) };
if atom == qjs::JS_ATOM_NULL {
return Err(Error::Exception);
}
Ok(Atom { atom, ctx })
}
pub fn from_f64(ctx: Ctx<'js>, val: f64) -> Result<Atom<'js>> {
let atom = unsafe { qjs::JS_ValueToAtom(ctx.as_ptr(), qjs::JS_NewFloat64(val)) };
if atom == qjs::JS_ATOM_NULL {
return Err(Error::Exception);
}
Ok(Atom { atom, ctx })
}
pub fn from_str(ctx: Ctx<'js>, name: &str) -> Result<Atom<'js>> {
unsafe {
let ptr = name.as_ptr() as *const std::os::raw::c_char;
let atom = qjs::JS_NewAtomLen(ctx.as_ptr(), ptr, name.len() as _);
if atom == qjs::JS_ATOM_NULL {
return Err(Error::Exception);
}
Ok(Atom { atom, ctx })
}
}
pub fn from_predefined(ctx: Ctx<'js>, predefined: PredefinedAtom) -> Atom<'js> {
unsafe { Atom::from_atom_val(ctx, predefined as qjs::JSAtom) }
}
pub fn to_string(&self) -> Result<StdString> {
unsafe {
let c_str = qjs::JS_AtomToCString(self.ctx.as_ptr(), self.atom);
if c_str.is_null() {
qjs::JS_FreeCString(self.ctx.as_ptr(), c_str);
return Err(Error::Unknown);
}
let bytes = CStr::from_ptr(c_str).to_bytes();
let res = std::str::from_utf8_unchecked(bytes).to_string();
qjs::JS_FreeCString(self.ctx.as_ptr(), c_str);
Ok(res)
}
}
pub fn to_js_string(&self) -> Result<String<'js>> {
unsafe {
let val = qjs::JS_AtomToString(self.ctx.as_ptr(), self.atom);
let val = self.ctx.handle_exception(val)?;
Ok(String::from_js_value(self.ctx.clone(), val))
}
}
pub fn to_value(&self) -> Result<Value<'js>> {
self.to_js_string().map(|String(value)| value)
}
pub(crate) unsafe fn from_atom_val(ctx: Ctx<'js>, val: qjs::JSAtom) -> Self {
Atom { atom: val, ctx }
}
pub(crate) unsafe fn from_atom_val_dup(ctx: Ctx<'js>, val: qjs::JSAtom) -> Self {
qjs::JS_DupAtom(ctx.as_ptr(), val);
Atom { atom: val, ctx }
}
}
impl<'js> Clone for Atom<'js> {
fn clone(&self) -> Atom<'js> {
let atom = unsafe { qjs::JS_DupAtom(self.ctx.as_ptr(), self.atom) };
Atom {
atom,
ctx: self.ctx.clone(),
}
}
}
impl<'js> Drop for Atom<'js> {
fn drop(&mut self) {
unsafe {
qjs::JS_FreeAtom(self.ctx.as_ptr(), self.atom);
}
}
}