use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use super::aggregates::*;
use super::expressions::*;
use super::filters::*;
use super::sort::*;
use crate::proto;
use crate::proto::columns_update;
#[derive(Clone, Debug, Deserialize, Default, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ViewConfig {
#[serde(default)]
pub group_by: Vec<String>,
#[serde(default)]
pub split_by: Vec<String>,
#[serde(default)]
pub columns: Vec<Option<String>>,
#[serde(default)]
pub filter: Vec<Filter>,
#[serde(skip_serializing_if = "is_default_value")]
#[serde(default)]
pub filter_op: FilterReducer,
#[serde(default)]
pub sort: Vec<Sort>,
#[serde(default)]
pub expressions: Expressions,
#[serde(default)]
pub aggregates: HashMap<String, Aggregate>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
pub group_by_depth: Option<u32>,
}
fn is_default_value<A: Default + PartialEq>(value: &A) -> bool {
value == &A::default()
}
#[derive(Clone, Debug, Deserialize, Default, Serialize, TS)]
#[serde(deny_unknown_fields)]
pub struct ViewConfigUpdate {
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub group_by: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub split_by: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub columns: Option<Vec<Option<String>>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub filter: Option<Vec<Filter>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub filter_op: Option<FilterReducer>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub sort: Option<Vec<Sort>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub expressions: Option<Expressions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub aggregates: Option<HashMap<String, Aggregate>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[serde(default)]
#[ts(optional)]
pub group_by_depth: Option<u32>,
}
impl From<ViewConfigUpdate> for proto::ViewConfig {
fn from(value: ViewConfigUpdate) -> Self {
proto::ViewConfig {
group_by: value.group_by.unwrap_or_default(),
split_by: value.split_by.unwrap_or_default(),
columns: value.columns.map(|x| proto::ColumnsUpdate {
opt_columns: Some(columns_update::OptColumns::Columns(
proto::columns_update::Columns {
columns: x.into_iter().flatten().collect(),
},
)),
}),
filter: value
.filter
.unwrap_or_default()
.into_iter()
.map(|x| x.into())
.collect(),
filter_op: value
.filter_op
.map(proto::view_config::FilterReducer::from)
.unwrap_or_default() as i32,
sort: value
.sort
.unwrap_or_default()
.into_iter()
.map(|x| x.into())
.collect(),
expressions: value.expressions.unwrap_or_default().0,
aggregates: value
.aggregates
.unwrap_or_default()
.into_iter()
.map(|(x, y)| (x, y.into()))
.collect(),
group_by_depth: value.group_by_depth,
}
}
}
impl From<FilterReducer> for proto::view_config::FilterReducer {
fn from(value: FilterReducer) -> Self {
match value {
FilterReducer::And => proto::view_config::FilterReducer::And,
FilterReducer::Or => proto::view_config::FilterReducer::Or,
}
}
}
impl From<proto::view_config::FilterReducer> for FilterReducer {
fn from(value: proto::view_config::FilterReducer) -> Self {
match value {
proto::view_config::FilterReducer::And => FilterReducer::And,
proto::view_config::FilterReducer::Or => FilterReducer::Or,
}
}
}
impl From<ViewConfig> for ViewConfigUpdate {
fn from(value: ViewConfig) -> Self {
ViewConfigUpdate {
group_by: Some(value.group_by),
split_by: Some(value.split_by),
columns: Some(value.columns),
filter: Some(value.filter),
filter_op: Some(value.filter_op),
sort: Some(value.sort),
expressions: Some(value.expressions),
aggregates: Some(value.aggregates),
group_by_depth: value.group_by_depth,
}
}
}
impl From<proto::ViewConfig> for ViewConfig {
fn from(value: proto::ViewConfig) -> Self {
ViewConfig {
group_by: value.group_by,
split_by: value.split_by,
columns: match value.columns.unwrap().opt_columns {
Some(columns_update::OptColumns::Columns(x)) => {
x.columns.into_iter().map(Some).collect()
},
_ => {
panic!("Expected columns in ViewConfig")
},
},
filter: value.filter.into_iter().map(|x| x.into()).collect(),
filter_op: proto::view_config::FilterReducer::try_from(value.filter_op)
.unwrap_or_default()
.into(),
sort: value.sort.into_iter().map(|x| x.into()).collect(),
expressions: Expressions(value.expressions),
aggregates: value
.aggregates
.into_iter()
.map(|(x, y)| (x, y.into()))
.collect(),
group_by_depth: value.group_by_depth,
}
}
}
impl ViewConfig {
fn _apply<T>(field: &mut T, update: Option<T>) -> bool {
match update {
None => false,
Some(update) => {
*field = update;
true
},
}
}
pub fn reset(&mut self, reset_expressions: bool) {
let mut config = Self::default();
if !reset_expressions {
config.expressions = self.expressions.clone();
}
std::mem::swap(self, &mut config);
}
pub fn apply_update(&mut self, update: ViewConfigUpdate) -> bool {
let mut changed = false;
changed = Self::_apply(&mut self.group_by, update.group_by) || changed;
changed = Self::_apply(&mut self.split_by, update.split_by) || changed;
changed = Self::_apply(&mut self.columns, update.columns) || changed;
changed = Self::_apply(&mut self.filter, update.filter) || changed;
changed = Self::_apply(&mut self.sort, update.sort) || changed;
changed = Self::_apply(&mut self.aggregates, update.aggregates) || changed;
changed = Self::_apply(&mut self.expressions, update.expressions) || changed;
changed
}
pub fn is_aggregated(&self) -> bool {
!self.group_by.is_empty()
}
pub fn is_column_expression_in_use(&self, name: &str) -> bool {
let name = name.to_owned();
self.group_by.contains(&name)
|| self.split_by.contains(&name)
|| self.sort.iter().any(|x| x.0 == name)
|| self.filter.iter().any(|x| x.column() == name)
|| self.columns.contains(&Some(name))
}
}