rquickjs_core/value/
atom.rsuse 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);
        }
    }
}