@perspective-dev/client 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +193 -0
- package/README.md +3 -0
- package/dist/cdn/perspective-server.worker.js +2 -0
- package/dist/cdn/perspective-server.worker.js.map +7 -0
- package/dist/cdn/perspective.js +3 -0
- package/dist/cdn/perspective.js.map +7 -0
- package/dist/esm/perspective-server.worker.d.ts +1 -0
- package/dist/esm/perspective.browser.d.ts +14 -0
- package/dist/esm/perspective.inline.js +3 -0
- package/dist/esm/perspective.inline.js.map +7 -0
- package/dist/esm/perspective.js +3 -0
- package/dist/esm/perspective.js.map +7 -0
- package/dist/esm/perspective.node.d.ts +60 -0
- package/dist/esm/perspective.node.js +2431 -0
- package/dist/esm/perspective.node.js.map +7 -0
- package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
- package/dist/esm/ts-rs/ColumnWindow.d.ts +4 -0
- package/dist/esm/ts-rs/DeleteOptions.d.ts +6 -0
- package/dist/esm/ts-rs/Expressions.d.ts +3 -0
- package/dist/esm/ts-rs/Filter.d.ts +2 -0
- package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
- package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
- package/dist/esm/ts-rs/OnUpdateMode.d.ts +9 -0
- package/dist/esm/ts-rs/OnUpdateOptions.d.ts +7 -0
- package/dist/esm/ts-rs/Scalar.d.ts +5 -0
- package/dist/esm/ts-rs/Sort.d.ts +2 -0
- package/dist/esm/ts-rs/SortDir.d.ts +1 -0
- package/dist/esm/ts-rs/SystemInfo.d.ts +40 -0
- package/dist/esm/ts-rs/TableInitOptions.d.ts +22 -0
- package/dist/esm/ts-rs/TableReadFormat.d.ts +7 -0
- package/dist/esm/ts-rs/UpdateOptions.d.ts +8 -0
- package/dist/esm/ts-rs/ViewConfigUpdate.d.ts +90 -0
- package/dist/esm/ts-rs/ViewOnUpdateResp.d.ts +4 -0
- package/dist/esm/ts-rs/ViewWindow.d.ts +23 -0
- package/dist/esm/wasm/browser.d.ts +21 -0
- package/dist/esm/wasm/decompress.d.ts +1 -0
- package/dist/esm/wasm/emscripten_api.d.ts +5 -0
- package/dist/esm/wasm/engine.d.ts +40 -0
- package/dist/esm/wasm/perspective-server.poly.d.ts +1 -0
- package/dist/esm/websocket.d.ts +4 -0
- package/dist/wasm/perspective-js.d.ts +712 -0
- package/dist/wasm/perspective-js.js +1934 -0
- package/dist/wasm/perspective-js.wasm +0 -0
- package/dist/wasm/perspective-js.wasm.d.ts +75 -0
- package/package.json +68 -0
- package/src/rust/client.rs +483 -0
- package/src/rust/lib.rs +70 -0
- package/src/rust/table.rs +364 -0
- package/src/rust/table_data.rs +159 -0
- package/src/rust/utils/browser.rs +39 -0
- package/src/rust/utils/console_logger.rs +236 -0
- package/src/rust/utils/errors.rs +288 -0
- package/src/rust/utils/futures.rs +174 -0
- package/src/rust/utils/json.rs +252 -0
- package/src/rust/utils/local_poll_loop.rs +63 -0
- package/src/rust/utils/mod.rs +32 -0
- package/src/rust/utils/serde.rs +46 -0
- package/src/rust/utils/trace_allocator.rs +98 -0
- package/src/rust/view.rs +355 -0
- package/src/ts/perspective-server.worker.ts +54 -0
- package/src/ts/perspective.browser.ts +132 -0
- package/src/ts/perspective.cdn.ts +22 -0
- package/src/ts/perspective.inline.ts +27 -0
- package/src/ts/perspective.node.ts +315 -0
- package/src/ts/ts-rs/Aggregate.ts +3 -0
- package/src/ts/ts-rs/ColumnWindow.ts +3 -0
- package/src/ts/ts-rs/DeleteOptions.ts +6 -0
- package/src/ts/ts-rs/Expressions.ts +3 -0
- package/src/ts/ts-rs/Filter.ts +4 -0
- package/src/ts/ts-rs/FilterReducer.ts +3 -0
- package/src/ts/ts-rs/FilterTerm.ts +4 -0
- package/src/ts/ts-rs/OnUpdateData.ts +8 -0
- package/src/ts/ts-rs/OnUpdateMode.ts +11 -0
- package/src/ts/ts-rs/OnUpdateOptions.ts +7 -0
- package/src/ts/ts-rs/Scalar.ts +7 -0
- package/src/ts/ts-rs/Sort.ts +4 -0
- package/src/ts/ts-rs/SortDir.ts +3 -0
- package/src/ts/ts-rs/SystemInfo.ts +41 -0
- package/src/ts/ts-rs/TableInitOptions.ts +21 -0
- package/src/ts/ts-rs/TableReadFormat.ts +9 -0
- package/src/ts/ts-rs/UpdateOptions.ts +7 -0
- package/src/ts/ts-rs/ViewConfigUpdate.ts +87 -0
- package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
- package/src/ts/ts-rs/ViewWindow.ts +17 -0
- package/src/ts/wasm/browser.ts +123 -0
- package/src/ts/wasm/decompress.ts +64 -0
- package/src/ts/wasm/emscripten_api.ts +63 -0
- package/src/ts/wasm/engine.ts +271 -0
- package/src/ts/wasm/perspective-server.poly.ts +244 -0
- package/src/ts/websocket.ts +95 -0
- package/tsconfig.json +20 -0
package/src/rust/lib.rs
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
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
|
+
|
|
13
|
+
//! <div class="warning">
|
|
14
|
+
//! The examples in this module are in JavaScript. See <a href="https://docs.rs/crate/perspective/latest"><code>perspective</code></a> docs for the Rust API.
|
|
15
|
+
//! </div>
|
|
16
|
+
|
|
17
|
+
#![warn(
|
|
18
|
+
clippy::all,
|
|
19
|
+
clippy::panic_in_result_fn,
|
|
20
|
+
clippy::await_holding_refcell_ref,
|
|
21
|
+
rustdoc::broken_intra_doc_links,
|
|
22
|
+
unstable_features
|
|
23
|
+
)]
|
|
24
|
+
#![allow(non_snake_case)]
|
|
25
|
+
|
|
26
|
+
extern crate alloc;
|
|
27
|
+
|
|
28
|
+
mod client;
|
|
29
|
+
mod table;
|
|
30
|
+
mod table_data;
|
|
31
|
+
pub mod utils;
|
|
32
|
+
mod view;
|
|
33
|
+
|
|
34
|
+
#[cfg(feature = "export-init")]
|
|
35
|
+
use wasm_bindgen::prelude::*;
|
|
36
|
+
|
|
37
|
+
pub use crate::client::Client;
|
|
38
|
+
pub use crate::table::*;
|
|
39
|
+
pub use crate::table_data::*;
|
|
40
|
+
|
|
41
|
+
#[cfg(feature = "export-init")]
|
|
42
|
+
#[wasm_bindgen(typescript_custom_section)]
|
|
43
|
+
const TS_APPEND_CONTENT: &'static str = r#"
|
|
44
|
+
export type * from "../../src/ts/ts-rs/ViewWindow.d.ts";
|
|
45
|
+
export type * from "../../src/ts/ts-rs/ColumnWindow.d.ts";
|
|
46
|
+
export type * from "../../src/ts/ts-rs/TableInitOptions.d.ts";
|
|
47
|
+
export type * from "../../src/ts/ts-rs/ViewConfigUpdate.d.ts";
|
|
48
|
+
export type * from "../../src/ts/ts-rs/ViewOnUpdateResp.d.ts";
|
|
49
|
+
export type * from "../../src/ts/ts-rs/OnUpdateOptions.d.ts";
|
|
50
|
+
export type * from "../../src/ts/ts-rs/UpdateOptions.d.ts";
|
|
51
|
+
export type * from "../../src/ts/ts-rs/DeleteOptions.d.ts";
|
|
52
|
+
export type * from "../../src/ts/ts-rs/SystemInfo.d.ts";
|
|
53
|
+
|
|
54
|
+
import type {ColumnWindow} from "../../src/ts/ts-rs/ColumnWindow.d.ts";
|
|
55
|
+
import type {ViewWindow} from "../../src/ts/ts-rs/ViewWindow.d.ts";
|
|
56
|
+
import type {TableInitOptions} from "../../src/ts/ts-rs/TableInitOptions.d.ts";
|
|
57
|
+
import type {ViewConfigUpdate} from "../../src/ts/ts-rs/ViewConfigUpdate.d.ts";
|
|
58
|
+
import type * as on_update_args from "../../src/ts/ts-rs/ViewOnUpdateResp.d.ts";
|
|
59
|
+
import type {OnUpdateOptions} from "../../src/ts/ts-rs/OnUpdateOptions.d.ts";
|
|
60
|
+
import type {UpdateOptions} from "../../src/ts/ts-rs/UpdateOptions.d.ts";
|
|
61
|
+
import type {DeleteOptions} from "../../src/ts/ts-rs/DeleteOptions.d.ts";
|
|
62
|
+
import type {SystemInfo} from "../../src/ts/ts-rs/SystemInfo.d.ts";
|
|
63
|
+
"#;
|
|
64
|
+
|
|
65
|
+
#[cfg(feature = "export-init")]
|
|
66
|
+
#[wasm_bindgen]
|
|
67
|
+
pub fn init() {
|
|
68
|
+
console_error_panic_hook::set_once();
|
|
69
|
+
utils::set_global_logging();
|
|
70
|
+
}
|
|
@@ -0,0 +1,364 @@
|
|
|
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
|
+
|
|
13
|
+
use js_sys::Function;
|
|
14
|
+
use perspective_client::config::*;
|
|
15
|
+
use perspective_client::{DeleteOptions, UpdateData, UpdateOptions, assert_table_api};
|
|
16
|
+
use wasm_bindgen::prelude::*;
|
|
17
|
+
use wasm_bindgen_derive::TryFromJsValue;
|
|
18
|
+
use wasm_bindgen_futures::spawn_local;
|
|
19
|
+
|
|
20
|
+
use crate::Client;
|
|
21
|
+
use crate::table_data::UpdateDataExt;
|
|
22
|
+
use crate::utils::{ApiFuture, ApiResult, JsValueSerdeExt, LocalPollLoop};
|
|
23
|
+
pub use crate::view::*;
|
|
24
|
+
|
|
25
|
+
#[derive(TryFromJsValue, Clone, PartialEq)]
|
|
26
|
+
#[wasm_bindgen]
|
|
27
|
+
pub struct Table(pub(crate) perspective_client::Table);
|
|
28
|
+
|
|
29
|
+
assert_table_api!(Table);
|
|
30
|
+
|
|
31
|
+
impl From<perspective_client::Table> for Table {
|
|
32
|
+
fn from(value: perspective_client::Table) -> Self {
|
|
33
|
+
Table(value)
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/// [`Table`] is Perspective's columnar data frame, analogous to a Pandas/Polars
|
|
38
|
+
/// `DataFrame` or Apache Arrow, supporting append & in-place updates, removal
|
|
39
|
+
/// by index, and update notifications.
|
|
40
|
+
///
|
|
41
|
+
/// A [`Table`] contains columns, each of which have a unique name, are strongly
|
|
42
|
+
/// and consistently typed, and contains rows of data conforming to the column's
|
|
43
|
+
/// type. Each column in a [`Table`] must have the same number of rows, though
|
|
44
|
+
/// not every row must contain data; null-values are used to indicate missing
|
|
45
|
+
/// values in the dataset. The schema of a [`Table`] is _immutable after
|
|
46
|
+
/// creation_, which means the column names and data types cannot be changed
|
|
47
|
+
/// after the [`Table`] has been created. Columns cannot be added or deleted
|
|
48
|
+
/// after creation either, but a [`View`] can be used to select an arbitrary set
|
|
49
|
+
/// of columns from the [`Table`].
|
|
50
|
+
impl Table {
|
|
51
|
+
pub fn get_table(&self) -> &'_ perspective_client::Table {
|
|
52
|
+
&self.0
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[wasm_bindgen]
|
|
57
|
+
extern "C" {
|
|
58
|
+
// TODO Fix me
|
|
59
|
+
#[wasm_bindgen(typescript_type = "\
|
|
60
|
+
string | ArrayBuffer | Record<string, unknown[]> | Record<string, unknown>[]")]
|
|
61
|
+
pub type JsTableInitData;
|
|
62
|
+
|
|
63
|
+
#[wasm_bindgen(typescript_type = "ViewConfigUpdate")]
|
|
64
|
+
pub type JsViewConfig;
|
|
65
|
+
|
|
66
|
+
#[wasm_bindgen(typescript_type = "UpdateOptions")]
|
|
67
|
+
pub type JsUpdateOptions;
|
|
68
|
+
|
|
69
|
+
#[wasm_bindgen(typescript_type = "DeleteOptions")]
|
|
70
|
+
pub type JsDeleteOptions;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
#[wasm_bindgen]
|
|
74
|
+
impl Table {
|
|
75
|
+
/// Returns the name of the index column for the table.
|
|
76
|
+
///
|
|
77
|
+
/// # JavaScript Examples
|
|
78
|
+
///
|
|
79
|
+
/// ```javascript
|
|
80
|
+
/// const table = await client.table("x,y\n1,2\n3,4", { index: "x" });
|
|
81
|
+
/// const index = table.get_index(); // "x"
|
|
82
|
+
/// ```
|
|
83
|
+
#[wasm_bindgen]
|
|
84
|
+
pub async fn get_index(&self) -> Option<String> {
|
|
85
|
+
self.0.get_index()
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/// Get a copy of the [`Client`] this [`Table`] came from.
|
|
89
|
+
#[wasm_bindgen]
|
|
90
|
+
pub async fn get_client(&self) -> Client {
|
|
91
|
+
Client {
|
|
92
|
+
close: None,
|
|
93
|
+
client: self.0.get_client(),
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/// Returns the user-specified name for this table, or the auto-generated
|
|
98
|
+
/// name if a name was not specified when the table was created.
|
|
99
|
+
#[wasm_bindgen]
|
|
100
|
+
pub async fn get_name(&self) -> String {
|
|
101
|
+
self.0.get_name().to_owned()
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/// Returns the user-specified row limit for this table.
|
|
105
|
+
#[wasm_bindgen]
|
|
106
|
+
pub async fn get_limit(&self) -> Option<u32> {
|
|
107
|
+
self.0.get_limit()
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/// Removes all the rows in the [`Table`], but preserves everything else
|
|
111
|
+
/// including the schema, index, and any callbacks or registered
|
|
112
|
+
/// [`View`] instances.
|
|
113
|
+
///
|
|
114
|
+
/// Calling [`Table::clear`], like [`Table::update`] and [`Table::remove`],
|
|
115
|
+
/// will trigger an update event to any registered listeners via
|
|
116
|
+
/// [`View::on_update`].
|
|
117
|
+
#[wasm_bindgen]
|
|
118
|
+
pub async fn clear(&self) -> ApiResult<()> {
|
|
119
|
+
self.0.clear().await?;
|
|
120
|
+
Ok(())
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/// Delete this [`Table`] and cleans up associated resources.
|
|
124
|
+
///
|
|
125
|
+
/// [`Table`]s do not stop consuming resources or processing updates when
|
|
126
|
+
/// they are garbage collected in their host language - you must call
|
|
127
|
+
/// this method to reclaim these.
|
|
128
|
+
///
|
|
129
|
+
/// # Arguments
|
|
130
|
+
///
|
|
131
|
+
/// - `options` An options dictionary.
|
|
132
|
+
/// - `lazy` Whether to delete this [`Table`] _lazily_. When false (the
|
|
133
|
+
/// default), the delete will occur immediately, assuming it has no
|
|
134
|
+
/// [`View`] instances registered to it (which must be deleted first,
|
|
135
|
+
/// otherwise this method will throw an error). When true, the
|
|
136
|
+
/// [`Table`] will only be marked for deltion once its [`View`]
|
|
137
|
+
/// dependency count reaches 0.
|
|
138
|
+
///
|
|
139
|
+
/// # JavaScript Examples
|
|
140
|
+
///
|
|
141
|
+
/// ```javascript
|
|
142
|
+
/// const table = await client.table("x,y\n1,2\n3,4");
|
|
143
|
+
///
|
|
144
|
+
/// // ...
|
|
145
|
+
///
|
|
146
|
+
/// await table.delete({ lazy: true });
|
|
147
|
+
/// ```
|
|
148
|
+
#[wasm_bindgen]
|
|
149
|
+
pub async fn delete(self, options: Option<JsDeleteOptions>) -> ApiResult<()> {
|
|
150
|
+
let options = options
|
|
151
|
+
.into_serde_ext::<Option<DeleteOptions>>()?
|
|
152
|
+
.unwrap_or_default();
|
|
153
|
+
|
|
154
|
+
self.0.delete(options).await?;
|
|
155
|
+
Ok(())
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/// Returns the number of rows in a [`Table`].
|
|
159
|
+
#[wasm_bindgen]
|
|
160
|
+
pub async fn size(&self) -> ApiResult<f64> {
|
|
161
|
+
Ok(self.0.size().await? as f64)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/// Returns a table's [`Schema`], a mapping of column names to column types.
|
|
165
|
+
///
|
|
166
|
+
/// The mapping of a [`Table`]'s column names to data types is referred to
|
|
167
|
+
/// as a [`Schema`]. Each column has a unique name and a data type, one
|
|
168
|
+
/// of:
|
|
169
|
+
///
|
|
170
|
+
/// - `"boolean"` - A boolean type
|
|
171
|
+
/// - `"date"` - A timesonze-agnostic date type (month/day/year)
|
|
172
|
+
/// - `"datetime"` - A millisecond-precision datetime type in the UTC
|
|
173
|
+
/// timezone
|
|
174
|
+
/// - `"float"` - A 64 bit float
|
|
175
|
+
/// - `"integer"` - A signed 32 bit integer (the integer type supported by
|
|
176
|
+
/// JavaScript)
|
|
177
|
+
/// - `"string"` - A [`String`] data type (encoded internally as a
|
|
178
|
+
/// _dictionary_)
|
|
179
|
+
///
|
|
180
|
+
/// Note that all [`Table`] columns are _nullable_, regardless of the data
|
|
181
|
+
/// type.
|
|
182
|
+
#[wasm_bindgen]
|
|
183
|
+
pub async fn schema(&self) -> ApiResult<JsValue> {
|
|
184
|
+
let schema = self.0.schema().await?;
|
|
185
|
+
Ok(JsValue::from_serde_ext(&schema)?)
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/// Returns the column names of this [`Table`] in "natural" order (the
|
|
189
|
+
/// ordering implied by the input format).
|
|
190
|
+
///
|
|
191
|
+
/// # JavaScript Examples
|
|
192
|
+
///
|
|
193
|
+
/// ```javascript
|
|
194
|
+
/// const columns = await table.columns();
|
|
195
|
+
/// ```
|
|
196
|
+
#[wasm_bindgen]
|
|
197
|
+
pub async fn columns(&self) -> ApiResult<JsValue> {
|
|
198
|
+
let columns = self.0.columns().await?;
|
|
199
|
+
Ok(JsValue::from_serde_ext(&columns)?)
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
/// Create a unique channel ID on this [`Table`], which allows
|
|
203
|
+
/// `View::on_update` callback calls to be associated with the
|
|
204
|
+
/// `Table::update` which caused them.
|
|
205
|
+
#[wasm_bindgen]
|
|
206
|
+
pub async fn make_port(&self) -> ApiResult<i32> {
|
|
207
|
+
Ok(self.0.make_port().await?)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/// Register a callback which is called exactly once, when this [`Table`] is
|
|
211
|
+
/// deleted with the [`Table::delete`] method.
|
|
212
|
+
///
|
|
213
|
+
/// [`Table::on_delete`] resolves when the subscription message is sent, not
|
|
214
|
+
/// when the _delete_ event occurs.
|
|
215
|
+
#[wasm_bindgen]
|
|
216
|
+
pub fn on_delete(&self, on_delete: Function) -> ApiFuture<u32> {
|
|
217
|
+
let table = self.clone();
|
|
218
|
+
ApiFuture::new(async move {
|
|
219
|
+
let emit = LocalPollLoop::new(move |()| on_delete.call0(&JsValue::UNDEFINED));
|
|
220
|
+
let on_delete = Box::new(move || spawn_local(emit.poll(())));
|
|
221
|
+
Ok(table.0.on_delete(on_delete).await?)
|
|
222
|
+
})
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/// Removes a listener with a given ID, as returned by a previous call to
|
|
226
|
+
/// [`Table::on_delete`].
|
|
227
|
+
#[wasm_bindgen]
|
|
228
|
+
pub fn remove_delete(&self, callback_id: u32) -> ApiFuture<()> {
|
|
229
|
+
let client = self.0.clone();
|
|
230
|
+
ApiFuture::new(async move {
|
|
231
|
+
client.remove_delete(callback_id).await?;
|
|
232
|
+
Ok(())
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/// Removes rows from this [`Table`] with the `index` column values
|
|
237
|
+
/// supplied.
|
|
238
|
+
///
|
|
239
|
+
/// # Arguments
|
|
240
|
+
///
|
|
241
|
+
/// - `indices` - A list of `index` column values for rows that should be
|
|
242
|
+
/// removed.
|
|
243
|
+
///
|
|
244
|
+
/// # JavaScript Examples
|
|
245
|
+
///
|
|
246
|
+
/// ```javascript
|
|
247
|
+
/// await table.remove([1, 2, 3]);
|
|
248
|
+
/// ```
|
|
249
|
+
#[wasm_bindgen]
|
|
250
|
+
pub async fn remove(&self, value: &JsValue, options: Option<JsUpdateOptions>) -> ApiResult<()> {
|
|
251
|
+
let options = options
|
|
252
|
+
.into_serde_ext::<Option<UpdateOptions>>()?
|
|
253
|
+
.unwrap_or_default();
|
|
254
|
+
|
|
255
|
+
let input = UpdateData::from_js_value(value, options.format)?;
|
|
256
|
+
self.0.remove(input).await?;
|
|
257
|
+
Ok(())
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/// Replace all rows in this [`Table`] with the input data, coerced to this
|
|
261
|
+
/// [`Table`]'s existing [`perspective_client::Schema`], notifying any
|
|
262
|
+
/// derived [`View`] and [`View::on_update`] callbacks.
|
|
263
|
+
///
|
|
264
|
+
/// Calling [`Table::replace`] is an easy way to replace _all_ the data in a
|
|
265
|
+
/// [`Table`] without losing any derived [`View`] instances or
|
|
266
|
+
/// [`View::on_update`] callbacks. [`Table::replace`] does _not_ infer
|
|
267
|
+
/// data types like [`Client::table`] does, rather it _coerces_ input
|
|
268
|
+
/// data to the `Schema` like [`Table::update`]. If you need a [`Table`]
|
|
269
|
+
/// with a different `Schema`, you must create a new one.
|
|
270
|
+
///
|
|
271
|
+
/// # JavaScript Examples
|
|
272
|
+
///
|
|
273
|
+
/// ```javascript
|
|
274
|
+
/// await table.replace("x,y\n1,2");
|
|
275
|
+
/// ```
|
|
276
|
+
#[wasm_bindgen]
|
|
277
|
+
pub async fn replace(
|
|
278
|
+
&self,
|
|
279
|
+
input: &JsValue,
|
|
280
|
+
options: Option<JsUpdateOptions>,
|
|
281
|
+
) -> ApiResult<()> {
|
|
282
|
+
let options = options
|
|
283
|
+
.into_serde_ext::<Option<UpdateOptions>>()?
|
|
284
|
+
.unwrap_or_default();
|
|
285
|
+
|
|
286
|
+
let input = UpdateData::from_js_value(input, options.format)?;
|
|
287
|
+
self.0.replace(input).await?;
|
|
288
|
+
Ok(())
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
/// Updates the rows of this table and any derived [`View`] instances.
|
|
292
|
+
///
|
|
293
|
+
/// Calling [`Table::update`] will trigger the [`View::on_update`] callbacks
|
|
294
|
+
/// register to derived [`View`], and the call itself will not resolve until
|
|
295
|
+
/// _all_ derived [`View`]'s are notified.
|
|
296
|
+
///
|
|
297
|
+
/// When updating a [`Table`] with an `index`, [`Table::update`] supports
|
|
298
|
+
/// partial updates, by omitting columns from the update data.
|
|
299
|
+
///
|
|
300
|
+
/// # Arguments
|
|
301
|
+
///
|
|
302
|
+
/// - `input` - The input data for this [`Table`]. The schema of a [`Table`]
|
|
303
|
+
/// is immutable after creation, so this method cannot be called with a
|
|
304
|
+
/// schema.
|
|
305
|
+
/// - `options` - Options for this update step - see [`UpdateOptions`].
|
|
306
|
+
///
|
|
307
|
+
/// # JavaScript Examples
|
|
308
|
+
///
|
|
309
|
+
/// ```javascript
|
|
310
|
+
/// await table.update("x,y\n1,2");
|
|
311
|
+
/// ```
|
|
312
|
+
#[wasm_bindgen]
|
|
313
|
+
pub fn update(
|
|
314
|
+
&self,
|
|
315
|
+
input: JsTableInitData,
|
|
316
|
+
options: Option<JsUpdateOptions>,
|
|
317
|
+
) -> ApiFuture<()> {
|
|
318
|
+
let table = self.clone();
|
|
319
|
+
ApiFuture::new(async move {
|
|
320
|
+
let options = options
|
|
321
|
+
.into_serde_ext::<Option<UpdateOptions>>()?
|
|
322
|
+
.unwrap_or_default();
|
|
323
|
+
|
|
324
|
+
let input = UpdateData::from_js_value(&input, options.format)?;
|
|
325
|
+
Ok(table.0.update(input, options).await?)
|
|
326
|
+
})
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/// Create a new [`View`] from this table with a specified
|
|
330
|
+
/// [`ViewConfigUpdate`].
|
|
331
|
+
///
|
|
332
|
+
/// See [`View`] struct.
|
|
333
|
+
///
|
|
334
|
+
/// # JavaScript Examples
|
|
335
|
+
///
|
|
336
|
+
/// ```javascript
|
|
337
|
+
/// const view = await table.view({
|
|
338
|
+
/// columns: ["Sales"],
|
|
339
|
+
/// aggregates: { Sales: "sum" },
|
|
340
|
+
/// group_by: ["Region", "Country"],
|
|
341
|
+
/// filter: [["Category", "in", ["Furniture", "Technology"]]],
|
|
342
|
+
/// });
|
|
343
|
+
/// ```
|
|
344
|
+
#[wasm_bindgen]
|
|
345
|
+
pub async fn view(&self, config: Option<JsViewConfig>) -> ApiResult<View> {
|
|
346
|
+
let config = config
|
|
347
|
+
.map(|config| js_sys::JSON::stringify(&config))
|
|
348
|
+
.transpose()?
|
|
349
|
+
.and_then(|x| x.as_string())
|
|
350
|
+
.map(|x| serde_json::from_str(x.as_str()))
|
|
351
|
+
.transpose()?;
|
|
352
|
+
|
|
353
|
+
let view = self.0.view(config).await?;
|
|
354
|
+
Ok(View(view))
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/// Validates the given expressions.
|
|
358
|
+
#[wasm_bindgen]
|
|
359
|
+
pub async fn validate_expressions(&self, exprs: &JsValue) -> ApiResult<JsValue> {
|
|
360
|
+
let exprs = JsValue::into_serde_ext::<Expressions>(exprs.clone())?;
|
|
361
|
+
let columns = self.0.validate_expressions(exprs).await?;
|
|
362
|
+
Ok(JsValue::from_serde_ext(&columns)?)
|
|
363
|
+
}
|
|
364
|
+
}
|
|
@@ -0,0 +1,159 @@
|
|
|
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
|
+
|
|
13
|
+
use extend::ext;
|
|
14
|
+
use js_sys::{Array, ArrayBuffer, Function, JSON, Object, Reflect, Uint8Array};
|
|
15
|
+
use perspective_client::config::ColumnType;
|
|
16
|
+
use perspective_client::{TableData, TableReadFormat, UpdateData};
|
|
17
|
+
use wasm_bindgen::convert::TryFromJsValue;
|
|
18
|
+
use wasm_bindgen::prelude::*;
|
|
19
|
+
|
|
20
|
+
use crate::apierror;
|
|
21
|
+
use crate::utils::{ApiError, ApiResult, JsValueSerdeExt, ToApiError};
|
|
22
|
+
pub use crate::view::*;
|
|
23
|
+
|
|
24
|
+
#[ext]
|
|
25
|
+
impl Vec<(String, ColumnType)> {
|
|
26
|
+
fn from_js_value(value: &JsValue) -> ApiResult<Vec<(String, ColumnType)>> {
|
|
27
|
+
Ok(Object::keys(value.unchecked_ref())
|
|
28
|
+
.iter()
|
|
29
|
+
.map(|x| -> Result<_, JsValue> {
|
|
30
|
+
let key = x.as_string().into_apierror()?;
|
|
31
|
+
let val = Reflect::get(value, &x)?
|
|
32
|
+
.as_string()
|
|
33
|
+
.into_apierror()?
|
|
34
|
+
.into_serde_ext()?;
|
|
35
|
+
|
|
36
|
+
Ok((key, val))
|
|
37
|
+
})
|
|
38
|
+
.collect::<Result<Vec<_>, _>>()?)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[ext]
|
|
43
|
+
pub(crate) impl TableData {
|
|
44
|
+
fn from_js_value(value: &JsValue, format: Option<TableReadFormat>) -> ApiResult<TableData> {
|
|
45
|
+
if let Some(result) = UpdateData::from_js_value_partial(value, format)? {
|
|
46
|
+
Ok(result.into())
|
|
47
|
+
} else if value.is_instance_of::<Object>() && Reflect::has(value, &"__get_model".into())? {
|
|
48
|
+
let val = Reflect::get(value, &"__get_model".into())?
|
|
49
|
+
.dyn_into::<Function>()?
|
|
50
|
+
.call0(value)?;
|
|
51
|
+
|
|
52
|
+
let view = View::try_from_js_value(val)?;
|
|
53
|
+
Ok(TableData::View(view.0))
|
|
54
|
+
} else if value.is_instance_of::<Object>() {
|
|
55
|
+
let all_strings = || {
|
|
56
|
+
Object::values(value.unchecked_ref())
|
|
57
|
+
.to_vec()
|
|
58
|
+
.iter()
|
|
59
|
+
.all(|x| x.is_string())
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
let all_arrays = || {
|
|
63
|
+
Object::values(value.unchecked_ref())
|
|
64
|
+
.to_vec()
|
|
65
|
+
.iter()
|
|
66
|
+
.all(|x| x.is_instance_of::<Array>())
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
if all_strings() {
|
|
70
|
+
Ok(TableData::Schema(Vec::from_js_value(value)?))
|
|
71
|
+
} else if all_arrays() {
|
|
72
|
+
let json = JSON::stringify(value)?.as_string().into_apierror()?;
|
|
73
|
+
Ok(UpdateData::JsonColumns(json).into())
|
|
74
|
+
} else {
|
|
75
|
+
Err(apierror!(TableError(value.clone())))
|
|
76
|
+
}
|
|
77
|
+
} else {
|
|
78
|
+
Err(apierror!(TableError(value.clone())))
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#[ext]
|
|
84
|
+
pub(crate) impl UpdateData {
|
|
85
|
+
fn from_js_value_partial(
|
|
86
|
+
value: &JsValue,
|
|
87
|
+
format: Option<TableReadFormat>,
|
|
88
|
+
) -> ApiResult<Option<UpdateData>> {
|
|
89
|
+
if value.is_undefined() {
|
|
90
|
+
Err(apierror!(TableError(value.clone())))
|
|
91
|
+
} else if value.is_string() {
|
|
92
|
+
match format {
|
|
93
|
+
None | Some(TableReadFormat::Csv) => {
|
|
94
|
+
Ok(Some(UpdateData::Csv(value.as_string().into_apierror()?)))
|
|
95
|
+
},
|
|
96
|
+
Some(TableReadFormat::JsonString) => Ok(Some(UpdateData::JsonRows(
|
|
97
|
+
value.as_string().into_apierror()?,
|
|
98
|
+
))),
|
|
99
|
+
Some(TableReadFormat::ColumnsString) => Ok(Some(UpdateData::JsonColumns(
|
|
100
|
+
value.as_string().into_apierror()?,
|
|
101
|
+
))),
|
|
102
|
+
Some(TableReadFormat::Arrow) => Ok(Some(UpdateData::Arrow(
|
|
103
|
+
value.as_string().into_apierror()?.into_bytes().into(),
|
|
104
|
+
))),
|
|
105
|
+
Some(TableReadFormat::Ndjson) => {
|
|
106
|
+
Ok(Some(UpdateData::Ndjson(value.as_string().into_apierror()?)))
|
|
107
|
+
},
|
|
108
|
+
}
|
|
109
|
+
} else if value.is_instance_of::<ArrayBuffer>() {
|
|
110
|
+
let uint8array = Uint8Array::new(value);
|
|
111
|
+
let slice = uint8array.to_vec();
|
|
112
|
+
match format {
|
|
113
|
+
Some(TableReadFormat::Csv) => Ok(Some(UpdateData::Csv(String::from_utf8(slice)?))),
|
|
114
|
+
Some(TableReadFormat::JsonString) => {
|
|
115
|
+
Ok(Some(UpdateData::JsonRows(String::from_utf8(slice)?)))
|
|
116
|
+
},
|
|
117
|
+
Some(TableReadFormat::ColumnsString) => {
|
|
118
|
+
Ok(Some(UpdateData::JsonColumns(String::from_utf8(slice)?)))
|
|
119
|
+
},
|
|
120
|
+
Some(TableReadFormat::Ndjson) => {
|
|
121
|
+
Ok(Some(UpdateData::Ndjson(String::from_utf8(slice)?)))
|
|
122
|
+
},
|
|
123
|
+
None | Some(TableReadFormat::Arrow) => Ok(Some(UpdateData::Arrow(slice.into()))),
|
|
124
|
+
}
|
|
125
|
+
} else if let Some(uint8array) = value.dyn_ref::<Uint8Array>() {
|
|
126
|
+
let slice = uint8array.to_vec();
|
|
127
|
+
match format {
|
|
128
|
+
Some(TableReadFormat::Csv) => Ok(Some(UpdateData::Csv(String::from_utf8(slice)?))),
|
|
129
|
+
Some(TableReadFormat::JsonString) => {
|
|
130
|
+
Ok(Some(UpdateData::JsonRows(String::from_utf8(slice)?)))
|
|
131
|
+
},
|
|
132
|
+
Some(TableReadFormat::ColumnsString) => {
|
|
133
|
+
Ok(Some(UpdateData::JsonColumns(String::from_utf8(slice)?)))
|
|
134
|
+
},
|
|
135
|
+
Some(TableReadFormat::Ndjson) => {
|
|
136
|
+
Ok(Some(UpdateData::Ndjson(String::from_utf8(slice)?)))
|
|
137
|
+
},
|
|
138
|
+
None | Some(TableReadFormat::Arrow) => Ok(Some(UpdateData::Arrow(slice.into()))),
|
|
139
|
+
}
|
|
140
|
+
} else if value.is_instance_of::<Array>() {
|
|
141
|
+
let rows = JSON::stringify(value)?.as_string().into_apierror()?;
|
|
142
|
+
Ok(Some(UpdateData::JsonRows(rows)))
|
|
143
|
+
} else {
|
|
144
|
+
Ok(None)
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fn from_js_value(value: &JsValue, format: Option<TableReadFormat>) -> ApiResult<UpdateData> {
|
|
149
|
+
match TableData::from_js_value(value, format)? {
|
|
150
|
+
TableData::Schema(_) => Err(ApiError::new(
|
|
151
|
+
"Method cannot be called with `Schema` argument",
|
|
152
|
+
)),
|
|
153
|
+
TableData::Update(x) => Ok(x),
|
|
154
|
+
TableData::View(_) => Err(ApiError::new(
|
|
155
|
+
"Method cannot be called with `Schema` argument",
|
|
156
|
+
)),
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
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
|
+
|
|
13
|
+
/// Convenient short-name functions for common browser globals. These types are
|
|
14
|
+
/// not `send` and cannot be made static.
|
|
15
|
+
pub mod global {
|
|
16
|
+
pub fn window() -> web_sys::Window {
|
|
17
|
+
web_sys::window().unwrap()
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
pub fn document() -> web_sys::Document {
|
|
21
|
+
window().document().unwrap()
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
pub fn performance() -> web_sys::Performance {
|
|
25
|
+
window().performance().unwrap()
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
pub fn body() -> web_sys::HtmlElement {
|
|
29
|
+
document().body().unwrap()
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
pub fn navigator() -> web_sys::Navigator {
|
|
33
|
+
window().navigator()
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
pub fn clipboard() -> web_sys::Clipboard {
|
|
37
|
+
navigator().clipboard()
|
|
38
|
+
}
|
|
39
|
+
}
|