Skip to main content

qraft_core/
aggregate.rs

1//! Aggregate expression helpers such as `count`, `sum`, and `avg`.
2
3use crate::{
4    alias::Aliased,
5    expression::Expression,
6    lower::LowerCtx,
7    query::{LowerHaving, LowerProject, Star},
8};
9
10/// Lowers a value into an aggregate argument list.
11pub trait LowerAggregate {
12    fn lower_aggregate(self, ctx: &mut LowerCtx);
13}
14
15impl<E> LowerAggregate for E
16where
17    E: Expression,
18{
19    fn lower_aggregate(self, ctx: &mut LowerCtx) {
20        let _ = self.lower(ctx);
21    }
22}
23
24/// Lowers a value into the argument list accepted by `count`.
25pub trait LowerCount {
26    fn lower_count(self, ctx: &mut LowerCtx);
27}
28
29impl<E> LowerCount for E
30where
31    E: LowerAggregate,
32{
33    fn lower_count(self, ctx: &mut LowerCtx) {
34        E::lower_aggregate(self, ctx);
35    }
36}
37
38impl LowerCount for Star {
39    fn lower_count(self, ctx: &mut LowerCtx) {
40        let _ = ctx.lower_star();
41    }
42}
43
44/// A typed `count(...)` expression.
45#[derive(Debug, Clone, Copy)]
46pub struct Count<E> {
47    inner: E,
48}
49
50impl<E: LowerCount> Count<E> {
51    fn lower(self, ctx: &mut LowerCtx) -> usize {
52        self.inner.lower_count(ctx);
53        ctx.lower_fn("count", 1)
54    }
55}
56
57/// A single-argument aggregate function such as `max` or `avg`.
58#[derive(Debug, Clone, Copy)]
59pub struct Aggregate<E> {
60    inner: E,
61    name: &'static str,
62}
63
64impl<E: LowerAggregate> Aggregate<E> {
65    fn lower(self, ctx: &mut LowerCtx) -> usize {
66        self.inner.lower_aggregate(ctx);
67        ctx.lower_fn(self.name, 1)
68    }
69}
70
71/// Builds a `count(...)` expression.
72pub fn count<E>(e: E) -> Count<E>
73where
74    E: LowerCount,
75{
76    Count { inner: e }
77}
78
79/// Builds a `max(...)` expression.
80pub fn max<E>(e: E) -> Aggregate<E>
81where
82    E: LowerAggregate,
83{
84    Aggregate {
85        inner: e,
86        name: "max",
87    }
88}
89
90/// Builds a `min(...)` expression.
91pub fn min<E>(e: E) -> Aggregate<E>
92where
93    E: LowerAggregate,
94{
95    Aggregate {
96        inner: e,
97        name: "min",
98    }
99}
100
101/// Builds a `sum(...)` expression.
102pub fn sum<E>(e: E) -> Aggregate<E>
103where
104    E: LowerAggregate,
105{
106    Aggregate {
107        inner: e,
108        name: "sum",
109    }
110}
111
112/// Builds an `avg(...)` expression.
113pub fn avg<E>(e: E) -> Aggregate<E>
114where
115    E: LowerAggregate,
116{
117    Aggregate {
118        inner: e,
119        name: "avg",
120    }
121}
122
123impl<E: LowerCount> LowerProject for Count<E> {
124    fn lower_project(self, ctx: &mut LowerCtx) {
125        self.lower(ctx);
126    }
127}
128
129impl<E: LowerCount> LowerHaving for Count<E> {
130    fn lower_having(self, ctx: &mut LowerCtx) {
131        self.lower(ctx);
132    }
133}
134
135impl<E: LowerCount> LowerProject for Aliased<Count<E>> {
136    fn lower_project(self, ctx: &mut LowerCtx) {
137        let inner = self.inner.lower(ctx);
138        let _ = ctx.lower_alias(self.alias, inner);
139    }
140}
141
142impl<E: LowerAggregate> LowerProject for Aliased<Aggregate<E>> {
143    fn lower_project(self, ctx: &mut LowerCtx) {
144        let inner = self.inner.lower(ctx);
145        let _ = ctx.lower_alias(self.alias, inner);
146    }
147}
148
149impl<E: LowerAggregate> LowerProject for Aggregate<E> {
150    fn lower_project(self, ctx: &mut LowerCtx) {
151        self.lower(ctx);
152    }
153}