fsqlite_func/
aggregate.rs1#![allow(clippy::unnecessary_literal_bound)]
12
13use std::any::Any;
14
15use fsqlite_error::Result;
16use fsqlite_types::SqliteValue;
17
18use crate::FunctionArity;
19
20pub trait AggregateFunction: Send + Sync {
36 type State: Send;
38
39 fn initial_state(&self) -> Self::State;
41
42 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> Result<()>;
44
45 fn finalize(&self, state: Self::State) -> Result<SqliteValue>;
47
48 fn num_args(&self) -> i32;
50
51 fn min_args(&self) -> i32 {
56 0
57 }
58
59 fn max_args(&self) -> Option<i32> {
64 None
65 }
66
67 fn arity(&self) -> FunctionArity {
73 let declared = self.num_args();
74 FunctionArity::from_declared_args(declared, || (self.min_args(), self.max_args()))
75 }
76
77 fn name(&self) -> &str;
79}
80
81pub struct AggregateAdapter<F> {
87 inner: F,
88}
89
90impl<F> AggregateAdapter<F> {
91 pub const fn new(inner: F) -> Self {
93 Self { inner }
94 }
95}
96
97impl<F> AggregateFunction for AggregateAdapter<F>
98where
99 F: AggregateFunction,
100 F::State: 'static,
101{
102 type State = Box<dyn Any + Send>;
103
104 fn initial_state(&self) -> Self::State {
105 Box::new(self.inner.initial_state())
106 }
107
108 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> Result<()> {
109 let concrete = state
110 .downcast_mut::<F::State>()
111 .expect("aggregate state type mismatch");
112 self.inner.step(concrete, args)
113 }
114
115 fn finalize(&self, state: Self::State) -> Result<SqliteValue> {
116 let concrete = *state
117 .downcast::<F::State>()
118 .expect("aggregate state type mismatch");
119 self.inner.finalize(concrete)
120 }
121
122 fn num_args(&self) -> i32 {
123 self.inner.num_args()
124 }
125
126 fn min_args(&self) -> i32 {
127 self.inner.min_args()
128 }
129
130 fn max_args(&self) -> Option<i32> {
131 self.inner.max_args()
132 }
133
134 fn arity(&self) -> FunctionArity {
135 self.inner.arity()
136 }
137
138 fn name(&self) -> &str {
139 self.inner.name()
140 }
141}
142
143#[cfg(test)]
144mod tests {
145 use std::sync::Arc;
146
147 use super::*;
148
149 struct SumAgg;
152
153 impl AggregateFunction for SumAgg {
154 type State = i64;
155
156 fn initial_state(&self) -> i64 {
157 0
158 }
159
160 fn step(&self, state: &mut i64, args: &[SqliteValue]) -> Result<()> {
161 *state += args[0].to_integer();
162 Ok(())
163 }
164
165 fn finalize(&self, state: i64) -> Result<SqliteValue> {
166 Ok(SqliteValue::Integer(state))
167 }
168
169 fn num_args(&self) -> i32 {
170 1
171 }
172
173 fn name(&self) -> &str {
174 "sum"
175 }
176 }
177
178 #[test]
179 fn test_aggregate_initial_state() {
180 let agg = SumAgg;
181 assert_eq!(agg.initial_state(), 0);
182 }
183
184 #[test]
185 fn test_aggregate_step_and_finalize() {
186 let agg = SumAgg;
187 let mut state = agg.initial_state();
188
189 agg.step(&mut state, &[SqliteValue::Integer(10)]).unwrap();
190 agg.step(&mut state, &[SqliteValue::Integer(20)]).unwrap();
191 agg.step(&mut state, &[SqliteValue::Integer(12)]).unwrap();
192
193 let result = agg.finalize(state).unwrap();
194 assert_eq!(result, SqliteValue::Integer(42));
195 }
196
197 #[test]
198 fn test_aggregate_type_erasure_adapter() {
199 let adapted: AggregateAdapter<SumAgg> = AggregateAdapter::new(SumAgg);
200 let erased: Arc<dyn AggregateFunction<State = Box<dyn Any + Send>>> = Arc::new(adapted);
201
202 let mut state = erased.initial_state();
203 erased
204 .step(&mut state, &[SqliteValue::Integer(10)])
205 .unwrap();
206 erased
207 .step(&mut state, &[SqliteValue::Integer(32)])
208 .unwrap();
209
210 let result = erased.finalize(state).unwrap();
211 assert_eq!(result, SqliteValue::Integer(42));
212
213 let e2 = Arc::clone(&erased);
215 assert_eq!(e2.name(), "sum");
216 }
217}