perspective_js/
view.rs

1// ┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
2// ┃ ██████ ██████ ██████       █      █      █      █      █ █▄  ▀███ █       ┃
3// ┃ ▄▄▄▄▄█ █▄▄▄▄▄ ▄▄▄▄▄█  ▀▀▀▀▀█▀▀▀▀▀ █ ▀▀▀▀▀█ ████████▌▐███ ███▄  ▀█ █ ▀▀▀▀▀ ┃
4// ┃ █▀▀▀▀▀ █▀▀▀▀▀ █▀██▀▀ ▄▄▄▄▄ █ ▄▄▄▄▄█ ▄▄▄▄▄█ ████████▌▐███ █████▄   █ ▄▄▄▄▄ ┃
5// ┃ █      ██████ █  ▀█▄       █ ██████      █      ███▌▐███ ███████▄ █       ┃
6// ┣━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┫
7// ┃ Copyright (c) 2017, the Perspective Authors.                              ┃
8// ┃ ╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌ ┃
9// ┃ This file is part of the Perspective library, distributed under the terms ┃
10// ┃ of the [Apache License 2.0](https://www.apache.org/licenses/LICENSE-2.0). ┃
11// ┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
12
13use js_sys::{Array, ArrayBuffer, Function, Object};
14use macro_rules_attribute::apply;
15use perspective_client::{assert_view_api, OnUpdateOptions, ViewWindow};
16use wasm_bindgen::prelude::*;
17use wasm_bindgen_futures::spawn_local;
18
19#[cfg(doc)]
20use crate::table::Table;
21use crate::utils::{inherit_docs, ApiFuture, ApiResult, JsValueSerdeExt, LocalPollLoop};
22
23#[wasm_bindgen]
24unsafe extern "C" {
25    #[wasm_bindgen(typescript_type = "ViewWindow")]
26    #[derive(Clone)]
27    pub type JsViewWindow;
28
29    #[wasm_bindgen(method, setter, js_name = "formatted")]
30    pub fn set_formatted(this: &JsViewWindow, x: bool);
31
32    #[wasm_bindgen(typescript_type = "OnUpdateOptions")]
33    pub type JsOnUpdateOptions;
34
35}
36
37impl From<ViewWindow> for JsViewWindow {
38    fn from(value: ViewWindow) -> Self {
39        JsViewWindow::from_serde_ext(&value)
40            .unwrap()
41            .unchecked_into()
42    }
43}
44
45#[apply(inherit_docs)]
46#[inherit_doc = "view.md"]
47#[wasm_bindgen]
48#[derive(Clone)]
49pub struct View(pub(crate) perspective_client::View);
50
51assert_view_api!(View);
52
53impl From<perspective_client::View> for View {
54    fn from(value: perspective_client::View) -> Self {
55        View(value)
56    }
57}
58
59#[wasm_bindgen]
60impl View {
61    #[doc(hidden)]
62    pub fn __get_model(&self) -> View {
63        self.clone()
64    }
65
66    #[apply(inherit_docs)]
67    #[inherit_doc = "view/column_paths.md"]
68    #[wasm_bindgen]
69    pub async fn column_paths(&self) -> ApiResult<JsValue> {
70        let columns = self.0.column_paths().await?;
71        Ok(JsValue::from_serde_ext(&columns)?)
72    }
73
74    #[apply(inherit_docs)]
75    #[inherit_doc = "view/delete.md"]
76    #[wasm_bindgen]
77    pub async fn delete(&self) -> ApiResult<()> {
78        self.0.delete().await?;
79        Ok(())
80    }
81
82    #[apply(inherit_docs)]
83    #[inherit_doc = "view/dimensions.md"]
84    #[wasm_bindgen]
85    pub async fn dimensions(&self) -> ApiResult<JsValue> {
86        let dimensions = self.0.dimensions().await?;
87        Ok(JsValue::from_serde_ext(&dimensions)?)
88    }
89
90    #[apply(inherit_docs)]
91    #[inherit_doc = "view/expression_schema.md"]
92    #[wasm_bindgen]
93    pub async fn expression_schema(&self) -> ApiResult<JsValue> {
94        let schema = self.0.expression_schema().await?;
95        Ok(JsValue::from_serde_ext(&schema)?)
96    }
97
98    #[apply(inherit_docs)]
99    #[inherit_doc = "view/get_config.md"]
100    #[wasm_bindgen]
101    pub async fn get_config(&self) -> ApiResult<JsValue> {
102        let config = self.0.get_config().await?;
103        Ok(JsValue::from_serde_ext(&config)?)
104    }
105
106    #[apply(inherit_docs)]
107    #[inherit_doc = "view/get_min_max.md"]
108    #[wasm_bindgen]
109    pub async fn get_min_max(&self, name: String) -> ApiResult<Array> {
110        let result = self.0.get_min_max(name).await?;
111        Ok([result.0, result.1]
112            .iter()
113            .map(|x| js_sys::JSON::parse(x))
114            .collect::<Result<_, _>>()?)
115    }
116
117    #[apply(inherit_docs)]
118    #[inherit_doc = "view/num_rows.md"]
119    #[wasm_bindgen]
120    pub async fn num_rows(&self) -> ApiResult<i32> {
121        let size = self.0.num_rows().await?;
122        Ok(size as i32)
123    }
124
125    #[apply(inherit_docs)]
126    #[inherit_doc = "view/schema.md"]
127    #[wasm_bindgen]
128    pub async fn schema(&self) -> ApiResult<JsValue> {
129        let schema = self.0.schema().await?;
130        Ok(JsValue::from_serde_ext(&schema)?)
131    }
132
133    #[apply(inherit_docs)]
134    #[inherit_doc = "view/to_arrow.md"]
135    #[wasm_bindgen]
136    pub async fn to_arrow(&self, window: Option<JsViewWindow>) -> ApiResult<ArrayBuffer> {
137        let window = window.into_serde_ext::<Option<ViewWindow>>()?;
138        let arrow = self.0.to_arrow(window.unwrap_or_default()).await?;
139        Ok(js_sys::Uint8Array::from(&arrow[..])
140            .buffer()
141            .unchecked_into())
142    }
143
144    #[apply(inherit_docs)]
145    #[inherit_doc = "view/to_columns_string.md"]
146    #[wasm_bindgen]
147    pub async fn to_columns_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
148        let window = window.into_serde_ext::<Option<ViewWindow>>()?;
149        let json = self.0.to_columns_string(window.unwrap_or_default()).await?;
150        Ok(json)
151    }
152
153    #[apply(inherit_docs)]
154    #[inherit_doc = "view/to_columns.md"]
155    #[wasm_bindgen]
156    pub async fn to_columns(&self, window: Option<JsViewWindow>) -> ApiResult<Object> {
157        let json = self.to_columns_string(window).await?;
158        Ok(js_sys::JSON::parse(&json)?.unchecked_into())
159    }
160
161    #[apply(inherit_docs)]
162    #[inherit_doc = "view/to_json_string.md"]
163    #[wasm_bindgen]
164    pub async fn to_json_string(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
165        let window = window.into_serde_ext::<Option<ViewWindow>>()?;
166        let json = self.0.to_json_string(window.unwrap_or_default()).await?;
167        Ok(json)
168    }
169
170    #[apply(inherit_docs)]
171    #[inherit_doc = "view/to_json.md"]
172    #[wasm_bindgen]
173    pub async fn to_json(&self, window: Option<JsViewWindow>) -> ApiResult<Array> {
174        let json = self.to_json_string(window).await?;
175        Ok(js_sys::JSON::parse(&json)?.unchecked_into())
176    }
177
178    #[apply(inherit_docs)]
179    #[inherit_doc = "view/to_ndjson.md"]
180    #[wasm_bindgen]
181    pub async fn to_ndjson(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
182        let window = window.into_serde_ext::<Option<ViewWindow>>()?;
183        let ndjson = self.0.to_ndjson(window.unwrap_or_default()).await?;
184        Ok(ndjson)
185    }
186
187    #[apply(inherit_docs)]
188    #[inherit_doc = "view/to_csv.md"]
189    #[wasm_bindgen]
190    pub async fn to_csv(&self, window: Option<JsViewWindow>) -> ApiResult<String> {
191        let window = window.into_serde_ext::<Option<ViewWindow>>()?;
192        Ok(self.0.to_csv(window.unwrap_or_default()).await?)
193    }
194
195    #[apply(inherit_docs)]
196    #[inherit_doc = "view/on_update.md"]
197    #[wasm_bindgen]
198    pub async fn on_update(
199        &self,
200        on_update_js: Function,
201        options: Option<JsOnUpdateOptions>,
202    ) -> ApiResult<u32> {
203        let poll_loop = LocalPollLoop::new(move |args| {
204            let js_obj = JsValue::from_serde_ext(&args)?;
205            on_update_js.call1(&JsValue::UNDEFINED, &js_obj)
206        });
207
208        let on_update = Box::new(move |msg| poll_loop.poll(msg));
209        let on_update_opts = options
210            .into_serde_ext::<Option<OnUpdateOptions>>()?
211            .unwrap_or_default();
212
213        let id = self.0.on_update(on_update, on_update_opts).await?;
214        Ok(id)
215    }
216
217    #[apply(inherit_docs)]
218    #[inherit_doc = "view/remove_update.md"]
219    #[wasm_bindgen]
220    pub async fn remove_update(&self, callback_id: u32) -> ApiResult<()> {
221        Ok(self.0.remove_update(callback_id).await?)
222    }
223
224    #[apply(inherit_docs)]
225    #[inherit_doc = "view/on_delete.md"]
226    #[wasm_bindgen]
227    pub async fn on_delete(&self, on_delete: Function) -> ApiResult<u32> {
228        let emit = LocalPollLoop::new(move |()| on_delete.call0(&JsValue::UNDEFINED));
229        let on_delete = Box::new(move || spawn_local(emit.poll(())));
230        Ok(self.0.on_delete(on_delete).await?)
231    }
232
233    #[apply(inherit_docs)]
234    #[inherit_doc = "view/num_columns.md"]
235    #[wasm_bindgen]
236    pub async fn num_columns(&self) -> ApiResult<u32> {
237        // TODO: This is broken because of how split by creates a
238        // cartesian product of columns * unique values.
239        Ok(self.0.dimensions().await?.num_view_columns)
240    }
241
242    #[apply(inherit_docs)]
243    #[inherit_doc = "view/remove_delete.md"]
244    #[wasm_bindgen]
245    pub fn remove_delete(&self, callback_id: u32) -> ApiFuture<()> {
246        let client = self.0.clone();
247        ApiFuture::new(async move {
248            client.remove_delete(callback_id).await?;
249            Ok(())
250        })
251    }
252
253    #[apply(inherit_docs)]
254    #[inherit_doc = "view/collapse.md"]
255    #[wasm_bindgen]
256    pub async fn collapse(&self, row_index: u32) -> ApiResult<u32> {
257        Ok(self.0.collapse(row_index).await?)
258    }
259
260    #[apply(inherit_docs)]
261    #[inherit_doc = "view/expand.md"]
262    #[wasm_bindgen]
263    pub async fn expand(&self, row_index: u32) -> ApiResult<u32> {
264        Ok(self.0.expand(row_index).await?)
265    }
266
267    #[apply(inherit_docs)]
268    #[inherit_doc = "view/set_depth.md"]
269    #[wasm_bindgen]
270    pub async fn set_depth(&self, depth: u32) -> ApiResult<()> {
271        Ok(self.0.set_depth(depth).await?)
272    }
273}