use std::fmt::Display;
use crate::{
query::{
expression::{Expression, PathExpression},
parser::{Column, Parser, AGGREGATION_AVG, FN_CLOSE, FN_OPEN},
QueryResult,
},
Any, Dapt, Number, Path,
};
use super::{math::DivideAggregation, Aggregation, CountAggregation, SumAggregation};
#[derive(Clone)]
pub struct AvgAggregation {
expr: Box<dyn Expression>,
sum_count: Option<(f64, usize)>,
}
impl AvgAggregation {
pub fn from_parser(parser: &mut Parser) -> QueryResult<AvgAggregation> {
parser.consume_token(AGGREGATION_AVG)?;
parser.consume_token(FN_OPEN)?;
let expr = parser.parse_expression()?;
parser.consume_token(FN_CLOSE)?;
Ok(AvgAggregation {
expr,
sum_count: None,
})
}
}
impl Display for AvgAggregation {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{AGGREGATION_AVG}({})", self.expr)
}
}
impl Aggregation for AvgAggregation {
fn process<'a>(&'a mut self, d: &Dapt) {
let value = match self.expr.evaluate(d) {
Some(v) => v,
None => return,
};
let value: f64 = match Number::try_from(value) {
Ok(v) => v.into(),
Err(_) => return,
};
self.sum_count = match self.sum_count {
Some((sum, count)) => Some((sum + value, count + 1)),
None => Some((value, 1)),
};
}
fn result<'a>(&'a mut self) -> Option<Any<'a>> {
let (sum, count) = self.sum_count.take()?;
let v = Some(Any::F64(sum / count as f64));
v
}
fn composable(&self, path: &Path) -> (Vec<Column>, Box<dyn Aggregation>) {
let mut sum_path = path.clone();
sum_path.append_key("sum");
let composite_sum = Column::new(
Box::new(SumAggregation::new(self.expr.clone())),
sum_path.clone(),
);
let mut count_path = path.clone();
count_path.append_key("count");
let composite_count = Column::new(
Box::new(CountAggregation::new(Some(self.expr.clone()))),
count_path.clone(),
);
let combine = Box::new(DivideAggregation::new(
Box::new(SumAggregation::new(Box::new(PathExpression::from(
sum_path,
)))),
Box::new(SumAggregation::new(Box::new(PathExpression::from(
count_path,
)))),
));
(vec![composite_sum, composite_count], combine)
}
}