use std::collections::{HashMap, HashSet};
use std::iter::IntoIterator;
use std::ops::{Deref, DerefMut};
use crate::config::*;
use crate::js::perspective::*;
use crate::utils::*;
use crate::*;
struct SessionViewExpressionMetadata {
schema: HashMap<String, Type>,
alias: HashMap<String, String>,
edited: HashMap<String, String>,
}
#[derive(Default)]
pub struct SessionMetadata(Option<SessionMetadataState>);
impl Deref for SessionMetadata {
type Target = Option<SessionMetadataState>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl DerefMut for SessionMetadata {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
#[derive(Default)]
pub struct SessionMetadataState {
column_names: Vec<String>,
table_schema: HashMap<String, Type>,
edit_port: f64,
view_schema: Option<HashMap<String, Type>>,
expr_meta: Option<SessionViewExpressionMetadata>,
}
impl SessionMetadata {
pub(super) async fn from_table(table: &JsPerspectiveTable) -> ApiResult<Self> {
let column_names = {
let columns = table.columns().await?;
if columns.length() > 0 {
(0..columns.length())
.map(|i| columns.get(i).as_string().unwrap())
.collect::<Vec<String>>()
} else {
vec![]
}
};
let table_schema = table.schema().await?.into_serde_ext()?;
let edit_port = table.make_port().await?;
Ok(Self(Some(SessionMetadataState {
column_names,
table_schema,
edit_port,
..SessionMetadataState::default()
})))
}
pub(super) fn update_view_schema(
&mut self,
view_schema: &JsPerspectiveViewSchema,
) -> ApiResult<()> {
let view_schema = view_schema.into_serde_ext()?;
self.as_mut().unwrap().view_schema = Some(view_schema);
Ok(())
}
pub(super) fn update_expressions(
&mut self,
valid_recs: &JsPerspectiveValidatedExpressions,
) -> ApiResult<HashSet<String>> {
if js_sys::Object::keys(&valid_recs.errors()).length() > 0 {
return Err("Expressions invalid".into());
}
let expression_alias: HashMap<String, String> =
valid_recs.expression_alias().into_serde_ext()?;
let expression_schema: HashMap<String, Type> =
valid_recs.expression_schema().into_serde_ext()?;
let expression_names = expression_schema.keys().cloned().collect::<HashSet<_>>();
let mut edited = self
.as_mut()
.unwrap()
.expr_meta
.take()
.map(|x| x.edited)
.unwrap_or_default();
edited.retain(|k, _| expression_alias.get(k).is_some());
self.as_mut().unwrap().expr_meta = Some(SessionViewExpressionMetadata {
schema: expression_schema,
alias: expression_alias,
edited,
});
Ok(expression_names)
}
pub fn get_expression_columns(&self) -> impl Iterator<Item = &'_ String> {
maybe!(Some(self.as_ref()?.expr_meta.as_ref()?.schema.keys()))
.into_iter()
.flatten()
}
pub fn get_expression_by_alias(&self, alias: &str) -> Option<String> {
maybe!(self.as_ref()?.expr_meta.as_ref()?.alias.get(alias)).cloned()
}
pub fn get_edit_by_alias(&self, alias: &str) -> Option<String> {
maybe!(self
.as_ref()?
.expr_meta
.as_ref()?
.edited
.get(alias)
.cloned())
}
pub fn set_edit_by_alias(&mut self, alias: &str, edit: String) {
drop(maybe!(self
.as_mut()?
.expr_meta
.as_mut()?
.edited
.insert(alias.to_owned(), edit)))
}
pub fn clear_edit_by_alias(&mut self, alias: &str) {
drop(maybe!(self
.as_mut()?
.expr_meta
.as_mut()?
.edited
.remove(alias)))
}
pub fn get_table_columns(&self) -> Option<&'_ Vec<String>> {
self.as_ref().map(|meta| &meta.column_names)
}
pub fn is_column_expression(&self, name: &str) -> bool {
let is_expr = maybe!(Some(
self.as_ref()?.expr_meta.as_ref()?.schema.contains_key(name)
));
is_expr.unwrap_or_default()
}
pub fn get_edit_port(&self) -> Option<f64> {
self.as_ref().map(|meta| meta.edit_port)
}
pub fn get_column_table_type(&self, name: &str) -> Option<Type> {
maybe!({
let meta = self.as_ref()?;
meta.table_schema
.get(name)
.or_else(|| meta.expr_meta.as_ref()?.schema.get(name))
.cloned()
})
}
pub fn get_column_view_type(&self, name: &str) -> Option<Type> {
maybe!(self.as_ref()?.view_schema.as_ref()?.get(name)).cloned()
}
pub fn get_column_aggregates<'a>(
&'a self,
name: &str,
) -> Option<Box<dyn Iterator<Item = Aggregate> + 'a>> {
maybe!({
let coltype = self.get_column_table_type(name)?;
let aggregates = coltype.aggregates_iter();
Some(match coltype {
Type::Float | Type::Integer => {
let num_cols = self
.get_expression_columns()
.cloned()
.chain(self.get_table_columns()?.clone().into_iter())
.map(move |name| {
self.get_column_table_type(&name)
.map(|coltype| (name, coltype))
})
.collect::<Option<Vec<_>>>()?
.into_iter()
.filter(|(_, coltype)| *coltype == Type::Integer || *coltype == Type::Float)
.map(|(name, _)| {
Aggregate::MultiAggregate(MultiAggregate::WeightedMean, name)
});
Box::new(aggregates.chain(num_cols))
}
_ => aggregates,
})
})
}
}