use bytes::Bytes;
use crate::function::{Constructor, Optional};
use crate::{
Class, ClassSetup, FromJSValue, HostError, IntoJSValue, JSArrayOps, JSClass, JSContext,
JSErrorFactory, JSExceptionThrower, JSObject, JSObjectOps, JSResult, JSTypeOf, JSValue,
JSValueConversion, JSValueImpl, PropertyDescriptor,
};
use std::ops::Deref;
#[derive(Clone)]
pub(crate) struct JSBytesData {
bytes: Bytes,
}
#[derive(Clone)]
pub struct JSBytes<V: JSValueImpl> {
inner: JSObject<V>,
}
impl<V: JSValueImpl> Deref for JSBytes<V> {
type Target = JSObject<V>;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<V> JSBytes<V>
where
V: JSValueImpl + JSObjectOps + JSTypeOf + JSValueConversion + JSArrayOps + 'static,
V::Context: JSErrorFactory + JSExceptionThrower,
{
pub fn len(&self) -> JSResult<usize> {
Ok(self.inner.borrow::<JSBytesData>()?.bytes.len())
}
pub fn is_empty(&self) -> JSResult<bool> {
Ok(self.inner.borrow::<JSBytesData>()?.bytes.is_empty())
}
pub fn to_bytes(&self) -> JSResult<Bytes> {
Ok(self.inner.borrow::<JSBytesData>()?.bytes.clone())
}
pub(crate) fn to_vec(&self) -> JSResult<Vec<u8>> {
Ok(self.to_bytes()?.to_vec())
}
pub fn to_string(&self) -> JSResult<String> {
String::from_utf8(self.to_vec()?).map_err(|err| {
HostError::new(
crate::error::E_TYPE,
format!("JSBytes contains invalid UTF-8: {}", err),
)
.with_name("TypeError")
.into()
})
}
pub(crate) fn from_object(obj: JSObject<V>) -> Option<Self> {
if Class::instance_of::<JSBytesData>(&obj) {
Some(Self { inner: obj })
} else {
None
}
}
}
impl<V> JSBytes<V>
where
V: JSValueImpl + JSObjectOps + JSTypeOf + JSValueConversion + JSArrayOps + 'static,
V::Context: JSErrorFactory + JSExceptionThrower,
{
pub fn from_bytes(ctx: &JSContext<V::Context>, bytes: Bytes) -> JSResult<Self> {
ctx.register_hidden_class::<JSBytesData>()?;
let instance = Class::lookup::<JSBytesData>(ctx)?.instance(JSBytesData { bytes });
Ok(Self { inner: instance })
}
pub fn from_string<S>(ctx: &JSContext<V::Context>, text: S) -> JSResult<Self>
where
S: Into<String>,
{
Self::from_bytes(ctx, Bytes::from(text.into()))
}
}
impl<V> JSClass<V> for JSBytesData
where
V: JSValueImpl + JSObjectOps + JSTypeOf + JSValueConversion + JSArrayOps + 'static,
V::Context: JSErrorFactory + JSExceptionThrower,
{
const NAME: &'static str = "JSBytes";
fn data_constructor() -> Constructor<V> {
Constructor::new(
|_ctx: JSContext<V::Context>, _arg: Optional<JSValue<V>>| -> JSResult<JSBytes<V>> {
crate::illegal_constructor("JSBytes cannot be constructed from JavaScript")
},
)
}
fn call_without_new() -> Constructor<V> {
Constructor::new(
|_ctx: JSContext<V::Context>, _arg: Optional<JSValue<V>>| -> JSResult<JSBytes<V>> {
crate::illegal_constructor("JSBytes cannot be constructed from JavaScript")
},
)
}
fn class_setup(class: &ClassSetup<V>) -> JSResult<()> {
let getter = class.new_func(|this: crate::function::This<JSBytes<V>>| this.len())?;
class.property(
"length",
PropertyDescriptor::from_getter(getter).configurable(),
)?;
class.method("toString", |this: crate::function::This<JSBytes<V>>| {
this.to_string()
})?;
Ok(())
}
}
impl<V> IntoJSValue<V> for JSBytes<V>
where
V: JSValueImpl,
{
fn into_js_value(self, _ctx: &JSContext<V::Context>) -> JSValue<V> {
self.inner.into_js_value()
}
}
impl<V> FromJSValue<V> for JSBytes<V>
where
V: JSValueImpl + JSTypeOf + JSObjectOps + JSValueConversion + JSArrayOps + 'static,
V::Context: JSErrorFactory + JSExceptionThrower,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
let obj = JSObject::from_js_value(ctx, value)?;
Self::from_object(obj).ok_or_else(|| {
HostError::new(crate::error::E_TYPE, "Value is not a JSBytes instance")
.with_name("TypeError")
.into()
})
}
}
impl<V> FromJSValue<V> for Bytes
where
V: JSValueImpl + JSTypeOf + JSObjectOps + JSValueConversion + JSArrayOps + 'static,
V::Context: JSErrorFactory + JSExceptionThrower,
{
fn from_js_value(ctx: &JSContext<V::Context>, value: JSValue<V>) -> JSResult<Self> {
JSBytes::from_js_value(ctx, value)?.to_bytes()
}
}
impl<V: JSValueImpl> crate::function::JSParameterType for JSBytes<V> {}
impl crate::function::JSParameterType for Bytes {}