perspective_js/
generic_sql_model.rs1use std::str::FromStr;
16
17use indexmap::IndexMap;
18use js_sys::Object;
19use perspective_client::config::ViewConfig;
20use perspective_client::proto::{ColumnType, ViewPort};
21use perspective_client::virtual_server;
22use wasm_bindgen::prelude::*;
23
24use crate::utils::*;
25
26#[wasm_bindgen]
31pub struct GenericSQLVirtualServerModel {
32 inner: virtual_server::GenericSQLVirtualServerModel,
33}
34
35#[wasm_bindgen]
36extern "C" {
37 pub type JsGenericSQLVirtualServerModelArgs;
38}
39
40#[wasm_bindgen]
41impl GenericSQLVirtualServerModel {
42 #[wasm_bindgen(constructor)]
44 pub fn new(args: Option<JsGenericSQLVirtualServerModelArgs>) -> Result<Self, JsValue> {
45 Ok(Self {
46 inner: virtual_server::GenericSQLVirtualServerModel::new(
47 args.map(|x| x.into_serde_ext())
48 .transpose()?
49 .unwrap_or_default(),
50 ),
51 })
52 }
53
54 #[wasm_bindgen(js_name = "getHostedTables")]
56 pub fn get_hosted_tables(&self) -> Result<String, JsValue> {
57 self.inner
58 .get_hosted_tables()
59 .map_err(|e| JsValue::from_str(&e.to_string()))
60 }
61
62 #[wasm_bindgen(js_name = "tableSchema")]
64 pub fn table_schema(&self, table_id: &str) -> Result<String, JsValue> {
65 self.inner
66 .table_schema(table_id)
67 .map_err(|e| JsValue::from_str(&e.to_string()))
68 }
69
70 #[wasm_bindgen(js_name = "tableSize")]
72 pub fn table_size(&self, table_id: &str) -> Result<String, JsValue> {
73 self.inner
74 .table_size(table_id)
75 .map_err(|e| JsValue::from_str(&e.to_string()))
76 }
77
78 #[wasm_bindgen(js_name = "viewColumnSize")]
80 pub fn view_column_size(&self, view_id: &str) -> Result<String, JsValue> {
81 self.inner
82 .view_column_size(view_id)
83 .map_err(|e| JsValue::from_str(&e.to_string()))
84 }
85
86 #[wasm_bindgen(js_name = "tableValidateExpression")]
88 pub fn table_validate_expression(
89 &self,
90 table_id: &str,
91 expression: &str,
92 ) -> Result<String, JsValue> {
93 self.inner
94 .table_validate_expression(table_id, expression)
95 .map_err(|e| JsValue::from_str(&e.to_string()))
96 }
97
98 #[wasm_bindgen(js_name = "viewDelete")]
100 pub fn view_delete(&self, view_id: &str) -> Result<String, JsValue> {
101 self.inner
102 .view_delete(view_id)
103 .map_err(|e| JsValue::from_str(&e.to_string()))
104 }
105
106 #[wasm_bindgen(js_name = "tableMakeView")]
109 pub fn table_make_view(
110 &self,
111 table_id: &str,
112 view_id: &str,
113 config: JsValue,
114 schema: JsValue,
115 ) -> Result<String, JsValue> {
116 let config: ViewConfig = serde_wasm_bindgen::from_value(config)
117 .map_err(|e| JsValue::from_str(&e.to_string()))?;
118
119 let schema = if schema.is_undefined() || schema.is_null() {
120 IndexMap::new()
121 } else {
122 self.parse_schema(schema)?
123 };
124
125 self.inner
126 .table_make_view(table_id, view_id, &config, &schema)
127 .map_err(|e| JsValue::from_str(&e.to_string()))
128 }
129
130 #[wasm_bindgen(js_name = "viewGetData")]
132 pub fn view_get_data(
133 &self,
134 view_id: &str,
135 config: JsValue,
136 viewport: JsValue,
137 schema: JsValue,
138 ) -> Result<String, JsValue> {
139 let config: ViewConfig = serde_wasm_bindgen::from_value(config)
140 .map_err(|e| JsValue::from_str(&e.to_string()))?;
141
142 let viewport: ViewPort = serde_wasm_bindgen::from_value(viewport)
143 .map_err(|e| JsValue::from_str(&e.to_string()))?;
144
145 let schema = self.parse_schema(schema)?;
146
147 self.inner
148 .view_get_data(view_id, &config, &viewport, &schema)
149 .map_err(|e| JsValue::from_str(&e.to_string()))
150 }
151
152 #[wasm_bindgen(js_name = "viewSchema")]
154 pub fn view_schema(&self, view_id: &str) -> Result<String, JsValue> {
155 self.inner
156 .view_schema(view_id)
157 .map_err(|e| JsValue::from_str(&e.to_string()))
158 }
159
160 #[wasm_bindgen(js_name = "viewSize")]
162 pub fn view_size(&self, view_id: &str) -> Result<String, JsValue> {
163 self.inner
164 .view_size(view_id)
165 .map_err(|e| JsValue::from_str(&e.to_string()))
166 }
167
168 #[wasm_bindgen(js_name = "viewGetMinMax")]
170 pub fn view_get_min_max(
171 &self,
172 view_id: &str,
173 column_name: &str,
174 config: JsValue,
175 ) -> Result<String, JsValue> {
176 let config: ViewConfig = serde_wasm_bindgen::from_value(config)
177 .map_err(|e| JsValue::from_str(&e.to_string()))?;
178
179 self.inner
180 .view_get_min_max(view_id, column_name, &config)
181 .map_err(|e| JsValue::from_str(&e.to_string()))
182 }
183}
184
185impl GenericSQLVirtualServerModel {
186 fn parse_schema(&self, schema: JsValue) -> Result<IndexMap<String, ColumnType>, JsValue> {
187 let obj = schema.dyn_ref::<Object>().ok_or_else(|| {
188 JsValue::from_str("Schema must be an object mapping column names to types")
189 })?;
190
191 let mut result = IndexMap::new();
192 let entries = Object::entries(obj);
193 for i in 0..entries.length() {
194 let entry = entries.get(i);
195 let entry_array = entry
196 .dyn_ref::<js_sys::Array>()
197 .ok_or_else(|| JsValue::from_str("Invalid schema entry"))?;
198 let key = entry_array
199 .get(0)
200 .as_string()
201 .ok_or_else(|| JsValue::from_str("Column name must be a string"))?;
202 let value = entry_array
203 .get(1)
204 .as_string()
205 .ok_or_else(|| JsValue::from_str("Column type must be a string"))?;
206 let column_type = ColumnType::from_str(&value)
207 .map_err(|_| JsValue::from_str(&format!("Unknown column type: {}", value)))?;
208 result.insert(key, column_type);
209 }
210 Ok(result)
211 }
212}