#![deny(missing_docs)]
use std::marker::PhantomData;
use type_bridge_orm::match_request::{ReducedValue, Reduction};
use crate::__codegen::Model;
use crate::__codegen::NumericValued;
use crate::Result;
use crate::error::{Error, ModelValidationPhase};
use crate::query::{BindingKey, BoundField};
use crate::schema::Schema;
pub(crate) fn wrong_reduction_value() -> Error {
Error::model_validation(
ModelValidationPhase::Hydration,
"wrong_result_shape",
vec![],
"provider reduction value does not fit its requested reducer",
None,
)
}
pub struct Agg<S: Schema, Out> {
pub(crate) reduction: Reduction,
pub(crate) input: Option<(BindingKey, &'static str)>,
marker: PhantomData<fn() -> (S, Out)>,
}
impl<S: Schema, Out> Copy for Agg<S, Out> {}
impl<S: Schema, Out> Clone for Agg<S, Out> {
fn clone(&self) -> Self {
*self
}
}
impl<S: Schema, Out> Agg<S, Out> {
pub(crate) fn new(reduction: Reduction, input: Option<(BindingKey, &'static str)>) -> Self {
Self {
reduction,
input,
marker: PhantomData,
}
}
}
#[must_use]
pub fn count<S: Schema>() -> Agg<S, u64> {
Agg::new(Reduction::Count, None)
}
pub trait ReducedOutput: reduced_sealed::Sealed + Sized {
#[doc(hidden)]
fn decode(value: &ReducedValue) -> Result<Self>;
}
mod reduced_sealed {
pub trait Sealed {}
}
impl reduced_sealed::Sealed for u64 {}
impl ReducedOutput for u64 {
fn decode(value: &ReducedValue) -> Result<Self> {
match value {
ReducedValue::Count(value) => Ok(*value),
_ => Err(wrong_reduction_value()),
}
}
}
impl reduced_sealed::Sealed for i64 {}
impl ReducedOutput for i64 {
fn decode(value: &ReducedValue) -> Result<Self> {
match value {
ReducedValue::Long(Some(value)) => Ok(*value),
_ => Err(wrong_reduction_value()),
}
}
}
impl reduced_sealed::Sealed for f64 {}
impl ReducedOutput for f64 {
fn decode(value: &ReducedValue) -> Result<Self> {
match value {
ReducedValue::Double(Some(value)) => Ok(*value),
_ => Err(wrong_reduction_value()),
}
}
}
impl reduced_sealed::Sealed for Option<i64> {}
impl ReducedOutput for Option<i64> {
fn decode(value: &ReducedValue) -> Result<Self> {
match value {
ReducedValue::Long(value) => Ok(*value),
_ => Err(wrong_reduction_value()),
}
}
}
impl reduced_sealed::Sealed for Option<f64> {}
impl ReducedOutput for Option<f64> {
fn decode(value: &ReducedValue) -> Result<Self> {
match value {
ReducedValue::Double(value) => Ok(*value),
_ => Err(wrong_reduction_value()),
}
}
}
impl<S, Owner, V> BoundField<S, Owner, V>
where
S: Schema,
Owner: Model<Schema = S>,
V: NumericValued,
{
fn aggregate<Out>(self, reduction: Reduction) -> Agg<S, Out> {
let (key, owns_id_json) = self.reduction_input();
Agg::new(reduction, Some((key, owns_id_json)))
}
#[must_use]
pub fn sum(self) -> Agg<S, V::Reduced> {
self.aggregate(Reduction::Sum)
}
#[must_use]
pub fn min(self) -> Agg<S, Option<V::Reduced>> {
self.aggregate(Reduction::Min)
}
#[must_use]
pub fn max(self) -> Agg<S, Option<V::Reduced>> {
self.aggregate(Reduction::Max)
}
#[must_use]
pub fn mean(self) -> Agg<S, Option<f64>> {
self.aggregate(Reduction::Mean)
}
#[must_use]
pub fn median(self) -> Agg<S, Option<f64>> {
self.aggregate(Reduction::Median)
}
#[must_use]
pub fn stddev(self) -> Agg<S, Option<f64>> {
self.aggregate(Reduction::Std)
}
}
pub trait AggregateTuple<S: Schema>: tuple_sealed::Sealed<S> {
type Output;
#[doc(hidden)]
fn terms(&self) -> Vec<(Reduction, Option<(BindingKey, &'static str)>)>;
#[doc(hidden)]
fn decode(values: &[ReducedValue]) -> Result<Self::Output>;
}
mod tuple_sealed {
pub trait Sealed<S> {}
}
macro_rules! aggregate_tuple {
($(($name:ident, $out:ident, $index:tt)),+) => {
impl<S: Schema, $($out: ReducedOutput),+> tuple_sealed::Sealed<S>
for ($(Agg<S, $out>,)+)
{
}
impl<S: Schema, $($out: ReducedOutput),+> AggregateTuple<S> for ($(Agg<S, $out>,)+) {
type Output = ($($out,)+);
fn terms(&self) -> Vec<(Reduction, Option<(BindingKey, &'static str)>)> {
vec![$((self.$index.reduction, self.$index.input)),+]
}
fn decode(values: &[ReducedValue]) -> Result<Self::Output> {
let expected = [$(stringify!($name)),+].len();
if values.len() != expected {
return Err(wrong_reduction_value());
}
Ok(($($out::decode(&values[$index])?,)+))
}
}
};
}
aggregate_tuple!((a, A, 0));
aggregate_tuple!((a, A, 0), (b, B, 1));
aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2));
aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2), (d, D, 3));
aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2), (d, D, 3), (e, E, 4));
aggregate_tuple!(
(a, A, 0),
(b, B, 1),
(c, C, 2),
(d, D, 3),
(e, E, 4),
(f, F, 5)
);
aggregate_tuple!(
(a, A, 0),
(b, B, 1),
(c, C, 2),
(d, D, 3),
(e, E, 4),
(f, F, 5),
(g, G, 6)
);
aggregate_tuple!(
(a, A, 0),
(b, B, 1),
(c, C, 2),
(d, D, 3),
(e, E, 4),
(f, F, 5),
(g, G, 6),
(h, H, 7)
);