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 step_with_arg_subtypes(
53 &self,
54 state: &mut Self::State,
55 args: &[SqliteValue],
56 _arg_subtypes: &[u32],
57 ) -> Result<()> {
58 self.step(state, args)
59 }
60
61 fn finalize(&self, state: Self::State) -> Result<SqliteValue>;
63
64 fn num_args(&self) -> i32;
66
67 fn min_args(&self) -> i32 {
72 0
73 }
74
75 fn max_args(&self) -> Option<i32> {
80 None
81 }
82
83 fn arity(&self) -> FunctionArity {
89 let declared = self.num_args();
90 FunctionArity::from_declared_args(declared, || (self.min_args(), self.max_args()))
91 }
92
93 fn name(&self) -> &str;
95}
96
97pub struct AggregateAdapter<F> {
103 inner: F,
104}
105
106impl<F> AggregateAdapter<F> {
107 pub const fn new(inner: F) -> Self {
109 Self { inner }
110 }
111}
112
113impl<F> AggregateFunction for AggregateAdapter<F>
114where
115 F: AggregateFunction,
116 F::State: 'static,
117{
118 type State = Box<dyn Any + Send>;
119
120 fn initial_state(&self) -> Self::State {
121 Box::new(self.inner.initial_state())
122 }
123
124 fn step(&self, state: &mut Self::State, args: &[SqliteValue]) -> Result<()> {
125 let concrete = state
126 .downcast_mut::<F::State>()
127 .expect("aggregate state type mismatch");
128 self.inner.step(concrete, args)
129 }
130
131 fn step_with_arg_subtypes(
132 &self,
133 state: &mut Self::State,
134 args: &[SqliteValue],
135 arg_subtypes: &[u32],
136 ) -> Result<()> {
137 let concrete = state
138 .downcast_mut::<F::State>()
139 .expect("aggregate state type mismatch");
140 self.inner
141 .step_with_arg_subtypes(concrete, args, arg_subtypes)
142 }
143
144 fn finalize(&self, state: Self::State) -> Result<SqliteValue> {
145 let concrete = *state
146 .downcast::<F::State>()
147 .expect("aggregate state type mismatch");
148 self.inner.finalize(concrete)
149 }
150
151 fn num_args(&self) -> i32 {
152 self.inner.num_args()
153 }
154
155 fn min_args(&self) -> i32 {
156 self.inner.min_args()
157 }
158
159 fn max_args(&self) -> Option<i32> {
160 self.inner.max_args()
161 }
162
163 fn arity(&self) -> FunctionArity {
164 self.inner.arity()
165 }
166
167 fn name(&self) -> &str {
168 self.inner.name()
169 }
170}
171
172#[cfg(test)]
173mod tests {
174 use std::sync::Arc;
175
176 use super::*;
177
178 struct SumAgg;
181
182 impl AggregateFunction for SumAgg {
183 type State = i64;
184
185 fn initial_state(&self) -> i64 {
186 0
187 }
188
189 fn step(&self, state: &mut i64, args: &[SqliteValue]) -> Result<()> {
190 *state += args[0].to_integer();
191 Ok(())
192 }
193
194 fn finalize(&self, state: i64) -> Result<SqliteValue> {
195 Ok(SqliteValue::Integer(state))
196 }
197
198 fn num_args(&self) -> i32 {
199 1
200 }
201
202 fn name(&self) -> &str {
203 "sum"
204 }
205 }
206
207 #[test]
208 fn test_aggregate_initial_state() {
209 let agg = SumAgg;
210 assert_eq!(agg.initial_state(), 0);
211 }
212
213 #[test]
214 fn test_aggregate_step_and_finalize() {
215 let agg = SumAgg;
216 let mut state = agg.initial_state();
217
218 agg.step(&mut state, &[SqliteValue::Integer(10)]).unwrap();
219 agg.step(&mut state, &[SqliteValue::Integer(20)]).unwrap();
220 agg.step(&mut state, &[SqliteValue::Integer(12)]).unwrap();
221
222 let result = agg.finalize(state).unwrap();
223 assert_eq!(result, SqliteValue::Integer(42));
224 }
225
226 #[test]
227 fn test_aggregate_type_erasure_adapter() {
228 let adapted: AggregateAdapter<SumAgg> = AggregateAdapter::new(SumAgg);
229 let erased: Arc<dyn AggregateFunction<State = Box<dyn Any + Send>>> = Arc::new(adapted);
230
231 let mut state = erased.initial_state();
232 erased
233 .step(&mut state, &[SqliteValue::Integer(10)])
234 .unwrap();
235 erased
236 .step(&mut state, &[SqliteValue::Integer(32)])
237 .unwrap();
238
239 let result = erased.finalize(state).unwrap();
240 assert_eq!(result, SqliteValue::Integer(42));
241
242 let e2 = Arc::clone(&erased);
244 assert_eq!(e2.name(), "sum");
245 }
246}