use js_sys::{Array, ArrayBuffer, Function, Object};
use perspective_client::{
ColumnWindow, OnUpdateData, OnUpdateOptions, ViewWindow, assert_view_api,
};
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::spawn_local;
#[cfg(doc)]
use crate::table::Table;
use crate::utils::{ApiFuture, ApiResult, JsValueSerdeExt, LocalPollLoop};
#[wasm_bindgen]
unsafe extern "C" {
#[wasm_bindgen(typescript_type = "ViewWindow")]
#[derive(Clone)]
pub type JsViewWindow;
#[wasm_bindgen(typescript_type = "ColumnWindow")]
#[derive(Clone)]
pub type JsColumnWindow;
#[wasm_bindgen(method, setter, js_name = "formatted")]
pub fn set_formatted(this: &JsViewWindow, x: bool);
#[wasm_bindgen(typescript_type = "OnUpdateOptions")]
pub type JsOnUpdateOptions;
}
impl From<ViewWindow> for JsViewWindow {
fn from(value: ViewWindow) -> Self {
JsViewWindow::from_serde_ext(&value)
.unwrap()
.unchecked_into()
}
}
fn scalar_to_jsvalue(scalar: &perspective_client::config::Scalar) -> JsValue {
match scalar {
perspective_client::config::Scalar::Float(x) => JsValue::from_f64(*x),
perspective_client::config::Scalar::String(x) => JsValue::from_str(x),
perspective_client::config::Scalar::Bool(x) => JsValue::from_bool(*x),
perspective_client::config::Scalar::Null => JsValue::NULL,
}
}
#[wasm_bindgen]
#[derive(Clone)]
pub struct View(pub(crate) perspective_client::View);
assert_view_api!(View);
impl From<perspective_client::View> for View {
fn from(value: perspective_client::View) -> Self {
View(value)
}
}
#[wasm_bindgen]
impl View {
#[doc(hidden)]
pub fn __get_model(&self) -> View {
self.clone()
}
#[wasm_bindgen]
pub async fn column_paths(&self, window: Option<JsColumnWindow>) -> ApiResult<JsValue> {
let window = window.into_serde_ext::<Option<ColumnWindow>>()?;
let columns = self.0.column_paths(window.unwrap_or_default()).await?;
Ok(JsValue::from_serde_ext(&columns)?)
}
#[wasm_bindgen]
pub async fn delete(self) -> ApiResult<()> {
self.0.delete().await?;
Ok(())
}
#[wasm_bindgen]
pub async fn dimensions(&self) -> ApiResult<JsValue> {
let dimensions = self.0.dimensions().await?;
Ok(JsValue::from_serde_ext(&dimensions)?)
}
#[wasm_bindgen]
pub async fn expression_schema(&self) -> ApiResult<JsValue> {
let schema = self.0.expression_schema().await?;
Ok(JsValue::from_serde_ext(&schema)?)
}
#[wasm_bindgen]
pub async fn get_config(&self) -> ApiResult<JsValue> {
let config = self.0.get_config().await?;
Ok(JsValue::from_serde_ext(&config)?)
}
#[wasm_bindgen]
pub async fn get_min_max(&self, name: String) -> ApiResult<Array> {
let result = self.0.get_min_max(name).await?;
let arr = Array::new();
arr.push(&scalar_to_jsvalue(&result.0));
arr.push(&scalar_to_jsvalue(&result.1));
Ok(arr)
}
#[wasm_bindgen]
pub async fn num_rows(&self) -> ApiResult<i32> {
let size = self.0.num_rows().await?;
Ok(size as i32)
}
#[wasm_bindgen]
pub async fn schema(&self) -> ApiResult<JsValue> {
let schema = self.0.schema().await?;
Ok(JsValue::from_serde_ext(&schema)?)
}
#[wasm_bindgen]
pub async fn to_arrow(&self, window: Option<JsViewWindow>) -> ApiResult<ArrayBuffer> {
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
let arrow = self.0.to_arrow(window.unwrap_or_default()).await?;
Ok(js_sys::Uint8Array::from(&arrow[..])
.buffer()
.unchecked_into())
}
#[wasm_bindgen]
pub async fn to_columns_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
let json = self.0.to_columns_string(window.unwrap_or_default()).await?;
Ok(json)
}
#[wasm_bindgen]
pub async fn to_columns(&self, window: Option<JsViewWindow>) -> ApiResult<Object> {
let json = self.to_columns_string(window).await?;
Ok(js_sys::JSON::parse(&json)?.unchecked_into())
}
#[wasm_bindgen]
pub async fn to_json_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
let json = self.0.to_json_string(window.unwrap_or_default()).await?;
Ok(json)
}
#[wasm_bindgen]
pub async fn to_json(&self, window: Option<JsViewWindow>) -> ApiResult<Array> {
let json = self.to_json_string(window).await?;
Ok(js_sys::JSON::parse(&json)?.unchecked_into())
}
#[wasm_bindgen]
pub async fn to_ndjson(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
let ndjson = self.0.to_ndjson(window.unwrap_or_default()).await?;
Ok(ndjson)
}
#[wasm_bindgen]
pub async fn to_csv(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
let window = window.into_serde_ext::<Option<ViewWindow>>()?;
Ok(self.0.to_csv(window.unwrap_or_default()).await?)
}
#[wasm_bindgen]
pub fn on_update(
&self,
on_update_js: Function,
options: Option<JsOnUpdateOptions>,
) -> ApiFuture<u32> {
let poll_loop = LocalPollLoop::new(move |args: OnUpdateData| {
let js_obj = JsValue::from_serde_ext(&*args)?;
on_update_js.call1(&JsValue::UNDEFINED, &js_obj)
});
let on_update = Box::new(move |msg| poll_loop.poll(msg));
let view = self.0.clone();
ApiFuture::new(async move {
let on_update_opts = options
.into_serde_ext::<Option<OnUpdateOptions>>()?
.unwrap_or_default();
let id = view.on_update(on_update, on_update_opts).await?;
Ok(id)
})
}
#[wasm_bindgen]
pub async fn remove_update(&self, callback_id: u32) -> ApiResult<()> {
Ok(self.0.remove_update(callback_id).await?)
}
#[wasm_bindgen]
pub fn on_delete(&self, on_delete: Function) -> ApiFuture<u32> {
let view = self.clone();
ApiFuture::new(async move {
let emit = LocalPollLoop::new(move |()| on_delete.call0(&JsValue::UNDEFINED));
let on_delete = Box::new(move || spawn_local(emit.poll(())));
Ok(view.0.on_delete(on_delete).await?)
})
}
#[wasm_bindgen]
pub async fn num_columns(&self) -> ApiResult<u32> {
Ok(self.0.dimensions().await?.num_view_columns)
}
#[wasm_bindgen]
pub fn remove_delete(&self, callback_id: u32) -> ApiFuture<()> {
let client = self.0.clone();
ApiFuture::new(async move {
client.remove_delete(callback_id).await?;
Ok(())
})
}
#[wasm_bindgen]
pub async fn collapse(&self, row_index: u32) -> ApiResult<u32> {
Ok(self.0.collapse(row_index).await?)
}
#[wasm_bindgen]
pub async fn expand(&self, row_index: u32) -> ApiResult<u32> {
Ok(self.0.expand(row_index).await?)
}
#[wasm_bindgen]
pub async fn set_depth(&self, depth: u32) -> ApiResult<()> {
Ok(self.0.set_depth(depth).await?)
}
}