@perspective-dev/viewer 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.
Files changed (394) hide show
  1. package/LICENSE.md +193 -0
  2. package/README.md +4 -0
  3. package/dist/cdn/perspective-viewer.js +3 -0
  4. package/dist/cdn/perspective-viewer.js.map +7 -0
  5. package/dist/css/dracula.css +1 -0
  6. package/dist/css/gruvbox-dark.css +1 -0
  7. package/dist/css/gruvbox.css +1 -0
  8. package/dist/css/icons.css +1 -0
  9. package/dist/css/intl/de.css +1 -0
  10. package/dist/css/intl/es.css +1 -0
  11. package/dist/css/intl/fr.css +1 -0
  12. package/dist/css/intl/ja.css +1 -0
  13. package/dist/css/intl/pt.css +1 -0
  14. package/dist/css/intl/zh.css +1 -0
  15. package/dist/css/intl.css +1 -0
  16. package/dist/css/monokai.css +1 -0
  17. package/dist/css/pro-dark.css +1 -0
  18. package/dist/css/pro.css +1 -0
  19. package/dist/css/solarized-dark.css +1 -0
  20. package/dist/css/solarized.css +1 -0
  21. package/dist/css/themes.css +1 -0
  22. package/dist/css/vaporwave.css +1 -0
  23. package/dist/css/variables.css +0 -0
  24. package/dist/esm/bootstrap.d.ts +1 -0
  25. package/dist/esm/extensions.d.ts +70 -0
  26. package/dist/esm/perspective-viewer.d.ts +39 -0
  27. package/dist/esm/perspective-viewer.inline.js +3 -0
  28. package/dist/esm/perspective-viewer.inline.js.map +7 -0
  29. package/dist/esm/perspective-viewer.js +3 -0
  30. package/dist/esm/perspective-viewer.js.map +7 -0
  31. package/dist/esm/plugin.d.ts +195 -0
  32. package/dist/esm/ts-rs/Aggregate.d.ts +1 -0
  33. package/dist/esm/ts-rs/ColumnConfigValues.d.ts +31 -0
  34. package/dist/esm/ts-rs/CustomDatetimeFormat.d.ts +1 -0
  35. package/dist/esm/ts-rs/CustomDatetimeStyleConfig.d.ts +15 -0
  36. package/dist/esm/ts-rs/CustomNumberFormatConfig.d.ts +18 -0
  37. package/dist/esm/ts-rs/DatetimeColorMode.d.ts +1 -0
  38. package/dist/esm/ts-rs/DatetimeFormatType.d.ts +6 -0
  39. package/dist/esm/ts-rs/Expressions.d.ts +3 -0
  40. package/dist/esm/ts-rs/Filter.d.ts +2 -0
  41. package/dist/esm/ts-rs/FilterReducer.d.ts +1 -0
  42. package/dist/esm/ts-rs/FilterTerm.d.ts +2 -0
  43. package/dist/esm/ts-rs/FormatMode.d.ts +1 -0
  44. package/dist/esm/ts-rs/FormatUnit.d.ts +1 -0
  45. package/dist/esm/ts-rs/NumberBackgroundMode.d.ts +1 -0
  46. package/dist/esm/ts-rs/NumberForegroundMode.d.ts +1 -0
  47. package/dist/esm/ts-rs/OptionalUpdate.d.ts +1 -0
  48. package/dist/esm/ts-rs/PluginConfig.d.ts +2 -0
  49. package/dist/esm/ts-rs/RoundingMode.d.ts +1 -0
  50. package/dist/esm/ts-rs/RoundingPriority.d.ts +1 -0
  51. package/dist/esm/ts-rs/Scalar.d.ts +5 -0
  52. package/dist/esm/ts-rs/SignDisplay.d.ts +1 -0
  53. package/dist/esm/ts-rs/SimpleDatetimeFormat.d.ts +1 -0
  54. package/dist/esm/ts-rs/SimpleDatetimeStyleConfig.d.ts +6 -0
  55. package/dist/esm/ts-rs/Sort.d.ts +2 -0
  56. package/dist/esm/ts-rs/SortDir.d.ts +1 -0
  57. package/dist/esm/ts-rs/StringColorMode.d.ts +1 -0
  58. package/dist/esm/ts-rs/TrailingZeroDisplay.d.ts +1 -0
  59. package/dist/esm/ts-rs/UseGrouping.d.ts +1 -0
  60. package/dist/esm/ts-rs/ViewerConfigUpdate.d.ts +102 -0
  61. package/dist/esm/ts-rs/serde_json/JsonValue.d.ts +3 -0
  62. package/dist/wasm/perspective-viewer.d.ts +1291 -0
  63. package/dist/wasm/perspective-viewer.js +3883 -0
  64. package/dist/wasm/perspective-viewer.wasm +0 -0
  65. package/dist/wasm/perspective-viewer.wasm.d.ts +142 -0
  66. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline0.js +1 -0
  67. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline1.js +1 -0
  68. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline2.js +1 -0
  69. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline3.js +44 -0
  70. package/dist/wasm/snippets/perspective-viewer-c69283f6f62a5f14/inline4.js +1 -0
  71. package/package.json +55 -0
  72. package/src/less/aggregate-selector.less +72 -0
  73. package/src/less/column-dropdown.less +95 -0
  74. package/src/less/column-selector.less +551 -0
  75. package/src/less/column-settings-panel.less +255 -0
  76. package/src/less/column-style.less +319 -0
  77. package/src/less/column-symbol-attributes.less +73 -0
  78. package/src/less/config-selector.less +363 -0
  79. package/src/less/containers/dropdown-menu.less +94 -0
  80. package/src/less/containers/pairs-list.less +46 -0
  81. package/src/less/containers/radio-list.less +29 -0
  82. package/src/less/containers/scroll-panel.less +27 -0
  83. package/src/less/containers/split-panel.less +80 -0
  84. package/src/less/containers/tabs.less +90 -0
  85. package/src/less/dom/checkbox.less +100 -0
  86. package/src/less/dom/scrollbar.less +35 -0
  87. package/src/less/dom/select.less +63 -0
  88. package/src/less/empty-column.less +72 -0
  89. package/src/less/expression-editor.less +152 -0
  90. package/src/less/filter-dropdown.less +53 -0
  91. package/src/less/filter-item.less +126 -0
  92. package/src/less/form/code-editor.less +137 -0
  93. package/src/less/form/debug.less +88 -0
  94. package/src/less/function-dropdown.less +45 -0
  95. package/src/less/plugin-selector.less +173 -0
  96. package/src/less/render-warning.less +81 -0
  97. package/src/less/status-bar.less +551 -0
  98. package/src/less/type-icon.less +68 -0
  99. package/src/less/viewer.less +368 -0
  100. package/src/rust/components/column_dropdown.rs +162 -0
  101. package/src/rust/components/column_selector/active_column.rs +462 -0
  102. package/src/rust/components/column_selector/add_expression_button.rs +59 -0
  103. package/src/rust/components/column_selector/aggregate_selector.rs +186 -0
  104. package/src/rust/components/column_selector/config_selector.rs +674 -0
  105. package/src/rust/components/column_selector/empty_column.rs +134 -0
  106. package/src/rust/components/column_selector/expression_toolbar.rs +45 -0
  107. package/src/rust/components/column_selector/filter_column.rs +530 -0
  108. package/src/rust/components/column_selector/inactive_column.rs +221 -0
  109. package/src/rust/components/column_selector/invalid_column.rs +37 -0
  110. package/src/rust/components/column_selector/pivot_column.rs +93 -0
  111. package/src/rust/components/column_selector/sort_column.rs +135 -0
  112. package/src/rust/components/column_selector.rs +426 -0
  113. package/src/rust/components/column_settings_sidebar/attributes_tab.rs +34 -0
  114. package/src/rust/components/column_settings_sidebar/mod.rs +17 -0
  115. package/src/rust/components/column_settings_sidebar/save_settings.rs +68 -0
  116. package/src/rust/components/column_settings_sidebar/sidebar.rs +396 -0
  117. package/src/rust/components/column_settings_sidebar/style_tab/column_style.rs +177 -0
  118. package/src/rust/components/column_settings_sidebar/style_tab/stub.rs +34 -0
  119. package/src/rust/components/column_settings_sidebar/style_tab/symbol/row_selector.rs +101 -0
  120. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_config.rs +24 -0
  121. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_pairs.rs +185 -0
  122. package/src/rust/components/column_settings_sidebar/style_tab/symbol/symbol_selector.rs +55 -0
  123. package/src/rust/components/column_settings_sidebar/style_tab/symbol.rs +131 -0
  124. package/src/rust/components/column_settings_sidebar/style_tab.rs +231 -0
  125. package/src/rust/components/containers/dragdrop_list.rs +325 -0
  126. package/src/rust/components/containers/dropdown_menu.rs +104 -0
  127. package/src/rust/components/containers/kvpair.rs +47 -0
  128. package/src/rust/components/containers/mod.rs +28 -0
  129. package/src/rust/components/containers/scroll_panel.rs +311 -0
  130. package/src/rust/components/containers/scroll_panel_item.rs +111 -0
  131. package/src/rust/components/containers/select.rs +269 -0
  132. package/src/rust/components/containers/sidebar.rs +89 -0
  133. package/src/rust/components/containers/split_panel.rs +483 -0
  134. package/src/rust/components/containers/tab_list.rs +104 -0
  135. package/src/rust/components/containers/tests/mod.rs +11 -0
  136. package/src/rust/components/containers/tests/split_panel.rs +91 -0
  137. package/src/rust/components/containers/trap_door_panel.rs +50 -0
  138. package/src/rust/components/copy_dropdown.rs +136 -0
  139. package/src/rust/components/datetime_column_style/custom.rs +222 -0
  140. package/src/rust/components/datetime_column_style/simple.rs +122 -0
  141. package/src/rust/components/datetime_column_style.rs +284 -0
  142. package/src/rust/components/editable_header.rs +183 -0
  143. package/src/rust/components/empty_row.rs +169 -0
  144. package/src/rust/components/error_message.rs +56 -0
  145. package/src/rust/components/export_dropdown.rs +153 -0
  146. package/src/rust/components/expression_editor.rs +153 -0
  147. package/src/rust/components/filter_dropdown.rs +144 -0
  148. package/src/rust/components/font_loader.rs +254 -0
  149. package/src/rust/components/form/code_editor.rs +286 -0
  150. package/src/rust/components/form/color_range_selector.rs +115 -0
  151. package/src/rust/components/form/color_selector.rs +54 -0
  152. package/src/rust/components/form/debug.rs +253 -0
  153. package/src/rust/components/form/highlight.rs +50 -0
  154. package/src/rust/components/form/mod.rs +29 -0
  155. package/src/rust/components/form/number_field.rs +72 -0
  156. package/src/rust/components/form/number_input.rs +45 -0
  157. package/src/rust/components/form/number_range_field.rs +77 -0
  158. package/src/rust/components/form/optional_field.rs +53 -0
  159. package/src/rust/components/form/select_field.rs +104 -0
  160. package/src/rust/components/function_dropdown.rs +151 -0
  161. package/src/rust/components/mod.rs +45 -0
  162. package/src/rust/components/modal.rs +171 -0
  163. package/src/rust/components/number_column_style.rs +522 -0
  164. package/src/rust/components/plugin_selector.rs +196 -0
  165. package/src/rust/components/render_warning.rs +178 -0
  166. package/src/rust/components/status_bar.rs +315 -0
  167. package/src/rust/components/status_bar_counter.rs +229 -0
  168. package/src/rust/components/status_indicator.rs +164 -0
  169. package/src/rust/components/string_column_style.rs +186 -0
  170. package/src/rust/components/style/local_style.rs +35 -0
  171. package/src/rust/components/style/mod.rs +52 -0
  172. package/src/rust/components/style/style_cache.rs +132 -0
  173. package/src/rust/components/style/style_provider.rs +65 -0
  174. package/src/rust/components/style_controls/number_string_format/digits_section.rs +140 -0
  175. package/src/rust/components/style_controls/number_string_format/misc_section.rs +56 -0
  176. package/src/rust/components/style_controls/number_string_format/style_section.rs +69 -0
  177. package/src/rust/components/style_controls/number_string_format/types.rs +31 -0
  178. package/src/rust/components/style_controls/number_string_format.rs +259 -0
  179. package/src/rust/components/style_controls.rs +15 -0
  180. package/src/rust/components/type_icon.rs +53 -0
  181. package/src/rust/components/viewer.rs +668 -0
  182. package/src/rust/config/columns_config.rs +132 -0
  183. package/src/rust/config/datetime_column_style/color_mode.rs +62 -0
  184. package/src/rust/config/datetime_column_style/custom.rs +121 -0
  185. package/src/rust/config/datetime_column_style/custom_format.rs +90 -0
  186. package/src/rust/config/datetime_column_style/simple.rs +55 -0
  187. package/src/rust/config/datetime_column_style/simple_format.rs +86 -0
  188. package/src/rust/config/datetime_column_style.rs +104 -0
  189. package/src/rust/config/mod.rs +30 -0
  190. package/src/rust/config/number_column_style.rs +138 -0
  191. package/src/rust/config/number_string_format/enums.rs +298 -0
  192. package/src/rust/config/number_string_format.rs +269 -0
  193. package/src/rust/config/string_column_style.rs +140 -0
  194. package/src/rust/config/view_config.rs +24 -0
  195. package/src/rust/config/viewer_config.rs +337 -0
  196. package/src/rust/custom_elements/column_dropdown.rs +123 -0
  197. package/src/rust/custom_elements/copy_dropdown.rs +112 -0
  198. package/src/rust/custom_elements/debug_plugin.rs +114 -0
  199. package/src/rust/custom_elements/export_dropdown.rs +112 -0
  200. package/src/rust/custom_elements/filter_dropdown.rs +179 -0
  201. package/src/rust/custom_elements/function_dropdown.rs +115 -0
  202. package/src/rust/custom_elements/mod.rs +24 -0
  203. package/src/rust/custom_elements/modal.rs +395 -0
  204. package/src/rust/custom_elements/viewer.rs +880 -0
  205. package/src/rust/custom_events.rs +214 -0
  206. package/src/rust/dragdrop.rs +362 -0
  207. package/src/rust/exprtk/cursor.rs +81 -0
  208. package/src/rust/exprtk/mod.rs +17 -0
  209. package/src/rust/exprtk/tokenize/comment.rs +21 -0
  210. package/src/rust/exprtk/tokenize/number.rs +44 -0
  211. package/src/rust/exprtk/tokenize/string.rs +98 -0
  212. package/src/rust/exprtk/tokenize/symbol.rs +26 -0
  213. package/src/rust/exprtk/tokenize.rs +190 -0
  214. package/src/rust/js/clipboard.rs +77 -0
  215. package/src/rust/js/clipboard_item.rs +21 -0
  216. package/src/rust/js/intersection_observer.rs +32 -0
  217. package/src/rust/js/mimetype.rs +42 -0
  218. package/src/rust/js/mod.rs +29 -0
  219. package/src/rust/js/plugin.rs +167 -0
  220. package/src/rust/js/resize_observer.rs +37 -0
  221. package/src/rust/js/testing.rs +149 -0
  222. package/src/rust/lib.rs +116 -0
  223. package/src/rust/model/columns_iter_set.rs +340 -0
  224. package/src/rust/model/copy_export.rs +157 -0
  225. package/src/rust/model/create_col.rs +59 -0
  226. package/src/rust/model/edit_expression.rs +95 -0
  227. package/src/rust/model/export_app.rs +58 -0
  228. package/src/rust/model/export_method.rs +151 -0
  229. package/src/rust/model/get_viewer_config.rs +85 -0
  230. package/src/rust/model/intersection_observer.rs +81 -0
  231. package/src/rust/model/is_invalid_drop.rs +36 -0
  232. package/src/rust/model/mod.rs +93 -0
  233. package/src/rust/model/plugin_column_styles.rs +106 -0
  234. package/src/rust/model/resize_observer.rs +98 -0
  235. package/src/rust/model/restore_and_render.rs +113 -0
  236. package/src/rust/model/structural.rs +73 -0
  237. package/src/rust/model/update_and_render.rs +74 -0
  238. package/src/rust/presentation.rs +370 -0
  239. package/src/rust/renderer/activate.rs +49 -0
  240. package/src/rust/renderer/limits.rs +200 -0
  241. package/src/rust/renderer/plugin_store.rs +45 -0
  242. package/src/rust/renderer/registry.rs +135 -0
  243. package/src/rust/renderer/render_timer.rs +185 -0
  244. package/src/rust/renderer.rs +463 -0
  245. package/src/rust/session/column_defaults_update.rs +141 -0
  246. package/src/rust/session/drag_drop_update.rs +179 -0
  247. package/src/rust/session/metadata.rs +308 -0
  248. package/src/rust/session/replace_expression_update.rs +131 -0
  249. package/src/rust/session/view_subscription.rs +189 -0
  250. package/src/rust/session.rs +794 -0
  251. package/src/rust/utils/browser/blob.rs +49 -0
  252. package/src/rust/utils/browser/download.rs +26 -0
  253. package/src/rust/utils/browser/mod.rs +24 -0
  254. package/src/rust/utils/browser/request_animation_frame.rs +76 -0
  255. package/src/rust/utils/browser/selection.rs +79 -0
  256. package/src/rust/utils/browser/tests/debounce.rs +114 -0
  257. package/src/rust/utils/browser/tests/mod.rs +13 -0
  258. package/src/rust/utils/custom_element.rs +85 -0
  259. package/src/rust/utils/datetime.rs +49 -0
  260. package/src/rust/utils/debounce.rs +54 -0
  261. package/src/rust/utils/hooks/mod.rs +15 -0
  262. package/src/rust/utils/hooks/use_async_callback.rs +53 -0
  263. package/src/rust/utils/mod.rs +114 -0
  264. package/src/rust/utils/number_format.rs +48 -0
  265. package/src/rust/utils/pubsub.rs +222 -0
  266. package/src/rust/utils/scope.rs +54 -0
  267. package/src/rust/utils/tee.rs +88 -0
  268. package/src/rust/utils/tests/mod.rs +14 -0
  269. package/src/rust/utils/tests/pubsub.rs +95 -0
  270. package/src/rust/utils/tests/request_animation_frame.rs +42 -0
  271. package/src/rust/utils/wasm_abi.rs +61 -0
  272. package/src/rust/utils/weak_scope.rs +39 -0
  273. package/src/svg/align-scroll-icon.svg +7 -0
  274. package/src/svg/bg-pattern.png +0 -0
  275. package/src/svg/boolean-type.svg +3 -0
  276. package/src/svg/checkbox-checked-icon.svg +7 -0
  277. package/src/svg/checkbox-hover.svg +4 -0
  278. package/src/svg/checkbox-off.svg +4 -0
  279. package/src/svg/checkbox-on.svg +4 -0
  280. package/src/svg/checkbox-unchecked-icon.svg +3 -0
  281. package/src/svg/close-icon.svg +6 -0
  282. package/src/svg/column-settings-icon.svg +6 -0
  283. package/src/svg/datagrid-select-column.svg +15 -0
  284. package/src/svg/datagrid-select-region.svg +15 -0
  285. package/src/svg/datagrid-select-row.svg +13 -0
  286. package/src/svg/datagrid.svg +13 -0
  287. package/src/svg/date-type.svg +23 -0
  288. package/src/svg/downloading.gif +0 -0
  289. package/src/svg/drag-handle.svg +10 -0
  290. package/src/svg/drawer-bg-pattern-hidpi.png +0 -0
  291. package/src/svg/drawer-tab-hover.svg +8 -0
  292. package/src/svg/drawer-tab-invert-hover.svg +9 -0
  293. package/src/svg/drawer-tab-invert.svg +8 -0
  294. package/src/svg/drawer-tab.svg +7 -0
  295. package/src/svg/dropdown-selector-light.svg +3 -0
  296. package/src/svg/dropdown-selector.svg +3 -0
  297. package/src/svg/duplicate-icon.svg +4 -0
  298. package/src/svg/editable-icon.svg +5 -0
  299. package/src/svg/export-icon.svg +4 -0
  300. package/src/svg/expression.svg +10 -0
  301. package/src/svg/free-scroll-icon.svg +5 -0
  302. package/src/svg/inactive-mouseover.svg +4 -0
  303. package/src/svg/mega-menu-icons-candlestick.svg +38 -0
  304. package/src/svg/mega-menu-icons-datagrid.svg +38 -0
  305. package/src/svg/mega-menu-icons-heatmap.svg +60 -0
  306. package/src/svg/mega-menu-icons-map-scatter.svg +43 -0
  307. package/src/svg/mega-menu-icons-ohlc.svg +40 -0
  308. package/src/svg/mega-menu-icons-sunburst.svg +39 -0
  309. package/src/svg/mega-menu-icons-treemap.svg +44 -0
  310. package/src/svg/mega-menu-icons-x-bar.svg +36 -0
  311. package/src/svg/mega-menu-icons-x-y-line.svg +38 -0
  312. package/src/svg/mega-menu-icons-x-y-scatter.svg +72 -0
  313. package/src/svg/mega-menu-icons-y-area.svg +73 -0
  314. package/src/svg/mega-menu-icons-y-bar.svg +36 -0
  315. package/src/svg/mega-menu-icons-y-line.svg +36 -0
  316. package/src/svg/mega-menu-icons-y-scatter.svg +72 -0
  317. package/src/svg/number-type.svg +4 -0
  318. package/src/svg/radio-hover.svg +4 -0
  319. package/src/svg/radio-off.svg +3 -0
  320. package/src/svg/radio-on.svg +4 -0
  321. package/src/svg/read-only-icon.svg +6 -0
  322. package/src/svg/revert-icon.svg +4 -0
  323. package/src/svg/sort-abs-asc-icon.svg +3 -0
  324. package/src/svg/sort-abs-col-asc-icon.svg +3 -0
  325. package/src/svg/sort-abs-col-desc-icon.svg +3 -0
  326. package/src/svg/sort-abs-desc-icon.svg +3 -0
  327. package/src/svg/sort-asc-icon.svg +4 -0
  328. package/src/svg/sort-col-asc-icon.svg +4 -0
  329. package/src/svg/sort-col-desc-icon.svg +4 -0
  330. package/src/svg/sort-desc-icon.svg +4 -0
  331. package/src/svg/sort-none-icon.svg +3 -0
  332. package/src/svg/status_error.svg +70 -0
  333. package/src/svg/status_ok.svg +26 -0
  334. package/src/svg/string-type.svg +8 -0
  335. package/src/svg/theme-icon.svg +5 -0
  336. package/src/svg/updating.gif +0 -0
  337. package/src/themes/dracula.less +101 -0
  338. package/src/themes/gruvbox-dark.less +116 -0
  339. package/src/themes/gruvbox.less +152 -0
  340. package/src/themes/icons.less +130 -0
  341. package/src/themes/intl/de.less +102 -0
  342. package/src/themes/intl/es.less +102 -0
  343. package/src/themes/intl/fr.less +102 -0
  344. package/src/themes/intl/ja.less +102 -0
  345. package/src/themes/intl/pt.less +102 -0
  346. package/src/themes/intl/zh.less +102 -0
  347. package/src/themes/intl.less +102 -0
  348. package/src/themes/monokai.less +107 -0
  349. package/src/themes/pro-dark.less +147 -0
  350. package/src/themes/pro.less +186 -0
  351. package/src/themes/solarized-dark.less +78 -0
  352. package/src/themes/solarized.less +102 -0
  353. package/src/themes/themes.less +21 -0
  354. package/src/themes/vaporwave.less +145 -0
  355. package/src/themes/variables.less +24 -0
  356. package/src/ts/bootstrap.ts +26 -0
  357. package/src/ts/extensions.ts +124 -0
  358. package/src/ts/perspective-viewer.cdn.ts +19 -0
  359. package/src/ts/perspective-viewer.inline.ts +23 -0
  360. package/src/ts/perspective-viewer.ts +59 -0
  361. package/src/ts/plugin.ts +279 -0
  362. package/src/ts/ts-rs/Aggregate.ts +3 -0
  363. package/src/ts/ts-rs/ColumnConfigValues.ts +14 -0
  364. package/src/ts/ts-rs/CustomDatetimeFormat.ts +3 -0
  365. package/src/ts/ts-rs/CustomDatetimeStyleConfig.ts +5 -0
  366. package/src/ts/ts-rs/CustomNumberFormatConfig.ts +8 -0
  367. package/src/ts/ts-rs/DatetimeColorMode.ts +3 -0
  368. package/src/ts/ts-rs/DatetimeFormatType.ts +8 -0
  369. package/src/ts/ts-rs/Expressions.ts +3 -0
  370. package/src/ts/ts-rs/Filter.ts +4 -0
  371. package/src/ts/ts-rs/FilterReducer.ts +3 -0
  372. package/src/ts/ts-rs/FilterTerm.ts +4 -0
  373. package/src/ts/ts-rs/FormatMode.ts +3 -0
  374. package/src/ts/ts-rs/FormatUnit.ts +3 -0
  375. package/src/ts/ts-rs/NumberBackgroundMode.ts +3 -0
  376. package/src/ts/ts-rs/NumberForegroundMode.ts +3 -0
  377. package/src/ts/ts-rs/OnUpdateData.ts +8 -0
  378. package/src/ts/ts-rs/OptionalUpdate.ts +3 -0
  379. package/src/ts/ts-rs/PluginConfig.ts +4 -0
  380. package/src/ts/ts-rs/RoundingMode.ts +3 -0
  381. package/src/ts/ts-rs/RoundingPriority.ts +3 -0
  382. package/src/ts/ts-rs/Scalar.ts +7 -0
  383. package/src/ts/ts-rs/SignDisplay.ts +3 -0
  384. package/src/ts/ts-rs/SimpleDatetimeFormat.ts +3 -0
  385. package/src/ts/ts-rs/SimpleDatetimeStyleConfig.ts +4 -0
  386. package/src/ts/ts-rs/Sort.ts +4 -0
  387. package/src/ts/ts-rs/SortDir.ts +3 -0
  388. package/src/ts/ts-rs/StringColorMode.ts +3 -0
  389. package/src/ts/ts-rs/TrailingZeroDisplay.ts +3 -0
  390. package/src/ts/ts-rs/UseGrouping.ts +3 -0
  391. package/src/ts/ts-rs/ViewOnUpdateResp.ts +3 -0
  392. package/src/ts/ts-rs/ViewerConfigUpdate.ts +90 -0
  393. package/src/ts/ts-rs/serde_json/JsonValue.ts +3 -0
  394. package/tsconfig.json +15 -0
@@ -0,0 +1,794 @@
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
+ mod column_defaults_update;
14
+ mod drag_drop_update;
15
+ mod metadata;
16
+ mod replace_expression_update;
17
+ mod view_subscription;
18
+
19
+ use std::cell::{Ref, RefCell};
20
+ use std::collections::HashSet;
21
+ use std::future::Future;
22
+ use std::ops::Deref;
23
+ use std::rc::Rc;
24
+ use std::sync::Arc;
25
+
26
+ use perspective_client::config::*;
27
+ use perspective_client::{ClientError, ReconnectCallback, View, ViewWindow};
28
+ use perspective_js::apierror;
29
+ use perspective_js::utils::*;
30
+ use wasm_bindgen::prelude::*;
31
+ use yew::html::ImplicitClone;
32
+ use yew::prelude::*;
33
+
34
+ use self::metadata::*;
35
+ use self::replace_expression_update::*;
36
+ pub use self::view_subscription::ViewStats;
37
+ use self::view_subscription::*;
38
+ use crate::dragdrop::*;
39
+ use crate::js::plugin::*;
40
+ use crate::utils::*;
41
+
42
+ /// The `Session` struct is the principal interface to the Perspective engine,
43
+ /// the `Table` and `View` objects for this viewer, and all associated state
44
+ /// including the `ViewConfig`.
45
+ #[derive(Clone, Default)]
46
+ pub struct Session(Arc<SessionHandle>);
47
+
48
+ impl ImplicitClone for Session {}
49
+
50
+ /// Immutable state for `Session`.
51
+ #[derive(Default)]
52
+ pub struct SessionHandle {
53
+ session_data: RefCell<SessionData>,
54
+ pub table_updated: PubSub<()>,
55
+ pub table_loaded: PubSub<()>,
56
+ pub view_created: PubSub<()>,
57
+ pub view_config_changed: PubSub<()>,
58
+ pub stats_changed: PubSub<Option<ViewStats>>,
59
+ pub table_errored: PubSub<ApiError>,
60
+ }
61
+
62
+ /// Mutable state for `Session`.
63
+ #[derive(Default)]
64
+ pub struct SessionData {
65
+ table: Option<perspective_client::Table>,
66
+ metadata: SessionMetadata,
67
+ old_config: Option<ViewConfig>,
68
+ config: ViewConfig,
69
+ view_sub: Option<ViewSubscription>,
70
+ stats: Option<ViewStats>,
71
+ is_clean: bool,
72
+ is_paused: bool,
73
+ error: Option<TableErrorState>,
74
+ }
75
+
76
+ #[derive(Clone)]
77
+ pub struct TableErrorState(ApiError, Option<ReconnectCallback>);
78
+
79
+ impl Deref for Session {
80
+ type Target = SessionHandle;
81
+
82
+ fn deref(&self) -> &Self::Target {
83
+ &self.0
84
+ }
85
+ }
86
+
87
+ impl PartialEq for Session {
88
+ fn eq(&self, other: &Self) -> bool {
89
+ Arc::ptr_eq(&self.0, &other.0)
90
+ }
91
+ }
92
+
93
+ impl Deref for SessionHandle {
94
+ type Target = RefCell<SessionData>;
95
+
96
+ fn deref(&self) -> &Self::Target {
97
+ &self.session_data
98
+ }
99
+ }
100
+
101
+ pub type MetadataRef<'a> = std::cell::Ref<'a, SessionMetadata>;
102
+ pub type MetadataMutRef<'a> = std::cell::RefMut<'a, SessionMetadata>;
103
+
104
+ impl Session {
105
+ pub fn metadata(&self) -> MetadataRef<'_> {
106
+ std::cell::Ref::map(self.borrow(), |x| &x.metadata)
107
+ }
108
+
109
+ pub fn metadata_mut(&self) -> MetadataMutRef<'_> {
110
+ std::cell::RefMut::map(self.borrow_mut(), |x| &mut x.metadata)
111
+ }
112
+
113
+ pub fn invalidate(&self) {
114
+ self.borrow_mut().error = None;
115
+ self.borrow_mut().is_clean = false;
116
+ self.borrow_mut().view_sub = None;
117
+ }
118
+
119
+ /// Reset this `Session`'s `View` state, but preserve the `Table`.
120
+ ///
121
+ /// # Arguments
122
+ /// - `reset_expressions` Whether to reset the `expressions` property.
123
+ pub fn reset(&self, reset_expressions: bool) -> impl Future<Output = ApiResult<()>> + use<> {
124
+ self.borrow_mut().is_clean = false;
125
+ let view = self.0.borrow_mut().view_sub.take();
126
+ self.borrow_mut().view_sub = None;
127
+ self.borrow_mut().config.reset(reset_expressions);
128
+ let err = self.get_error();
129
+ async move {
130
+ let res = view.delete().await;
131
+ if let Some(err) = err { Err(err) } else { res }
132
+ }
133
+ }
134
+
135
+ /// Reset this (presumably shared) `Session` to its initial state, returning
136
+ /// a bool indicating whether this `Session` had a table which was
137
+ /// deleted. TODO Table should be an immutable constructor parameter to
138
+ /// `Session`.
139
+ pub async fn delete(&self) -> ApiResult<()> {
140
+ self.borrow_mut().is_clean = false;
141
+ self.borrow_mut().config.reset(true);
142
+ self.borrow_mut().metadata = SessionMetadata::default();
143
+ self.borrow_mut().table = None;
144
+ let view = self.borrow_mut().view_sub.take();
145
+ view.delete().await?;
146
+ Ok(())
147
+ }
148
+
149
+ pub fn has_table(&self) -> bool {
150
+ self.borrow().table.is_some()
151
+ }
152
+
153
+ pub fn get_table(&self) -> Option<perspective_client::Table> {
154
+ self.borrow().table.clone()
155
+ }
156
+
157
+ /// Reset this `Session`'s state with a new `Table`. Implicitly clears the
158
+ /// `ViewSubscription`, which will need to be re-initialized later via
159
+ /// `create_view()`.
160
+ pub async fn set_table(&self, table: perspective_client::Table) -> ApiResult<JsValue> {
161
+ match SessionMetadata::from_table(&table).await {
162
+ Ok(metadata) => {
163
+ let client = table.get_client();
164
+ let set_error = self.table_errored.as_boxfn();
165
+ let session = self.clone();
166
+ let poll_loop = LocalPollLoop::new(move |(message, reconnect): (ApiError, _)| {
167
+ set_error(message.clone());
168
+ session.borrow_mut().error = Some(TableErrorState(message, reconnect));
169
+ if let Some(sub) = session.borrow_mut().view_sub.take() {
170
+ sub.dismiss();
171
+ }
172
+
173
+ Ok(JsValue::UNDEFINED)
174
+ });
175
+
176
+ let _callback_id = client
177
+ .on_error(Box::new(move |message: ClientError, reconnect| {
178
+ let poll_loop = poll_loop.clone();
179
+ async move {
180
+ poll_loop.poll((message.into(), reconnect)).await;
181
+ Ok(())
182
+ }
183
+ }))
184
+ .await?;
185
+
186
+ let sub = self.borrow_mut().view_sub.take();
187
+ self.borrow_mut().metadata = metadata;
188
+ self.borrow_mut().table = Some(table);
189
+ sub.delete().await?;
190
+ self.table_loaded.emit(());
191
+ Ok(JsValue::UNDEFINED)
192
+ },
193
+ Err(err) => self.set_error(false, err).await.map(|_| JsValue::UNDEFINED),
194
+ }
195
+ }
196
+
197
+ pub async fn set_error(&self, reset_table: bool, err: ApiError) -> ApiResult<()> {
198
+ let session = self.clone();
199
+ let poll_loop = LocalPollLoop::new(move |()| {
200
+ session.invalidate();
201
+ ApiFuture::spawn(session.reset(true));
202
+ Ok(JsValue::UNDEFINED)
203
+ });
204
+
205
+ self.borrow_mut().error = Some(TableErrorState(
206
+ err.clone(),
207
+ Some(ReconnectCallback::new(move || {
208
+ clone!(poll_loop);
209
+ Box::pin(async move {
210
+ poll_loop.poll(()).await;
211
+ Ok(())
212
+ })
213
+ })),
214
+ ));
215
+
216
+ self.table_errored.emit(err.clone());
217
+ let sub = self.borrow_mut().view_sub.take();
218
+ if reset_table {
219
+ self.borrow_mut().metadata = SessionMetadata::default();
220
+ self.borrow_mut().table = None;
221
+ }
222
+
223
+ sub.delete().await?;
224
+ Err(err)
225
+ }
226
+
227
+ pub fn set_pause(&self, pause: bool) -> bool {
228
+ self.borrow_mut().is_clean = false;
229
+ if pause == self.borrow().is_paused {
230
+ false
231
+ } else if pause {
232
+ ApiFuture::spawn(self.borrow_mut().view_sub.take().delete());
233
+ self.borrow_mut().is_paused = true;
234
+ true
235
+ } else {
236
+ self.borrow_mut().is_paused = false;
237
+ true
238
+ }
239
+ }
240
+
241
+ pub async fn await_table(&self) -> ApiResult<()> {
242
+ if self.js_get_table().is_none() {
243
+ self.table_loaded.listen_once().await?;
244
+ let _ = self.js_get_table().ok_or("No table set")?;
245
+ }
246
+
247
+ Ok(())
248
+ }
249
+
250
+ pub fn js_get_table(&self) -> Option<JsValue> {
251
+ Some(perspective_js::Table::from(self.borrow().table.clone()?).into())
252
+ }
253
+
254
+ pub fn js_get_view(&self) -> Option<JsValue> {
255
+ let view = self.borrow().view_sub.as_ref()?.get_view().clone();
256
+ Some(perspective_js::View::from(view).into())
257
+ }
258
+
259
+ pub fn is_errored(&self) -> bool {
260
+ self.borrow().error.is_some()
261
+ }
262
+
263
+ pub fn get_error(&self) -> Option<ApiError> {
264
+ self.borrow().error.as_ref().map(|x| x.0.clone())
265
+ }
266
+
267
+ pub fn is_reconnect(&self) -> bool {
268
+ self.borrow()
269
+ .error
270
+ .as_ref()
271
+ .map(|x| x.1.is_some())
272
+ .unwrap_or_default()
273
+ }
274
+
275
+ pub async fn reconnect(&self) -> ApiResult<()> {
276
+ let err = self.borrow().error.clone();
277
+ if let Some(TableErrorState(_, Some(reconnect))) = err {
278
+ reconnect().await?;
279
+ self.borrow_mut().error = None;
280
+ self.borrow_mut().is_clean = false;
281
+ self.borrow_mut().view_sub = None;
282
+ }
283
+
284
+ Ok(())
285
+ }
286
+
287
+ pub fn is_column_expression_in_use(&self, name: &str) -> bool {
288
+ self.borrow().config.is_column_expression_in_use(name)
289
+ }
290
+
291
+ /// Is this column currently being used or not
292
+ pub fn is_column_active(&self, name: &str) -> bool {
293
+ let config = Ref::map(self.borrow(), |x| &x.config);
294
+ config.columns.iter().any(|maybe_col| {
295
+ maybe_col
296
+ .as_ref()
297
+ .map(|col| col == name)
298
+ .unwrap_or_default()
299
+ }) || config.group_by.iter().any(|col| col == name)
300
+ || config.split_by.iter().any(|col| col == name)
301
+ || config.filter.iter().any(|col| col.column() == name)
302
+ || config.sort.iter().any(|col| col.0 == name)
303
+ }
304
+
305
+ pub fn create_drag_drop_update(
306
+ &self,
307
+ column: String,
308
+ index: usize,
309
+ drop: DragTarget,
310
+ drag: DragEffect,
311
+ requirements: &ViewConfigRequirements,
312
+ ) -> ViewConfigUpdate {
313
+ use self::drag_drop_update::*;
314
+ let col_type = self
315
+ .metadata()
316
+ .get_column_table_type(column.as_str())
317
+ .unwrap();
318
+
319
+ self.get_view_config().create_drag_drop_update(
320
+ column,
321
+ col_type,
322
+ index,
323
+ drop,
324
+ drag,
325
+ requirements,
326
+ self.metadata().get_features().unwrap(),
327
+ )
328
+ }
329
+
330
+ /// An async task which replaces a `column` aliased expression with another.
331
+ pub async fn create_replace_expression_update(
332
+ &self,
333
+ old_expr_name: &str,
334
+ new_expr: &Expression<'static>,
335
+ ) -> ViewConfigUpdate {
336
+ let old_expr_val = self
337
+ .metadata()
338
+ .get_expression_by_alias(old_expr_name)
339
+ .unwrap();
340
+
341
+ let old_expr = Expression::new(Some(old_expr_name.into()), old_expr_val.into());
342
+
343
+ use self::replace_expression_update::*;
344
+ self.get_view_config()
345
+ .create_replace_expression_update(&old_expr, new_expr)
346
+ }
347
+
348
+ pub async fn create_rename_expression_update(
349
+ &self,
350
+ old_expr_name: String,
351
+ new_expr_name: Option<String>,
352
+ ) -> ViewConfigUpdate {
353
+ let old_expr_val = self
354
+ .metadata()
355
+ .get_expression_by_alias(&old_expr_name)
356
+ .expect_throw(&format!("Unable to get expr with name {old_expr_name}"));
357
+ let old_expr = Expression::new(Some(old_expr_name.into()), old_expr_val.clone().into());
358
+ let new_expr = Expression::new(new_expr_name.map(|n| n.into()), old_expr_val.into());
359
+ self.get_view_config()
360
+ .create_replace_expression_update(&old_expr, &new_expr)
361
+ }
362
+
363
+ /// Validate an expression string and marshall the results.
364
+ pub async fn validate_expr(
365
+ &self,
366
+ expr: &str,
367
+ ) -> Result<Option<perspective_client::ExprValidationError>, ApiError> {
368
+ let table = self.borrow().table.as_ref().unwrap().clone();
369
+ let errors = table
370
+ .validate_expressions(
371
+ ExpressionsDeserde::Map(std::collections::HashMap::from_iter([(
372
+ "_".to_string(),
373
+ expr.to_string(),
374
+ )]))
375
+ .into(),
376
+ )
377
+ .await?
378
+ .errors;
379
+
380
+ Ok(errors.get("_").cloned())
381
+ }
382
+
383
+ pub async fn arrow_as_vec(
384
+ &self,
385
+ flat: bool,
386
+ window: Option<ViewWindow>,
387
+ ) -> Result<Vec<u8>, ApiError> {
388
+ Ok(self
389
+ .flat_view(flat)
390
+ .await?
391
+ .to_arrow(window.unwrap_or_default())
392
+ .await?
393
+ .into())
394
+ }
395
+
396
+ pub async fn arrow_as_jsvalue(
397
+ &self,
398
+ flat: bool,
399
+ window: Option<ViewWindow>,
400
+ ) -> Result<js_sys::ArrayBuffer, ApiError> {
401
+ let arrow = self
402
+ .flat_view(flat)
403
+ .await?
404
+ .to_arrow(window.unwrap_or_default())
405
+ .await?;
406
+ Ok(js_sys::Uint8Array::from(&arrow[..])
407
+ .buffer()
408
+ .unchecked_into())
409
+ }
410
+
411
+ pub async fn ndjson_as_jsvalue(
412
+ &self,
413
+ flat: bool,
414
+ window: Option<ViewWindow>,
415
+ ) -> Result<js_sys::JsString, ApiError> {
416
+ let json: String = self
417
+ .flat_view(flat)
418
+ .await?
419
+ .to_ndjson(window.unwrap_or_default())
420
+ .await?;
421
+
422
+ Ok(json.into())
423
+ }
424
+
425
+ pub async fn json_as_jsvalue(
426
+ &self,
427
+ flat: bool,
428
+ window: Option<ViewWindow>,
429
+ ) -> Result<js_sys::Object, ApiError> {
430
+ let json: String = self
431
+ .flat_view(flat)
432
+ .await?
433
+ .to_columns_string(window.unwrap_or_default())
434
+ .await?;
435
+
436
+ Ok(js_sys::JSON::parse(&json)?.unchecked_into())
437
+ }
438
+
439
+ pub async fn csv_as_jsvalue(
440
+ &self,
441
+ flat: bool,
442
+ window: Option<ViewWindow>,
443
+ ) -> Result<js_sys::JsString, ApiError> {
444
+ let window = window.unwrap_or_default();
445
+ let csv = self.flat_view(flat).await?.to_csv(window).await;
446
+ Ok(csv.map(js_sys::JsString::from)?)
447
+ }
448
+
449
+ pub fn get_view(&self) -> Option<View> {
450
+ self.borrow()
451
+ .view_sub
452
+ .as_ref()
453
+ .map(|sub| sub.get_view().clone())
454
+ }
455
+
456
+ pub fn get_table_stats(&self) -> Option<ViewStats> {
457
+ self.borrow().stats.clone()
458
+ }
459
+
460
+ pub fn get_view_config(&self) -> Ref<ViewConfig> {
461
+ Ref::map(self.borrow(), |x| &x.config)
462
+ }
463
+
464
+ /// Get all unique column values for a given column name.
465
+ ///
466
+ /// Use the `.to_csv()` method, as I suspected copying this large string
467
+ /// once was more efficient than copying many smaller strings, and
468
+ /// string copying shows up frequently when doing performance analysis.
469
+ ///
470
+ /// TODO Does not work with expressions yet.
471
+ ///
472
+ /// # Arguments
473
+ /// - `column` The name of the column (or expression).
474
+ pub async fn get_column_values(&self, column: String) -> Result<Vec<String>, ApiError> {
475
+ let expressions = Some(self.borrow().config.expressions.clone());
476
+ let config = ViewConfigUpdate {
477
+ group_by: Some(vec![column]),
478
+ columns: Some(vec![]),
479
+ expressions,
480
+ ..ViewConfigUpdate::default()
481
+ };
482
+
483
+ let table = self.borrow().table.clone().unwrap();
484
+ let view = table.view(Some(config.clone())).await?;
485
+ let csv = view.to_csv(ViewWindow::default()).await?;
486
+
487
+ ApiFuture::spawn(async move {
488
+ view.delete().await?;
489
+ Ok(())
490
+ });
491
+
492
+ let res = csv
493
+ .lines()
494
+ .map(|val| {
495
+ if val.starts_with('\"') && val.ends_with('\"') {
496
+ (val[1..val.len() - 1]).to_owned()
497
+ } else {
498
+ val.to_owned()
499
+ }
500
+ })
501
+ .skip(2)
502
+ .collect::<Vec<String>>();
503
+ Ok(res)
504
+ }
505
+
506
+ pub fn set_update_column_defaults(
507
+ &self,
508
+ config_update: &mut ViewConfigUpdate,
509
+ requirements: &ViewConfigRequirements,
510
+ ) {
511
+ use self::column_defaults_update::*;
512
+ config_update.set_update_column_defaults(
513
+ &self.metadata(),
514
+ &self.borrow().config.columns,
515
+ requirements,
516
+ )
517
+ }
518
+
519
+ /// Update the config, setting the `columns` property to the plugin defaults
520
+ /// if provided.
521
+ pub fn update_view_config(&self, config_update: ViewConfigUpdate) -> ApiResult<()> {
522
+ if let Some(x) = self.borrow().error.as_ref() {
523
+ tracing::warn!("Errored state");
524
+
525
+ // Load bearing return
526
+ return Err(ApiError::new(x.0.clone()));
527
+ }
528
+
529
+ if self.borrow_mut().config.apply_update(config_update) {
530
+ self.0.borrow_mut().is_clean = false;
531
+ self.view_config_changed.emit(());
532
+ }
533
+
534
+ Ok(())
535
+ }
536
+
537
+ pub fn reset_stats(&self) {
538
+ self.update_stats(ViewStats::default());
539
+ }
540
+
541
+ #[cfg(test)]
542
+ pub fn set_stats(&self, stats: ViewStats) {
543
+ self.update_stats(stats)
544
+ }
545
+
546
+ /// In order to create a new view in this session, the session must first be
547
+ /// validated to create a `ValidSession<'_>` guard.
548
+ pub async fn validate(&self) -> Result<ValidSession<'_>, ApiError> {
549
+ let old = self.borrow_mut().old_config.take();
550
+ let is_diff = match old.as_ref() {
551
+ Some(old) => !old.is_equivalent(&self.borrow().config),
552
+ None => true,
553
+ };
554
+
555
+ if let Err(err) = self.validate_view_config().await {
556
+ let session = self.clone();
557
+ let poll_loop = LocalPollLoop::new(move |()| {
558
+ session.invalidate();
559
+ ApiFuture::spawn(session.reset(true));
560
+ Ok(JsValue::UNDEFINED)
561
+ });
562
+
563
+ self.borrow_mut().error = Some(TableErrorState(
564
+ err.clone(),
565
+ Some(ReconnectCallback::new(move || {
566
+ clone!(poll_loop);
567
+ Box::pin(async move {
568
+ poll_loop.poll(()).await;
569
+ Ok(())
570
+ })
571
+ })),
572
+ ));
573
+
574
+ if let Some(config) = old {
575
+ self.borrow_mut().config = config;
576
+ } else {
577
+ self.reset(true).await?;
578
+ }
579
+
580
+ return Err(err);
581
+ } else {
582
+ let old_config = Some(self.borrow().config.clone());
583
+ self.borrow_mut().old_config = old_config;
584
+ }
585
+
586
+ Ok(ValidSession(self, is_diff))
587
+ }
588
+
589
+ async fn flat_view(&self, flat: bool) -> ApiResult<View> {
590
+ if flat {
591
+ let table = self.borrow().table.clone().into_apierror()?;
592
+ Ok(table.view(None).await?)
593
+ } else {
594
+ self.borrow()
595
+ .view_sub
596
+ .as_ref()
597
+ .map(|x| x.get_view().clone())
598
+ .into_apierror()
599
+ }
600
+ }
601
+
602
+ fn update_stats(&self, stats: ViewStats) {
603
+ self.borrow_mut().stats = Some(stats.clone());
604
+ self.stats_changed.emit(Some(stats));
605
+ }
606
+
607
+ async fn validate_view_config(&self) -> ApiResult<()> {
608
+ let mut config = self.borrow().config.clone();
609
+ let table_columns = self
610
+ .metadata()
611
+ .get_table_columns()
612
+ .into_iter()
613
+ .flatten()
614
+ .cloned()
615
+ .collect::<Vec<String>>();
616
+
617
+ let all_columns: HashSet<String> = table_columns.iter().cloned().collect();
618
+ let mut view_columns: HashSet<&str> = HashSet::new();
619
+ let table = self
620
+ .borrow()
621
+ .table
622
+ .as_ref()
623
+ .ok_or_else(|| apierror!(NoTableError))?
624
+ .clone();
625
+
626
+ let expression_names = if self.metadata().get_features().unwrap().expressions {
627
+ let valid_recs = table
628
+ .validate_expressions(config.expressions.clone())
629
+ .await?;
630
+
631
+ self.metadata_mut().update_expressions(&valid_recs)?
632
+ } else {
633
+ HashSet::default()
634
+ };
635
+
636
+ if config.columns.is_empty() {
637
+ config.columns = table_columns.into_iter().map(Some).collect();
638
+ }
639
+
640
+ for column in config.columns.iter().flatten() {
641
+ if all_columns.contains(column) || expression_names.contains(column) {
642
+ let _existed = view_columns.insert(column);
643
+ } else {
644
+ return Err(apierror!(InvalidViewerConfigError(
645
+ "columns",
646
+ column.to_owned()
647
+ )));
648
+ }
649
+ }
650
+
651
+ for column in config.group_by.iter() {
652
+ if all_columns.contains(column) || expression_names.contains(column) {
653
+ let _existed = view_columns.insert(column);
654
+ } else {
655
+ return Err(apierror!(InvalidViewerConfigError(
656
+ "group_by",
657
+ column.to_owned(),
658
+ )));
659
+ }
660
+ }
661
+
662
+ for column in config.split_by.iter() {
663
+ if all_columns.contains(column) || expression_names.contains(column) {
664
+ let _existed = view_columns.insert(column);
665
+ } else {
666
+ return Err(apierror!(InvalidViewerConfigError(
667
+ "split_by",
668
+ column.to_owned(),
669
+ )));
670
+ }
671
+ }
672
+
673
+ for sort in config.sort.iter() {
674
+ if all_columns.contains(&sort.0) || expression_names.contains(&sort.0) {
675
+ let _existed = view_columns.insert(&sort.0);
676
+ } else {
677
+ return Err(apierror!(InvalidViewerConfigError(
678
+ "sort",
679
+ sort.0.to_owned(),
680
+ )));
681
+ }
682
+ }
683
+
684
+ for filter in config.filter.iter() {
685
+ // TODO check filter op
686
+ if all_columns.contains(filter.column()) || expression_names.contains(filter.column()) {
687
+ let _existed = view_columns.insert(filter.column());
688
+ } else {
689
+ return Err(apierror!(InvalidViewerConfigError(
690
+ "filter",
691
+ filter.column().to_owned(),
692
+ )));
693
+ }
694
+ }
695
+
696
+ config
697
+ .aggregates
698
+ .retain(|column, _| view_columns.contains(column.as_str()));
699
+
700
+ self.borrow_mut().config = config;
701
+ Ok(())
702
+ }
703
+
704
+ fn reset_clean(&self) -> bool {
705
+ let mut is_clean = true;
706
+ std::mem::swap(&mut is_clean, &mut self.0.borrow_mut().is_clean);
707
+ is_clean
708
+ }
709
+ }
710
+
711
+ /// A newtype wrapper which only provides `create_view()`
712
+ pub struct ValidSession<'a>(&'a Session, bool);
713
+
714
+ impl<'a> ValidSession<'a> {
715
+ /// Set a new `View` (derived from this `Session`'s `Table`), and create the
716
+ /// `update()` subscription, consuming this `ValidSession<'_>` and returning
717
+ /// the original `&Session`.
718
+ pub async fn create_view(&self) -> Result<&'a Session, ApiError> {
719
+ if !self.0.reset_clean() && !self.0.borrow().is_paused {
720
+ if !self.1 {
721
+ let config = self.0.borrow().config.clone();
722
+ if let Some(sub) = &mut self.0.borrow_mut().view_sub.as_mut() {
723
+ sub.update_view_config(Rc::new(config));
724
+ return Ok(self.0);
725
+ }
726
+ }
727
+
728
+ let table = self
729
+ .0
730
+ .borrow()
731
+ .table
732
+ .clone()
733
+ .ok_or("`restore()` called before `load()`")?;
734
+
735
+ let mut view_config = self.0.borrow().config.clone();
736
+
737
+ // Populate the aggreagtes with defaults as a courtesy to the
738
+ // virtual server api.
739
+ for col in view_config
740
+ .columns
741
+ .iter()
742
+ .flatten()
743
+ .chain(view_config.sort.iter().map(|x| &x.0))
744
+ {
745
+ if !view_config.aggregates.contains_key(col.as_str()) {
746
+ let agg = self
747
+ .0
748
+ .metadata()
749
+ .get_column_aggregates(col.as_str())
750
+ .into_apierror()?
751
+ .next()
752
+ .into_apierror()?;
753
+
754
+ let _ = view_config.aggregates.insert(col.to_string(), agg);
755
+ }
756
+ }
757
+
758
+ let view = table.view(Some(view_config.into())).await?;
759
+ let view_schema = view.schema().await?;
760
+ self.0.metadata_mut().update_view_schema(&view_schema)?;
761
+ let on_stats = Callback::from({
762
+ let this = self.0.clone();
763
+ move |stats| this.update_stats(stats)
764
+ });
765
+
766
+ let sub = {
767
+ let config = self.0.borrow().config.clone();
768
+ let on_update = self
769
+ .0
770
+ .metadata()
771
+ .get_features()
772
+ .unwrap()
773
+ .on_update
774
+ .then(|| self.0.table_updated.callback());
775
+
776
+ ViewSubscription::new(view, config, on_stats, on_update).await?
777
+ };
778
+
779
+ let view = self.0.borrow_mut().view_sub.take();
780
+ ApiFuture::spawn(view.delete());
781
+ self.0.borrow_mut().view_sub = Some(sub);
782
+ }
783
+
784
+ Ok(self.0)
785
+ }
786
+ }
787
+
788
+ impl Drop for ValidSession<'_> {
789
+ /// `ValidSession` is a guard for listeners of the `view_created` pubsub
790
+ /// event.
791
+ fn drop(&mut self) {
792
+ self.0.view_created.emit(());
793
+ }
794
+ }