use std::fmt::Display;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use crate::proto;
use crate::proto::view_config;
#[derive(Clone, Copy, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
#[serde()]
pub enum SingleAggregate {
#[serde(rename = "sum")]
Sum,
#[serde(rename = "sum abs")]
SumAbs,
#[serde(rename = "sum not null")]
SumNotNull,
#[serde(rename = "abs sum")]
AbsSum,
#[serde(rename = "pct sum parent")]
PctSumParent,
#[serde(rename = "pct sum grand total")]
PctSumGrandTotal,
#[serde(rename = "any")]
Any,
#[serde(rename = "unique")]
Unique,
#[serde(rename = "dominant")]
Dominant,
#[serde(rename = "median")]
Median,
#[serde(rename = "first by index")]
FirstByIndex,
#[serde(rename = "first")]
First,
#[serde(rename = "last by index")]
LastByIndex,
#[serde(rename = "last minus first")]
LastMinusFirst,
#[serde(rename = "last")]
Last,
#[serde(rename = "count")]
Count,
#[serde(rename = "distinct count")]
DistinctCount,
#[serde(rename = "avg")]
Avg,
#[serde(rename = "mean")]
Mean,
#[serde(rename = "join")]
Join,
#[serde(rename = "high")]
High,
#[serde(rename = "low")]
Low,
#[serde(rename = "max")]
Max,
#[serde(rename = "min")]
Min,
#[serde(rename = "high minus low")]
HighMinusLow,
#[serde(rename = "stddev")]
StdDev,
#[serde(rename = "var")]
Var,
}
impl Display for SingleAggregate {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
let term = match self {
Self::Sum => "sum",
Self::SumAbs => "sum abs",
Self::SumNotNull => "sum not null",
Self::AbsSum => "abs sum",
Self::PctSumParent => "pct sum parent",
Self::PctSumGrandTotal => "pct sum grand total",
Self::Any => "any",
Self::Unique => "unique",
Self::Dominant => "dominant",
Self::Median => "median",
Self::First => "first",
Self::FirstByIndex => "first by index",
Self::LastByIndex => "last by index",
Self::LastMinusFirst => "last minus first",
Self::Last => "last",
Self::Count => "count",
Self::DistinctCount => "distinct count",
Self::Avg => "avg",
Self::Mean => "mean",
Self::Join => "join",
Self::High => "high",
Self::Low => "low",
Self::HighMinusLow => "high minus low",
Self::StdDev => "stddev",
Self::Var => "var",
Self::Max => "max",
Self::Min => "min",
};
write!(fmt, "{}", term)
}
}
impl FromStr for SingleAggregate {
type Err = String;
fn from_str(value: &str) -> Result<Self, String> {
match value {
"sum" => Ok(Self::Sum),
"sum abs" => Ok(Self::SumAbs),
"sum not null" => Ok(Self::SumNotNull),
"abs sum" => Ok(Self::AbsSum),
"pct sum parent" => Ok(Self::PctSumParent),
"pct sum grand total" => Ok(Self::PctSumGrandTotal),
"any" => Ok(Self::Any),
"unique" => Ok(Self::Unique),
"dominant" => Ok(Self::Dominant),
"median" => Ok(Self::Median),
"first by index" => Ok(Self::FirstByIndex),
"first" => Ok(Self::First),
"last by index" => Ok(Self::LastByIndex),
"last minus first" => Ok(Self::LastMinusFirst),
"last" => Ok(Self::Last),
"count" => Ok(Self::Count),
"distinct count" => Ok(Self::DistinctCount),
"avg" => Ok(Self::Avg),
"mean" => Ok(Self::Mean),
"join" => Ok(Self::Join),
"high" => Ok(Self::High),
"low" => Ok(Self::Low),
"max" => Ok(Self::Max),
"min" => Ok(Self::Min),
"high minus low" => Ok(Self::HighMinusLow),
"stddev" => Ok(Self::StdDev),
"var" => Ok(Self::Var),
x => Err(format!("Unknown aggregate `{}`", x)),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
#[serde()]
pub enum MultiAggregate {
#[serde(rename = "weighted mean")]
WeightedMean,
}
impl Display for MultiAggregate {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MultiAggregate::WeightedMean => write!(f, "weighted mean"),
}
}
}
#[derive(Clone, Debug, Deserialize, Eq, Ord, PartialEq, PartialOrd, Serialize, TS)]
#[serde(untagged)]
pub enum Aggregate {
SingleAggregate(SingleAggregate),
MultiAggregate(MultiAggregate, String),
}
impl From<&'static str> for Aggregate {
fn from(value: &'static str) -> Self {
Self::from_str(value).expect("Unknown aggregate")
}
}
impl Display for Aggregate {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::result::Result<(), std::fmt::Error> {
match self {
Self::SingleAggregate(x) => write!(fmt, "{}", x)?,
Self::MultiAggregate(MultiAggregate::WeightedMean, x) => {
write!(fmt, "weighted mean by {}", x)?
},
};
Ok(())
}
}
impl FromStr for Aggregate {
type Err = String;
fn from_str(input: &str) -> Result<Self, String> {
Ok(
if let Some(stripped) = input.strip_prefix("weighted mean by ") {
Self::MultiAggregate(MultiAggregate::WeightedMean, stripped.to_owned())
} else {
Self::SingleAggregate(SingleAggregate::from_str(input)?)
},
)
}
}
const STRING_AGGREGATES: &[SingleAggregate] = &[
SingleAggregate::Any,
SingleAggregate::Count,
SingleAggregate::DistinctCount,
SingleAggregate::Dominant,
SingleAggregate::First,
SingleAggregate::Join,
SingleAggregate::Last,
SingleAggregate::LastByIndex,
SingleAggregate::Median,
SingleAggregate::Unique,
];
const NUMBER_AGGREGATES: &[SingleAggregate] = &[
SingleAggregate::AbsSum,
SingleAggregate::Any,
SingleAggregate::Avg,
SingleAggregate::Count,
SingleAggregate::DistinctCount,
SingleAggregate::Dominant,
SingleAggregate::First,
SingleAggregate::High,
SingleAggregate::Low,
SingleAggregate::Max,
SingleAggregate::Min,
SingleAggregate::HighMinusLow,
SingleAggregate::LastByIndex,
SingleAggregate::LastMinusFirst,
SingleAggregate::Last,
SingleAggregate::Mean,
SingleAggregate::Median,
SingleAggregate::PctSumParent,
SingleAggregate::PctSumGrandTotal,
SingleAggregate::StdDev,
SingleAggregate::Sum,
SingleAggregate::SumAbs,
SingleAggregate::SumNotNull,
SingleAggregate::Unique,
SingleAggregate::Var,
];
impl proto::ColumnType {
pub fn aggregates_iter(&self) -> Box<dyn Iterator<Item = Aggregate>> {
match self {
Self::Boolean | Self::Date | Self::Datetime | Self::String => Box::new(
STRING_AGGREGATES
.iter()
.map(|x| Aggregate::SingleAggregate(*x)),
),
Self::Integer | Self::Float => Box::new(
NUMBER_AGGREGATES
.iter()
.map(|x| Aggregate::SingleAggregate(*x)),
),
}
}
pub const fn default_aggregate(&self) -> Aggregate {
match self {
Self::Boolean | Self::Date | Self::Datetime | Self::String => {
Aggregate::SingleAggregate(SingleAggregate::Count)
},
Self::Integer | Self::Float => Aggregate::SingleAggregate(SingleAggregate::Sum),
}
}
}
impl From<Aggregate> for view_config::AggList {
fn from(value: Aggregate) -> Self {
view_config::AggList {
aggregations: match value {
Aggregate::SingleAggregate(x) => vec![format!("{}", x)],
Aggregate::MultiAggregate(x, y) => vec![format!("{}", x), format!("{}", y)],
},
}
}
}
impl From<view_config::AggList> for Aggregate {
fn from(value: view_config::AggList) -> Self {
Aggregate::SingleAggregate(
SingleAggregate::from_str(value.aggregations.first().unwrap()).unwrap(),
)
}
}