perspective_viewer/config/
viewer_config.rs1use std::ops::Deref;
14use std::sync::LazyLock;
15
16use perspective_client::config::*;
17use perspective_js::utils::*;
18use serde::{Deserialize, Deserializer, Serialize};
19use serde_json::Value;
20use ts_rs::TS;
21use wasm_bindgen::prelude::*;
22
23use crate::renderer::ColumnConfigMap;
24
25#[derive(Debug, Default, Serialize, PartialEq, TS)]
32#[serde(deny_unknown_fields)]
33pub struct ViewerConfig<V: TS = String> {
34 pub settings: bool,
35
36 #[serde(flatten)]
37 pub panel: PanelViewerConfig<V>,
38}
39
40#[derive(Debug, Default, Serialize, PartialEq, TS)]
44pub struct PanelViewerConfig<V: TS = String> {
45 pub version: V,
46 pub columns_config: ColumnConfigMap,
47 pub plugin: String,
48 pub plugin_config: serde_json::Map<String, Value>,
49 pub table: Option<String>,
50 pub theme: Option<String>,
51 pub title: Option<String>,
52
53 #[serde(flatten)]
54 pub view_config: ViewConfig,
55}
56
57impl<V: TS> Deref for ViewerConfig<V> {
58 type Target = PanelViewerConfig<V>;
59
60 fn deref(&self) -> &Self::Target {
61 &self.panel
62 }
63}
64
65pub static API_VERSION: LazyLock<&'static str> = LazyLock::new(|| {
66 #[derive(Deserialize)]
67 struct Package {
68 version: &'static str,
69 }
70 let pkg: &'static str = include_str!("../../../package.json");
71 let pkg: Package = serde_json::from_str(pkg).unwrap();
72 pkg.version
73});
74
75impl ViewerConfig {
76 pub fn encode(&self) -> ApiResult<JsValue> {
78 Ok(JsValue::from_serde_ext(self)?)
79 }
80}
81
82#[derive(Clone, Debug, TS, Deserialize, PartialEq, Serialize)]
83#[serde(transparent)]
84pub struct PluginConfig(serde_json::Value);
85
86impl Deref for PluginConfig {
87 type Target = Value;
88
89 fn deref(&self) -> &Self::Target {
90 &self.0
91 }
92}
93
94#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize, TS)]
95pub struct ViewerConfigUpdate {
96 #[serde(default)]
97 #[ts(as = "Option<_>")]
98 #[ts(optional)]
99 pub version: VersionUpdate,
100
101 #[serde(default)]
102 #[ts(as = "Option<_>")]
103 #[ts(optional)]
104 pub plugin: PluginUpdate,
105
106 #[serde(default)]
107 #[ts(as = "Option<_>")]
108 #[ts(optional)]
109 pub title: TitleUpdate,
110
111 #[serde(default)]
112 #[ts(as = "Option<_>")]
113 #[ts(optional)]
114 pub table: TableUpdate,
115
116 #[serde(default)]
117 #[ts(as = "Option<_>")]
118 #[ts(optional)]
119 pub theme: ThemeUpdate,
120
121 #[serde(default)]
122 #[ts(as = "Option<_>")]
123 #[ts(optional)]
124 pub settings: SettingsUpdate,
125
126 #[serde(default)]
127 #[ts(as = "Option<_>")]
128 #[ts(optional)]
129 pub plugin_config: PluginConfigUpdate,
130
131 #[serde(default)]
132 #[ts(as = "Option<_>")]
133 #[ts(optional)]
134 pub columns_config: ColumnConfigUpdate,
135
136 #[serde(flatten)]
137 pub view_config: ViewConfigUpdate,
138}
139
140impl ViewerConfigUpdate {
141 pub fn decode(update: &JsValue) -> ApiResult<Self> {
144 Ok(update.into_serde_ext()?)
145 }
146
147 pub fn migrate(&self) -> ApiResult<Self> {
148 Ok(self.clone())
150 }
151}
152
153impl std::fmt::Display for ViewerConfigUpdate {
154 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
155 write!(
156 f,
157 "{}",
158 serde_json::to_string(self).map_err(|_| std::fmt::Error)?
159 )
160 }
161}
162
163#[derive(Clone, Debug, Serialize, PartialEq, TS)]
164#[serde(untagged)]
165pub enum OptionalUpdate<T: Clone> {
167 #[ts(skip)]
168 SetDefault,
169
170 Missing,
173
174 Update(T),
177}
178
179pub type PluginUpdate = OptionalUpdate<String>;
180pub type SettingsUpdate = OptionalUpdate<bool>;
181pub type ThemeUpdate = OptionalUpdate<String>;
182pub type TitleUpdate = OptionalUpdate<String>;
183pub type TableUpdate = OptionalUpdate<String>;
184pub type VersionUpdate = OptionalUpdate<String>;
185pub type ColumnConfigUpdate = OptionalUpdate<ColumnConfigMap>;
186pub type PluginConfigUpdate = OptionalUpdate<serde_json::Map<String, Value>>;
187
188impl<T: Clone> Default for OptionalUpdate<T> {
190 fn default() -> Self {
191 Self::Missing
192 }
193}
194
195impl<T: Clone> From<Option<T>> for OptionalUpdate<T> {
198 fn from(opt: Option<T>) -> Self {
199 match opt {
200 Some(v) => Self::Update(v),
201 None => Self::SetDefault,
202 }
203 }
204}
205
206impl<'a, T> Deserialize<'a> for OptionalUpdate<T>
209where
210 T: Deserialize<'a> + Clone,
211{
212 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
213 where
214 D: Deserializer<'a>,
215 {
216 Option::deserialize(deserializer).map(Into::into)
217 }
218}