1use std::collections::HashMap;
14use std::fmt::Display;
15
16use serde::{Deserialize, Serialize};
17use ts_rs::TS;
18
19use super::aggregates::*;
20use super::expressions::*;
21use super::filters::*;
22use super::sort::*;
23use super::windows::*;
24use crate::proto;
25use crate::proto::columns_update;
26
27#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, TS)]
28pub enum GroupRollupMode {
29 #[default]
30 #[serde(rename = "rollup")]
31 Rollup,
32
33 #[serde(rename = "flat")]
34 Flat,
35
36 #[serde(rename = "total")]
37 Total,
38}
39
40impl Display for GroupRollupMode {
41 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
42 write!(fmt, "{}", match self {
43 Self::Rollup => "Rollup",
44 Self::Flat => "Flat",
45 Self::Total => "Total",
46 })
47 }
48}
49
50impl From<proto::GroupRollupMode> for GroupRollupMode {
51 fn from(value: proto::GroupRollupMode) -> Self {
52 match value {
53 proto::GroupRollupMode::Rollup => Self::Rollup,
54 proto::GroupRollupMode::Flat => Self::Flat,
55 proto::GroupRollupMode::Total => Self::Total,
56 }
57 }
58}
59
60impl From<GroupRollupMode> for proto::GroupRollupMode {
61 fn from(value: GroupRollupMode) -> Self {
62 match value {
63 GroupRollupMode::Rollup => proto::GroupRollupMode::Rollup,
64 GroupRollupMode::Flat => proto::GroupRollupMode::Flat,
65 GroupRollupMode::Total => proto::GroupRollupMode::Total,
66 }
67 }
68}
69
70#[derive(Clone, Copy, Debug, Default, Deserialize, Serialize, PartialEq, Eq, TS)]
77pub enum SplitRollupMode {
78 #[default]
79 #[serde(rename = "flat")]
80 Flat,
81
82 #[serde(rename = "rollup")]
83 Rollup,
84}
85
86impl Display for SplitRollupMode {
87 fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
88 write!(fmt, "{}", match self {
89 Self::Flat => "Flat",
90 Self::Rollup => "Rollup",
91 })
92 }
93}
94
95impl From<proto::SplitRollupMode> for SplitRollupMode {
96 fn from(value: proto::SplitRollupMode) -> Self {
97 match value {
98 proto::SplitRollupMode::Flat => Self::Flat,
99 proto::SplitRollupMode::Rollup => Self::Rollup,
100 }
101 }
102}
103
104impl From<SplitRollupMode> for proto::SplitRollupMode {
105 fn from(value: SplitRollupMode) -> Self {
106 match value {
107 SplitRollupMode::Flat => proto::SplitRollupMode::Flat,
108 SplitRollupMode::Rollup => proto::SplitRollupMode::Rollup,
109 }
110 }
111}
112
113#[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize, TS)]
114#[serde(deny_unknown_fields)]
115pub struct ViewConfig {
116 #[serde(default)]
117 pub group_by: Vec<String>,
118
119 #[serde(default)]
120 pub split_by: Vec<String>,
121
122 #[serde(default)]
123 pub sort: Vec<Sort>,
124
125 #[serde(default)]
126 pub filter: Vec<Filter>,
127
128 #[serde(default)]
130 pub group_rollup_mode: GroupRollupMode,
131
132 #[serde(default)]
133 pub split_rollup_mode: SplitRollupMode,
134
135 #[serde(skip_serializing_if = "is_default_value")]
136 #[serde(default)]
137 pub filter_op: FilterReducer,
138
139 #[serde(default)]
140 pub expressions: Expressions,
141
142 #[serde(default)]
143 #[serde(skip_serializing_if = "is_default_value")]
144 pub windows: Windows,
145
146 #[serde(default)]
147 pub columns: Vec<Option<String>>,
148
149 #[serde(default)]
150 pub aggregates: HashMap<String, Aggregate>,
151
152 #[serde(skip_serializing_if = "Option::is_none")]
153 #[serde(default)]
154 pub group_by_depth: Option<u32>,
155}
156
157fn is_default_value<A: Default + PartialEq>(value: &A) -> bool {
158 value == &A::default()
159}
160
161#[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize, TS)]
162#[serde(deny_unknown_fields)]
163pub struct ViewConfigUpdate {
164 #[serde(skip_serializing_if = "Option::is_none")]
177 #[serde(default)]
178 #[ts(optional)]
179 pub group_by: Option<Vec<String>>,
180
181 #[serde(skip_serializing_if = "Option::is_none")]
189 #[serde(default)]
190 #[ts(optional)]
191 pub split_by: Option<Vec<String>>,
192
193 #[serde(skip_serializing_if = "Option::is_none")]
199 #[serde(default)]
200 #[ts(optional)]
201 pub columns: Option<Vec<Option<String>>>,
202
203 #[serde(skip_serializing_if = "Option::is_none")]
214 #[serde(default)]
215 #[ts(optional)]
216 pub filter: Option<Vec<Filter>>,
217
218 #[serde(skip_serializing_if = "Option::is_none")]
233 #[serde(default)]
234 #[ts(optional)]
235 pub sort: Option<Vec<Sort>>,
236
237 #[serde(skip_serializing_if = "Option::is_none")]
243 #[serde(default)]
244 #[ts(optional)]
245 pub expressions: Option<Expressions>,
246
247 #[serde(skip_serializing_if = "Option::is_none")]
253 #[serde(default)]
254 #[ts(optional)]
255 pub windows: Option<Windows>,
256
257 #[serde(skip_serializing_if = "Option::is_none")]
276 #[serde(default)]
277 #[ts(optional)]
278 pub aggregates: Option<HashMap<String, Aggregate>>,
279
280 #[serde(skip_serializing)]
281 #[serde(default)]
282 #[ts(optional)]
283 pub group_by_depth: Option<u32>,
284
285 #[serde(skip_serializing_if = "Option::is_none")]
286 #[serde(default)]
287 #[ts(optional)]
288 pub filter_op: Option<FilterReducer>,
289
290 #[serde(skip_serializing_if = "Option::is_none")]
291 #[serde(default)]
292 #[ts(optional)]
293 pub group_rollup_mode: Option<GroupRollupMode>,
294
295 #[serde(skip_serializing_if = "Option::is_none")]
296 #[serde(default)]
297 #[ts(optional)]
298 pub split_rollup_mode: Option<SplitRollupMode>,
299}
300
301impl From<ViewConfigUpdate> for proto::ViewConfig {
302 fn from(value: ViewConfigUpdate) -> Self {
303 proto::ViewConfig {
304 group_by: value.group_by.unwrap_or_default(),
305 split_by: value.split_by.unwrap_or_default(),
306 columns: value.columns.map(|x| proto::ColumnsUpdate {
307 opt_columns: Some(columns_update::OptColumns::Columns(
308 proto::columns_update::Columns {
309 columns: x.into_iter().flatten().collect(),
310 },
311 )),
312 }),
313 filter: value
314 .filter
315 .unwrap_or_default()
316 .into_iter()
317 .map(|x| x.into())
318 .collect(),
319 filter_op: value
320 .filter_op
321 .map(proto::view_config::FilterReducer::from)
322 .unwrap_or_default() as i32,
323 sort: value
324 .sort
325 .unwrap_or_default()
326 .into_iter()
327 .map(|x| x.into())
328 .collect(),
329 expressions: value.expressions.unwrap_or_default().0,
330 windows: value
331 .windows
332 .unwrap_or_default()
333 .0
334 .into_iter()
335 .map(|(k, v)| (k, v.into()))
336 .collect(),
337 aggregates: value
338 .aggregates
339 .unwrap_or_default()
340 .into_iter()
341 .map(|(x, y)| (x, y.into()))
342 .collect(),
343 group_by_depth: value.group_by_depth,
344 group_rollup_mode: value
345 .group_rollup_mode
346 .map(|x| proto::GroupRollupMode::from(x).into()),
347 split_rollup_mode: value
348 .split_rollup_mode
349 .map(|x| proto::SplitRollupMode::from(x).into()),
350 }
351 }
352}
353
354impl From<FilterReducer> for proto::view_config::FilterReducer {
355 fn from(value: FilterReducer) -> Self {
356 match value {
357 FilterReducer::And => proto::view_config::FilterReducer::And,
358 FilterReducer::Or => proto::view_config::FilterReducer::Or,
359 }
360 }
361}
362
363impl From<proto::view_config::FilterReducer> for FilterReducer {
364 fn from(value: proto::view_config::FilterReducer) -> Self {
365 match value {
366 proto::view_config::FilterReducer::And => FilterReducer::And,
367 proto::view_config::FilterReducer::Or => FilterReducer::Or,
368 }
369 }
370}
371
372impl From<ViewConfig> for ViewConfigUpdate {
373 fn from(value: ViewConfig) -> Self {
374 ViewConfigUpdate {
375 group_by: Some(value.group_by),
376 split_by: Some(value.split_by),
377 columns: Some(value.columns),
378 filter: Some(value.filter),
379 filter_op: Some(value.filter_op),
380 sort: Some(value.sort),
381 expressions: Some(value.expressions),
382 windows: Some(value.windows),
383 aggregates: Some(value.aggregates),
384 group_by_depth: value.group_by_depth,
385 group_rollup_mode: Some(value.group_rollup_mode),
386 split_rollup_mode: Some(value.split_rollup_mode),
387 }
388 }
389}
390
391impl From<proto::ViewConfig> for ViewConfig {
392 fn from(value: proto::ViewConfig) -> Self {
393 ViewConfig {
394 group_by: value.group_by,
395 split_by: value.split_by,
396 columns: match value.columns.unwrap_or_default().opt_columns {
397 Some(columns_update::OptColumns::Columns(x)) => {
398 x.columns.into_iter().map(Some).collect()
399 },
400 _ => {
401 vec![]
402 },
403 },
404 filter: value.filter.into_iter().map(|x| x.into()).collect(),
405 filter_op: proto::view_config::FilterReducer::try_from(value.filter_op)
406 .unwrap_or_default()
407 .into(),
408 sort: value.sort.into_iter().map(|x| x.into()).collect(),
409 expressions: Expressions(value.expressions),
410 windows: Windows(
411 value
412 .windows
413 .into_iter()
414 .map(|(k, v)| (k, v.into()))
415 .collect(),
416 ),
417 aggregates: value
418 .aggregates
419 .into_iter()
420 .map(|(x, y)| (x, y.into()))
421 .collect(),
422 group_by_depth: value.group_by_depth,
423 group_rollup_mode: value
424 .group_rollup_mode
425 .map(proto::GroupRollupMode::try_from)
426 .and_then(|x| x.ok())
427 .map(|x| x.into())
428 .unwrap_or_default(),
429 split_rollup_mode: value
430 .split_rollup_mode
431 .map(proto::SplitRollupMode::try_from)
432 .and_then(|x| x.ok())
433 .map(|x| x.into())
434 .unwrap_or_default(),
435 }
436 }
437}
438
439impl From<ViewConfigUpdate> for ViewConfig {
440 fn from(value: ViewConfigUpdate) -> Self {
441 ViewConfig {
442 group_by: value.group_by.unwrap_or_default(),
443 split_by: value.split_by.unwrap_or_default(),
444 columns: value.columns.unwrap_or_default(),
445 filter: value.filter.unwrap_or_default(),
446 filter_op: value.filter_op.unwrap_or_default(),
447 sort: value.sort.unwrap_or_default(),
448 expressions: value.expressions.unwrap_or_default(),
449 windows: value.windows.unwrap_or_default(),
450 aggregates: value.aggregates.unwrap_or_default(),
451 group_by_depth: value.group_by_depth,
452 group_rollup_mode: value.group_rollup_mode.unwrap_or_default(),
453 split_rollup_mode: value.split_rollup_mode.unwrap_or_default(),
454 }
455 }
456}
457
458impl From<proto::ViewConfig> for ViewConfigUpdate {
459 fn from(value: proto::ViewConfig) -> Self {
460 ViewConfigUpdate {
461 group_by: Some(value.group_by),
462 split_by: Some(value.split_by),
463 columns: match value.columns.unwrap_or_default().opt_columns {
464 Some(columns_update::OptColumns::Columns(x)) => {
465 Some(x.columns.into_iter().map(Some).collect())
466 },
467 _ => None,
468 },
469 filter: Some(value.filter.into_iter().map(|x| x.into()).collect()),
470 filter_op: Some(
471 proto::view_config::FilterReducer::try_from(value.filter_op)
472 .unwrap_or_default()
473 .into(),
474 ),
475 sort: Some(value.sort.into_iter().map(|x| x.into()).collect()),
476 expressions: Some(Expressions(value.expressions)),
477 windows: Some(Windows(
478 value
479 .windows
480 .into_iter()
481 .map(|(k, v)| (k, v.into()))
482 .collect(),
483 )),
484 aggregates: Some(
485 value
486 .aggregates
487 .into_iter()
488 .map(|(x, y)| (x, y.into()))
489 .collect(),
490 ),
491 group_by_depth: value.group_by_depth,
492 group_rollup_mode: value
493 .group_rollup_mode
494 .and_then(|x| proto::GroupRollupMode::try_from(x).ok())
495 .map(|x| x.into()),
496 split_rollup_mode: value
497 .split_rollup_mode
498 .and_then(|x| proto::SplitRollupMode::try_from(x).ok())
499 .map(|x| x.into()),
500 }
501 }
502}
503
504impl ViewConfig {
505 fn _apply<T>(field: &mut T, update: Option<T>) -> bool {
506 match update {
507 None => false,
508 Some(update) => {
509 *field = update;
510 true
511 },
512 }
513 }
514
515 pub fn reset(&mut self, reset_expressions: bool) {
516 let mut config = Self::default();
517 if !reset_expressions {
518 config.expressions = self.expressions.clone();
519 }
520 std::mem::swap(self, &mut config);
521 }
522
523 pub fn apply_update(&mut self, mut update: ViewConfigUpdate) -> bool {
526 let mut changed = false;
527 if ((self.group_rollup_mode == GroupRollupMode::Total
528 && update.group_rollup_mode.is_none())
529 || update.group_rollup_mode == Some(GroupRollupMode::Total))
530 && update
531 .group_by
532 .as_ref()
533 .map(|x| !x.is_empty())
534 .unwrap_or_default()
535 {
536 tracing::info!("`total` incompatible with `group_by`");
537 changed = true;
538 update.group_rollup_mode = Some(GroupRollupMode::Rollup);
539 }
540
541 if update.group_rollup_mode == Some(GroupRollupMode::Total) && !self.group_by.is_empty() {
542 tracing::info!("`group_by` incompatible with `total`");
543 changed = true;
544 update.group_by = Some(vec![]);
545 }
546
547 changed = Self::_apply(&mut self.group_by, update.group_by) || changed;
548 changed = Self::_apply(&mut self.split_by, update.split_by) || changed;
549 changed = Self::_apply(&mut self.columns, update.columns) || changed;
550 changed = Self::_apply(&mut self.filter, update.filter) || changed;
551 changed = Self::_apply(&mut self.sort, update.sort) || changed;
552 changed = Self::_apply(&mut self.aggregates, update.aggregates) || changed;
553 changed = Self::_apply(&mut self.expressions, update.expressions) || changed;
554 changed = Self::_apply(&mut self.windows, update.windows) || changed;
555 changed = Self::_apply(&mut self.group_rollup_mode, update.group_rollup_mode) || changed;
556 changed = Self::_apply(&mut self.split_rollup_mode, update.split_rollup_mode) || changed;
557 if self.group_rollup_mode == GroupRollupMode::Total && !self.group_by.is_empty() {
558 tracing::info!("`total` incompatible with `group_by`");
559 changed = true;
560 self.group_by = vec![];
561 }
562
563 changed
564 }
565
566 pub fn is_aggregated(&self) -> bool {
567 !self.group_by.is_empty() || self.group_rollup_mode == GroupRollupMode::Total
568 }
569
570 pub fn is_column_expression_in_use(&self, name: &str) -> bool {
571 let name = name.to_owned();
572 self.group_by.contains(&name)
573 || self.split_by.contains(&name)
574 || self.sort.iter().any(|x| x.0 == name)
575 || self.filter.iter().any(|x| x.column() == name)
576 || self.columns.contains(&Some(name))
577 }
578
579 pub fn is_equivalent(&self, other: &Self) -> bool {
584 let _self = self.clone();
585 let _self = ViewConfig {
586 columns: _self.columns.into_iter().filter(|x| x.is_some()).collect(),
587 .._self
588 };
589
590 let _other = other.clone();
591 let _other = ViewConfig {
592 columns: _other.columns.into_iter().filter(|x| x.is_some()).collect(),
593 ..other.clone()
594 };
595
596 _self == _other
597 }
598}