mod geometry;
mod ids;
mod layer;
mod properties;
mod tile;
use std::cell::RefCell;
use std::f64;
use ids::IdState;
use js_sys::Uint8Array;
use layer::DecodedLayer;
use mlt_core::borrowme::ToOwned;
use mlt_core::v01::{Geometry, GeometryType, Id};
use mlt_core::{MltError, parse_layers};
use tile::MltTile;
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn decode_tile(data: &[u8]) -> Result<MltTile, JsError> {
let raw_layers = parse_layers(data).map_err(|e| to_js_err(&e))?;
let mut layers = Vec::with_capacity(raw_layers.len());
for raw_layer in raw_layers {
let mlt_core::Layer::Tag01(layer01) = raw_layer else {
continue;
};
let name = layer01.name.to_string();
let extent = layer01.extent;
let vector_types: Vec<GeometryType> = match &layer01.geometry {
Geometry::Encoded(encoded) => encoded
.meta
.clone()
.decode_bits_u32()
.map_err(|e| to_js_err(&e))?
.decode_u32()
.map_err(|e| to_js_err(&e))?
.into_iter()
.map::<Result<GeometryType, JsError>, _>(|v| {
u8::try_from(v)
.map_err(|_| JsError::new("invalid geometry type"))?
.try_into()
.map_err(|_| JsError::new("invalid geometry type"))
})
.collect::<Result<Vec<_>, _>>()?,
Geometry::Decoded(decoded) => decoded.vector_types.clone(),
};
let types_bytes: Vec<u8> = vector_types
.iter()
.map(|t| match t {
GeometryType::Point | GeometryType::MultiPoint => 1,
GeometryType::LineString | GeometryType::MultiLineString => 2,
GeometryType::Polygon | GeometryType::MultiPolygon => 3,
#[allow(unreachable_patterns)]
_ => 0,
})
.collect();
let types_array = Uint8Array::from(types_bytes.as_slice());
let geometry = RefCell::new(ToOwned::to_owned(&layer01.geometry));
let ids = RefCell::new(match &layer01.id {
Id::Encoded(Some(encoded)) => IdState::Encoded(ToOwned::to_owned(encoded)),
Id::Encoded(None) | Id::Decoded(None) => IdState::Absent,
Id::Decoded(Some(decoded)) => {
use js_sys::Float64Array;
let floats: Vec<f64> = decoded
.values()
.iter()
.copied()
.map(|f| {
#[expect(clippy::cast_precision_loss)]
f.map_or(f64::NAN, |f| f as f64)
})
.collect();
IdState::Ready(Float64Array::from(floats.as_slice()))
}
});
let props = RefCell::new(layer01.properties.iter().map(ToOwned::to_owned).collect());
layers.push(DecodedLayer {
name,
extent,
types_array,
geometry,
geometry_cache: RefCell::new(None),
ids,
props,
prop_cache: RefCell::new(None),
});
}
Ok(MltTile { layers })
}
pub(crate) fn to_js_err(e: &MltError) -> JsError {
JsError::new(&e.to_string())
}