use js_sys::{Array, Float32Array, Float64Array, Int8Array, Int32Array, Uint8Array, Uint32Array};
use mlt_core::v01::{DecodedProperty, OwnedProperty};
use wasm_bindgen::prelude::*;
pub(crate) struct PropCache {
pub(crate) keys: Array,
pub(crate) columns: Array,
}
pub(crate) fn build_prop_cache(props: &[OwnedProperty], feature_count: u32) -> PropCache {
let keys = Array::new();
let columns = Array::new();
for p in props {
let OwnedProperty::Decoded(prop) = p else {
continue;
};
if let DecodedProperty::SharedDict(shared_dict) = prop {
for item in &shared_dict.items {
let key = format!("{}{}", prop.name(), item.suffix);
keys.push(&JsValue::from_str(&key));
let col = Array::new_with_length(feature_count);
for i in 0_u32..feature_count {
if let Some(s) = item.get(shared_dict, i as usize) {
col.set(i, JsValue::from_str(s));
}
}
columns.push(&col);
}
} else {
keys.push(&JsValue::from_str(prop.name()));
columns.push(&prop_values_to_js_column(prop, feature_count));
}
}
PropCache { keys, columns }
}
#[allow(clippy::cast_precision_loss)]
pub(crate) fn prop_values_to_js_column(prop: &DecodedProperty, n: u32) -> JsValue {
match prop {
DecodedProperty::Bool(v) => {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(b) = val {
arr.set(i, JsValue::from_bool(*b));
}
}
arr.into()
}
DecodedProperty::I8(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(f64::from(*n)));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Int8Array::from(buf.as_slice()).into()
}
}
DecodedProperty::U8(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(f64::from(*n)));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Uint8Array::from(buf.as_slice()).into()
}
}
DecodedProperty::I32(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(f64::from(*n)));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Int32Array::from(buf.as_slice()).into()
}
}
DecodedProperty::U32(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(f64::from(*n)));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Uint32Array::from(buf.as_slice()).into()
}
}
DecodedProperty::I64(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(*n as f64));
}
}
arr.into()
} else {
let buf = v
.values
.iter()
.flatten()
.copied()
.map(|n| n as f64)
.collect::<Vec<_>>();
Float64Array::from(buf.as_slice()).into()
}
}
DecodedProperty::U64(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(*n as f64));
}
}
arr.into()
} else {
let buf = v
.values
.iter()
.flatten()
.copied()
.map(|n| n as f64)
.collect::<Vec<_>>();
Float64Array::from(buf.as_slice()).into()
}
}
DecodedProperty::F32(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(f64::from(*n)));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Float32Array::from(buf.as_slice()).into()
}
}
DecodedProperty::F64(v) => {
if v.values.iter().any(Option::is_none) {
let arr = Array::new_with_length(n);
for (val, i) in v.values.iter().zip(0_u32..) {
if let Some(n) = val {
arr.set(i, JsValue::from_f64(*n));
}
}
arr.into()
} else {
let buf = v.values.iter().flatten().copied().collect::<Vec<_>>();
Float64Array::from(buf.as_slice()).into()
}
}
DecodedProperty::Str(_, v) => {
let arr = Array::new_with_length(n);
for i in 0_u32..n {
if let Some(s) = v.get(i) {
arr.set(i, JsValue::from_str(s));
}
}
arr.into()
}
DecodedProperty::SharedDict(..) => {
unreachable!("SharedDict is expanded by build_prop_cache before reaching here.")
}
}
}
#[allow(clippy::cast_precision_loss)]
pub(crate) fn prop_to_js(prop: &DecodedProperty, i: usize) -> Option<JsValue> {
match prop {
DecodedProperty::Bool(v) => v.values[i].map(JsValue::from_bool),
DecodedProperty::I8(v) => v.values[i].map(|n| JsValue::from_f64(f64::from(n))),
DecodedProperty::U8(v) => v.values[i].map(|n| JsValue::from_f64(f64::from(n))),
DecodedProperty::I32(v) => v.values[i].map(|n| JsValue::from_f64(f64::from(n))),
DecodedProperty::U32(v) => v.values[i].map(|n| JsValue::from_f64(f64::from(n))),
DecodedProperty::I64(v) => v.values[i].map(|n| JsValue::from_f64(n as f64)),
DecodedProperty::U64(v) => v.values[i].map(|n| JsValue::from_f64(n as f64)),
DecodedProperty::F32(v) => v.values[i].map(|n| JsValue::from_f64(f64::from(n))),
DecodedProperty::F64(v) => v.values[i].map(JsValue::from_f64),
DecodedProperty::Str(_, v) => u32::try_from(i)
.ok()
.and_then(|i| v.get(i))
.map(JsValue::from_str),
DecodedProperty::SharedDict(..) => None,
}
}