use std::collections::{BTreeMap, HashMap};
use rquickjs::{
atom::PredefinedAtom, Array, ArrayBuffer, Coerced, Ctx, Exception, FromJs, Function, IntoAtom,
IntoJs, Object, Result, Symbol, TypedArray, Value,
};
use super::result::ResultExt;
#[allow(dead_code)]
pub fn array_to_hash_map<'js>(
ctx: &Ctx<'js>,
array: Array<'js>,
) -> Result<HashMap<String, String>> {
let value = object_from_entries(ctx, array)?;
let value = value.into_value();
HashMap::from_js(ctx, value)
}
pub fn array_to_btree_map<'js>(
ctx: &Ctx<'js>,
array: Array<'js>,
) -> Result<BTreeMap<String, Coerced<String>>> {
let value = object_from_entries(ctx, array)?;
let value = value.into_value();
BTreeMap::from_js(ctx, value)
}
pub fn object_from_entries<'js>(ctx: &Ctx<'js>, array: Array<'js>) -> Result<Object<'js>> {
let obj = Object::new(ctx.clone())?;
for value in array.into_iter().flatten() {
if let Some(entry) = value.as_array() {
if let Ok(key) = entry.get::<Value>(0) {
if let Ok(value) = entry.get::<Value>(1) {
let _ = obj.set(key, value); }
}
}
}
Ok(obj)
}
pub fn map_to_entries<'js, K, V, M>(ctx: &Ctx<'js>, map: M) -> Result<Array<'js>>
where
M: IntoIterator<Item = (K, V)>,
K: IntoJs<'js>,
V: IntoJs<'js>,
{
let array = Array::new(ctx.clone())?;
for (idx, (key, value)) in map.into_iter().enumerate() {
let entry = Array::new(ctx.clone())?;
entry.set(0, key)?;
entry.set(1, value)?;
array.set(idx, entry)?;
}
Ok(array)
}
pub fn get_start_end_indexes(
source_len: usize,
target_len: Option<usize>,
offset: usize,
) -> (usize, usize) {
if offset > source_len {
return (0, 0);
}
let target_len = target_len.unwrap_or(source_len - offset);
if offset + target_len > source_len {
return (offset, source_len);
}
(offset, target_len + offset)
}
pub fn get_bytes_offset_length<'js>(
ctx: &Ctx<'js>,
value: Value<'js>,
offset: usize,
length: Option<usize>,
) -> Result<Vec<u8>> {
if let Some(bytes) = get_string_bytes(&value, offset, length)? {
return Ok(bytes);
}
if let Some(bytes) = get_array_bytes(ctx, &value, offset, length)? {
return Ok(bytes);
}
if let Some(obj) = value.as_object() {
if let Some((array_buffer, source_length, source_offset)) = obj_to_array_buffer(obj)? {
let (start, end) = get_start_end_indexes(source_length, length, offset);
let bytes: &[u8] = array_buffer.as_ref();
return Ok(bytes[start + source_offset..end - source_offset].to_vec());
}
}
if let Some(bytes) = get_coerced_string_bytes(&value, offset, length) {
return Ok(bytes);
}
Err(Exception::throw_message(
ctx,
"value must be typed DataView, Buffer, ArrayBuffer, Uint8Array or interpretable as string",
))
}
pub fn get_array_bytes<'js>(
ctx: &Ctx<'js>,
value: &Value<'js>,
offset: usize,
length: Option<usize>,
) -> Result<Option<Vec<u8>>> {
if value.is_array() {
let array = value.as_array().unwrap();
let (start, end) = get_start_end_indexes(array.len(), length, offset);
let size = end - start;
let mut bytes: Vec<u8> = Vec::with_capacity(size);
for val in array.iter::<u8>().skip(start).take(size) {
let val: u8 = val.or_throw_msg(ctx, "array value is not u8")?;
bytes.push(val);
}
return Ok(Some(bytes));
}
Ok(None)
}
pub fn get_coerced_string_bytes(
value: &Value<'_>,
offset: usize,
length: Option<usize>,
) -> Option<Vec<u8>> {
if let Ok(val) = value.get::<Coerced<String>>() {
let string = val.to_string();
return Some(bytes_from_js_string(string, offset, length));
};
None
}
#[inline]
pub fn get_string_bytes(
value: &Value<'_>,
offset: usize,
length: Option<usize>,
) -> Result<Option<Vec<u8>>> {
if let Some(val) = value.as_string() {
let string = val.to_string()?;
return Ok(Some(bytes_from_js_string(string, offset, length)));
}
Ok(None)
}
fn bytes_from_js_string(string: String, offset: usize, length: Option<usize>) -> Vec<u8> {
let (start, end) = get_start_end_indexes(string.len(), length, offset);
string.as_bytes()[start..end].to_vec()
}
pub fn obj_to_array_buffer<'js>(
obj: &Object<'js>,
) -> Result<Option<(ArrayBuffer<'js>, usize, usize)>> {
if let Ok(typed_array) = TypedArray::<u8>::from_object(obj.clone()) {
let byte_length = typed_array.len();
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Some(array_buffer) = ArrayBuffer::from_object(obj.clone()) {
let byte_length = array_buffer.len();
return Ok(Some((array_buffer, byte_length, 0)));
}
if let Ok(typed_array) = TypedArray::<i8>::from_object(obj.clone()) {
let byte_length = typed_array.len();
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<u16>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 2;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<i16>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 2;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<u32>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 4;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<i32>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 4;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<u64>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 8;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<i64>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 8;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<f32>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 4;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(typed_array) = TypedArray::<f64>::from_object(obj.clone()) {
let byte_length = typed_array.len() * 8;
let offset: usize = typed_array.get("byteOffset")?;
return Ok(Some((typed_array.arraybuffer()?, byte_length, offset)));
}
if let Ok(array_buffer) = obj.get::<_, ArrayBuffer>("buffer") {
let length = array_buffer.len();
return Ok(Some((array_buffer, length, 0)));
}
Ok(None)
}
pub fn get_array_buffer_bytes(
array_buffer: ArrayBuffer<'_>,
start: usize,
end_end: usize,
) -> Vec<u8> {
let bytes: &[u8] = array_buffer.as_ref();
bytes[start..end_end].to_vec()
}
pub fn get_bytes<'js>(ctx: &Ctx<'js>, value: Value<'js>) -> Result<Vec<u8>> {
get_bytes_offset_length(ctx, value, 0, None)
}
pub fn bytes_to_typed_array<'js>(ctx: Ctx<'js>, bytes: &[u8]) -> Result<Value<'js>> {
TypedArray::<u8>::new(ctx.clone(), bytes).into_js(&ctx)
}
pub trait ObjectExt<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>>;
}
impl<'js> ObjectExt<'js> for Object<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js> + Sized>(
&self,
k: K,
) -> Result<Option<V>> {
self.get::<K, Option<V>>(k)
}
}
impl<'js> ObjectExt<'js> for Value<'js> {
fn get_optional<K: IntoAtom<'js> + Clone, V: FromJs<'js>>(&self, k: K) -> Result<Option<V>> {
if let Some(obj) = self.as_object() {
return obj.get_optional(k);
}
Ok(None)
}
}
pub trait CreateSymbol<'js> {
fn for_description(globals: &Object<'js>, description: &'static str) -> Result<Symbol<'js>>;
}
impl<'js> CreateSymbol<'js> for Symbol<'js> {
fn for_description(globals: &Object<'js>, description: &'static str) -> Result<Symbol<'js>> {
let symbol_function: Function = globals.get(PredefinedAtom::Symbol)?;
let for_function: Function = symbol_function.get(PredefinedAtom::For)?;
for_function.call((description,))
}
}