Skip to main content

cratestack_sqlx/query/read/
aggregate.rs

1//! `Aggregate` entry point — factory that hands out per-op builders.
2//! Like `find_many`, aggregates are filtered through the model's read
3//! policy AND the soft-delete column so counts/sums describe rows the
4//! caller would be allowed to retrieve.
5
6use cratestack_sql::IntoColumnName;
7
8use crate::{ModelDescriptor, SqlxRuntime};
9
10use super::aggregate_column::AggregateColumn;
11use super::aggregate_count::AggregateCount;
12
13#[derive(Debug, Clone, Copy)]
14pub(super) enum AggregateOp {
15    Sum,
16    Avg,
17    Min,
18    Max,
19}
20
21impl AggregateOp {
22    pub(super) fn function_name(self) -> &'static str {
23        match self {
24            Self::Sum => "SUM",
25            Self::Avg => "AVG",
26            Self::Min => "MIN",
27            Self::Max => "MAX",
28        }
29    }
30}
31
32#[derive(Debug, Clone)]
33pub struct Aggregate<'a, M: 'static, PK: 'static> {
34    pub(crate) runtime: &'a SqlxRuntime,
35    pub(crate) descriptor: &'static ModelDescriptor<M, PK>,
36}
37
38impl<'a, M: 'static, PK: 'static> Aggregate<'a, M, PK> {
39    /// `COUNT(*)` over the matching rows. Always returns `i64`; empty
40    /// matches yield 0 rather than NULL.
41    pub fn count(self) -> AggregateCount<'a, M, PK> {
42        AggregateCount::new(self.runtime, self.descriptor)
43    }
44
45    /// `SUM(<col>)`. Returns `Option<T>` — `None` when no rows match.
46    pub fn sum<C: IntoColumnName>(self, column: C) -> AggregateColumn<'a, M, PK> {
47        AggregateColumn::new(self.runtime, self.descriptor, AggregateOp::Sum, column)
48    }
49
50    /// `AVG(<col>)`. Returns `Option<T>` — `None` when no rows match.
51    pub fn avg<C: IntoColumnName>(self, column: C) -> AggregateColumn<'a, M, PK> {
52        AggregateColumn::new(self.runtime, self.descriptor, AggregateOp::Avg, column)
53    }
54
55    /// `MIN(<col>)`. Returns `Option<T>` — `None` when no rows match.
56    pub fn min<C: IntoColumnName>(self, column: C) -> AggregateColumn<'a, M, PK> {
57        AggregateColumn::new(self.runtime, self.descriptor, AggregateOp::Min, column)
58    }
59
60    /// `MAX(<col>)`. Returns `Option<T>` — `None` when no rows match.
61    pub fn max<C: IntoColumnName>(self, column: C) -> AggregateColumn<'a, M, PK> {
62        AggregateColumn::new(self.runtime, self.descriptor, AggregateOp::Max, column)
63    }
64}