perspective_client/config/
view_config.rs1use std::collections::HashMap;
14
15use serde::{Deserialize, Serialize};
16use ts_rs::TS;
17
18use super::aggregates::*;
19use super::expressions::*;
20use super::filters::*;
21use super::sort::*;
22use crate::proto;
23use crate::proto::columns_update;
24
25#[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize)]
26#[serde(deny_unknown_fields)]
27pub struct ViewConfig {
28 #[serde(default)]
29 pub group_by: Vec<String>,
30
31 #[serde(default)]
32 pub split_by: Vec<String>,
33
34 #[serde(default)]
35 pub sort: Vec<Sort>,
36
37 #[serde(default)]
38 pub filter: Vec<Filter>,
39
40 #[serde(skip_serializing_if = "is_default_value")]
41 #[serde(default)]
42 pub filter_op: FilterReducer,
43
44 #[serde(default)]
45 pub expressions: Expressions,
46
47 #[serde(default)]
48 pub columns: Vec<Option<String>>,
49
50 #[serde(default)]
51 pub aggregates: HashMap<String, Aggregate>,
52
53 #[serde(skip_serializing_if = "Option::is_none")]
54 #[serde(default)]
55 pub group_by_depth: Option<u32>,
56}
57
58fn is_default_value<A: Default + PartialEq>(value: &A) -> bool {
59 value == &A::default()
60}
61
62#[derive(Clone, Debug, Deserialize, Default, Serialize, TS)]
63#[serde(deny_unknown_fields)]
64pub struct ViewConfigUpdate {
65 #[serde(skip_serializing_if = "Option::is_none")]
78 #[serde(default)]
79 #[ts(optional)]
80 pub group_by: Option<Vec<String>>,
81
82 #[serde(skip_serializing_if = "Option::is_none")]
90 #[serde(default)]
91 #[ts(optional)]
92 pub split_by: Option<Vec<String>>,
93
94 #[serde(skip_serializing_if = "Option::is_none")]
100 #[serde(default)]
101 #[ts(optional)]
102 pub columns: Option<Vec<Option<String>>>,
103
104 #[serde(skip_serializing_if = "Option::is_none")]
115 #[serde(default)]
116 #[ts(optional)]
117 pub filter: Option<Vec<Filter>>,
118
119 #[serde(skip_serializing_if = "Option::is_none")]
128 #[serde(default)]
129 #[ts(optional)]
130 pub sort: Option<Vec<Sort>>,
131
132 #[serde(skip_serializing_if = "Option::is_none")]
138 #[serde(default)]
139 #[ts(optional)]
140 pub expressions: Option<Expressions>,
141
142 #[serde(skip_serializing_if = "Option::is_none")]
154 #[serde(default)]
155 #[ts(optional)]
156 pub aggregates: Option<HashMap<String, Aggregate>>,
157
158 #[serde(skip_serializing)]
159 #[serde(default)]
160 #[ts(optional)]
161 pub group_by_depth: Option<u32>,
162
163 #[serde(skip_serializing_if = "Option::is_none")]
164 #[serde(default)]
165 #[ts(optional)]
166 pub filter_op: Option<FilterReducer>,
167}
168
169impl From<ViewConfigUpdate> for proto::ViewConfig {
170 fn from(value: ViewConfigUpdate) -> Self {
171 proto::ViewConfig {
172 group_by: value.group_by.unwrap_or_default(),
173 split_by: value.split_by.unwrap_or_default(),
174 columns: value.columns.map(|x| proto::ColumnsUpdate {
175 opt_columns: Some(columns_update::OptColumns::Columns(
176 proto::columns_update::Columns {
177 columns: x.into_iter().flatten().collect(),
178 },
179 )),
180 }),
181 filter: value
182 .filter
183 .unwrap_or_default()
184 .into_iter()
185 .map(|x| x.into())
186 .collect(),
187 filter_op: value
188 .filter_op
189 .map(proto::view_config::FilterReducer::from)
190 .unwrap_or_default() as i32,
191 sort: value
192 .sort
193 .unwrap_or_default()
194 .into_iter()
195 .map(|x| x.into())
196 .collect(),
197 expressions: value.expressions.unwrap_or_default().0,
198 aggregates: value
199 .aggregates
200 .unwrap_or_default()
201 .into_iter()
202 .map(|(x, y)| (x, y.into()))
203 .collect(),
204 group_by_depth: value.group_by_depth,
205 }
206 }
207}
208
209impl From<FilterReducer> for proto::view_config::FilterReducer {
210 fn from(value: FilterReducer) -> Self {
211 match value {
212 FilterReducer::And => proto::view_config::FilterReducer::And,
213 FilterReducer::Or => proto::view_config::FilterReducer::Or,
214 }
215 }
216}
217
218impl From<proto::view_config::FilterReducer> for FilterReducer {
219 fn from(value: proto::view_config::FilterReducer) -> Self {
220 match value {
221 proto::view_config::FilterReducer::And => FilterReducer::And,
222 proto::view_config::FilterReducer::Or => FilterReducer::Or,
223 }
224 }
225}
226
227impl From<ViewConfig> for ViewConfigUpdate {
228 fn from(value: ViewConfig) -> Self {
229 ViewConfigUpdate {
230 group_by: Some(value.group_by),
231 split_by: Some(value.split_by),
232 columns: Some(value.columns),
233 filter: Some(value.filter),
234 filter_op: Some(value.filter_op),
235 sort: Some(value.sort),
236 expressions: Some(value.expressions),
237 aggregates: Some(value.aggregates),
238 group_by_depth: value.group_by_depth,
239 }
240 }
241}
242
243impl From<proto::ViewConfig> for ViewConfig {
244 fn from(value: proto::ViewConfig) -> Self {
245 ViewConfig {
246 group_by: value.group_by,
247 split_by: value.split_by,
248 columns: match value.columns.unwrap_or_default().opt_columns {
249 Some(columns_update::OptColumns::Columns(x)) => {
250 x.columns.into_iter().map(Some).collect()
251 },
252 _ => {
253 vec![]
254 },
255 },
256 filter: value.filter.into_iter().map(|x| x.into()).collect(),
257 filter_op: proto::view_config::FilterReducer::try_from(value.filter_op)
258 .unwrap_or_default()
259 .into(),
260 sort: value.sort.into_iter().map(|x| x.into()).collect(),
261 expressions: Expressions(value.expressions),
262 aggregates: value
263 .aggregates
264 .into_iter()
265 .map(|(x, y)| (x, y.into()))
266 .collect(),
267 group_by_depth: value.group_by_depth,
268 }
269 }
270}
271
272impl From<ViewConfigUpdate> for ViewConfig {
273 fn from(value: ViewConfigUpdate) -> Self {
274 ViewConfig {
275 group_by: value.group_by.unwrap_or_default(),
276 split_by: value.split_by.unwrap_or_default(),
277 columns: value.columns.unwrap_or_default(),
278 filter: value.filter.unwrap_or_default(),
279 filter_op: value.filter_op.unwrap_or_default(),
280 sort: value.sort.unwrap_or_default(),
281 expressions: value.expressions.unwrap_or_default(),
282 aggregates: value.aggregates.unwrap_or_default(),
283 group_by_depth: value.group_by_depth,
284 }
285 }
286}
287
288impl From<proto::ViewConfig> for ViewConfigUpdate {
289 fn from(value: proto::ViewConfig) -> Self {
290 ViewConfigUpdate {
291 group_by: Some(value.group_by),
292 split_by: Some(value.split_by),
293 columns: match value.columns.unwrap_or_default().opt_columns {
294 Some(columns_update::OptColumns::Columns(x)) => {
295 Some(x.columns.into_iter().map(Some).collect())
296 },
297 _ => None,
298 },
299 filter: Some(value.filter.into_iter().map(|x| x.into()).collect()),
300 filter_op: Some(
301 proto::view_config::FilterReducer::try_from(value.filter_op)
302 .unwrap_or_default()
303 .into(),
304 ),
305 sort: Some(value.sort.into_iter().map(|x| x.into()).collect()),
306 expressions: Some(Expressions(value.expressions)),
307 aggregates: Some(
308 value
309 .aggregates
310 .into_iter()
311 .map(|(x, y)| (x, y.into()))
312 .collect(),
313 ),
314 group_by_depth: value.group_by_depth,
315 }
316 }
317}
318
319impl ViewConfig {
320 fn _apply<T>(field: &mut T, update: Option<T>) -> bool {
321 match update {
322 None => false,
323 Some(update) => {
324 *field = update;
325 true
326 },
327 }
328 }
329
330 pub fn reset(&mut self, reset_expressions: bool) {
331 let mut config = Self::default();
332 if !reset_expressions {
333 config.expressions = self.expressions.clone();
334 }
335 std::mem::swap(self, &mut config);
336 }
337
338 pub fn apply_update(&mut self, update: ViewConfigUpdate) -> bool {
341 let mut changed = false;
342 changed = Self::_apply(&mut self.group_by, update.group_by) || changed;
343 changed = Self::_apply(&mut self.split_by, update.split_by) || changed;
344 changed = Self::_apply(&mut self.columns, update.columns) || changed;
345 changed = Self::_apply(&mut self.filter, update.filter) || changed;
346 changed = Self::_apply(&mut self.sort, update.sort) || changed;
347 changed = Self::_apply(&mut self.aggregates, update.aggregates) || changed;
348 changed = Self::_apply(&mut self.expressions, update.expressions) || changed;
349 changed
350 }
351
352 pub fn is_aggregated(&self) -> bool {
353 !self.group_by.is_empty()
354 }
355
356 pub fn is_column_expression_in_use(&self, name: &str) -> bool {
357 let name = name.to_owned();
358 self.group_by.contains(&name)
359 || self.split_by.contains(&name)
360 || self.sort.iter().any(|x| x.0 == name)
361 || self.filter.iter().any(|x| x.column() == name)
362 || self.columns.contains(&Some(name))
363 }
364
365 pub fn is_equivalent(&self, other: &Self) -> bool {
370 let _self = self.clone();
371 let _self = ViewConfig {
372 columns: _self.columns.into_iter().filter(|x| x.is_some()).collect(),
373 .._self
374 };
375
376 let _other = other.clone();
377 let _other = ViewConfig {
378 columns: _other.columns.into_iter().filter(|x| x.is_some()).collect(),
379 ..other.clone()
380 };
381
382 _self == _other
383 }
384}