1#![deny(missing_docs)]
2use std::marker::PhantomData;
10
11use type_bridge_orm::match_request::{ReducedValue, Reduction};
12
13use crate::__codegen::Model;
14use crate::__codegen::NumericValued;
15use crate::Result;
16use crate::error::{Error, ModelValidationPhase};
17use crate::query::{BindingKey, BoundField};
18use crate::schema::Schema;
19
20pub(crate) fn wrong_reduction_value() -> Error {
21 Error::model_validation(
22 ModelValidationPhase::Hydration,
23 "wrong_result_shape",
24 vec![],
25 "provider reduction value does not fit its requested reducer",
26 None,
27 )
28}
29
30pub struct Agg<S: Schema, Out> {
32 pub(crate) reduction: Reduction,
33 pub(crate) input: Option<(BindingKey, &'static str)>,
34 marker: PhantomData<fn() -> (S, Out)>,
35}
36
37impl<S: Schema, Out> Copy for Agg<S, Out> {}
38impl<S: Schema, Out> Clone for Agg<S, Out> {
39 fn clone(&self) -> Self {
40 *self
41 }
42}
43
44impl<S: Schema, Out> Agg<S, Out> {
45 pub(crate) fn new(reduction: Reduction, input: Option<(BindingKey, &'static str)>) -> Self {
46 Self {
47 reduction,
48 input,
49 marker: PhantomData,
50 }
51 }
52}
53
54#[must_use]
56pub fn count<S: Schema>() -> Agg<S, u64> {
57 Agg::new(Reduction::Count, None)
58}
59
60pub trait ReducedOutput: reduced_sealed::Sealed + Sized {
62 #[doc(hidden)]
63 fn decode(value: &ReducedValue) -> Result<Self>;
64}
65
66mod reduced_sealed {
67 pub trait Sealed {}
68}
69
70impl reduced_sealed::Sealed for u64 {}
71impl ReducedOutput for u64 {
72 fn decode(value: &ReducedValue) -> Result<Self> {
73 match value {
74 ReducedValue::Count(value) => Ok(*value),
75 _ => Err(wrong_reduction_value()),
76 }
77 }
78}
79
80impl reduced_sealed::Sealed for i64 {}
81impl ReducedOutput for i64 {
82 fn decode(value: &ReducedValue) -> Result<Self> {
83 match value {
84 ReducedValue::Long(Some(value)) => Ok(*value),
85 _ => Err(wrong_reduction_value()),
86 }
87 }
88}
89
90impl reduced_sealed::Sealed for f64 {}
91impl ReducedOutput for f64 {
92 fn decode(value: &ReducedValue) -> Result<Self> {
93 match value {
94 ReducedValue::Double(Some(value)) => Ok(*value),
95 _ => Err(wrong_reduction_value()),
96 }
97 }
98}
99
100impl reduced_sealed::Sealed for Option<i64> {}
101impl ReducedOutput for Option<i64> {
102 fn decode(value: &ReducedValue) -> Result<Self> {
103 match value {
104 ReducedValue::Long(value) => Ok(*value),
105 _ => Err(wrong_reduction_value()),
106 }
107 }
108}
109
110impl reduced_sealed::Sealed for Option<f64> {}
111impl ReducedOutput for Option<f64> {
112 fn decode(value: &ReducedValue) -> Result<Self> {
113 match value {
114 ReducedValue::Double(value) => Ok(*value),
115 _ => Err(wrong_reduction_value()),
116 }
117 }
118}
119
120impl<S, Owner, V> BoundField<S, Owner, V>
121where
122 S: Schema,
123 Owner: Model<Schema = S>,
124 V: NumericValued,
125{
126 fn aggregate<Out>(self, reduction: Reduction) -> Agg<S, Out> {
127 let (key, owns_id_json) = self.reduction_input();
128 Agg::new(reduction, Some((key, owns_id_json)))
129 }
130
131 #[must_use]
135 pub fn sum(self) -> Agg<S, V::Reduced> {
136 self.aggregate(Reduction::Sum)
137 }
138
139 #[must_use]
142 pub fn min(self) -> Agg<S, Option<V::Reduced>> {
143 self.aggregate(Reduction::Min)
144 }
145
146 #[must_use]
149 pub fn max(self) -> Agg<S, Option<V::Reduced>> {
150 self.aggregate(Reduction::Max)
151 }
152
153 #[must_use]
156 pub fn mean(self) -> Agg<S, Option<f64>> {
157 self.aggregate(Reduction::Mean)
158 }
159
160 #[must_use]
163 pub fn median(self) -> Agg<S, Option<f64>> {
164 self.aggregate(Reduction::Median)
165 }
166
167 #[must_use]
170 pub fn stddev(self) -> Agg<S, Option<f64>> {
171 self.aggregate(Reduction::Std)
172 }
173}
174
175pub trait AggregateTuple<S: Schema>: tuple_sealed::Sealed<S> {
177 type Output;
179 #[doc(hidden)]
180 fn terms(&self) -> Vec<(Reduction, Option<(BindingKey, &'static str)>)>;
181 #[doc(hidden)]
182 fn decode(values: &[ReducedValue]) -> Result<Self::Output>;
183}
184
185mod tuple_sealed {
186 pub trait Sealed<S> {}
187}
188
189macro_rules! aggregate_tuple {
190 ($(($name:ident, $out:ident, $index:tt)),+) => {
191 impl<S: Schema, $($out: ReducedOutput),+> tuple_sealed::Sealed<S>
192 for ($(Agg<S, $out>,)+)
193 {
194 }
195 impl<S: Schema, $($out: ReducedOutput),+> AggregateTuple<S> for ($(Agg<S, $out>,)+) {
196 type Output = ($($out,)+);
197 fn terms(&self) -> Vec<(Reduction, Option<(BindingKey, &'static str)>)> {
198 vec![$((self.$index.reduction, self.$index.input)),+]
199 }
200 fn decode(values: &[ReducedValue]) -> Result<Self::Output> {
201 let expected = [$(stringify!($name)),+].len();
202 if values.len() != expected {
203 return Err(wrong_reduction_value());
204 }
205 Ok(($($out::decode(&values[$index])?,)+))
206 }
207 }
208 };
209}
210
211aggregate_tuple!((a, A, 0));
212aggregate_tuple!((a, A, 0), (b, B, 1));
213aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2));
214aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2), (d, D, 3));
215aggregate_tuple!((a, A, 0), (b, B, 1), (c, C, 2), (d, D, 3), (e, E, 4));
216aggregate_tuple!(
217 (a, A, 0),
218 (b, B, 1),
219 (c, C, 2),
220 (d, D, 3),
221 (e, E, 4),
222 (f, F, 5)
223);
224aggregate_tuple!(
225 (a, A, 0),
226 (b, B, 1),
227 (c, C, 2),
228 (d, D, 3),
229 (e, E, 4),
230 (f, F, 5),
231 (g, G, 6)
232);
233aggregate_tuple!(
234 (a, A, 0),
235 (b, B, 1),
236 (c, C, 2),
237 (d, D, 3),
238 (e, E, 4),
239 (f, F, 5),
240 (g, G, 6),
241 (h, H, 7)
242);