signals2/
combiner.rs

1// Copyright Christian Daley 2021
2// Copyright Frank Mori Hess 2007-2008.
3// Distributed under the Boost Software License, Version 1.0. 
4// See http://www.boost.org/LICENSE_1_0.txt
5
6use std::iter::Sum;
7
8/// Types that can be used as a combiner for a signal. 
9pub trait Combiner<R>: Send + Sync {
10    /// The return type of the signal. May be different than the return type of
11    /// the individual slots.
12    type Output;
13
14    /// Combines the results of executing the signal's slots into a single output.
15    /// Note that `iter` lazily executes the signal's slots. The first slot will not be
16    /// executed until `iter.next()` is called for the first time. The second slot will not
17    /// be executed until `iter.next()` is called again, etc. Consuming the `iter` is what
18    /// causes slots to be executed. If a custom combiner is created that never uses `iter`,
19    /// no slots will ever be executed when the signal is emitted.
20    fn combine(&self, iter: impl Iterator<Item=R>) -> Self::Output;
21}
22
23#[derive(Default)]
24/// The default combiner for signals. Will return an `Option<R>` representing the returned value
25/// from the last slot that was executed. If no slots were executed, returns `None`.
26pub struct DefaultCombiner {}
27
28impl<R> Combiner<R> for DefaultCombiner {
29    type Output = Option<R>;
30
31    fn combine(&self, iter: impl Iterator<Item=R>) -> Option<R> {
32        iter.last()
33    }
34}
35
36#[derive(Default)]
37/// A combiner that collects all of the slot's return values into a vector.
38pub struct VecCombiner {}
39
40impl<R> Combiner<R> for VecCombiner {
41    type Output = Vec<R>;
42
43    fn combine(&self, iter: impl Iterator<Item=R>) -> Vec<R> {
44        iter.collect()
45    }
46}
47
48#[derive(Default)]
49/// A combiner that sums all of the slot's return values.
50pub struct SumCombiner {}
51
52impl<R> Combiner<R> for SumCombiner 
53where
54    R: Sum
55{
56    type Output = R;
57
58    fn combine(&self, iter: impl Iterator<Item=R>) -> R {
59        iter.sum()
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::*;
66
67    #[test]
68    fn default_combiner_test() {
69        let combiner = DefaultCombiner::default();
70        let values1 = vec!(5, 1, 9);
71        let values2: Vec<i32> = Vec::new();
72        assert_eq!(combiner.combine(values1.into_iter()), Some(9));
73        assert_eq!(combiner.combine(values2.into_iter()), None);
74    }
75
76    #[test]
77    fn vec_combiner_test() {
78        let combiner = VecCombiner::default();
79        let values1 = vec!(5, 1, 9);
80        let values2: Vec<i32> = Vec::new();
81        assert_eq!(combiner.combine(values1.iter().cloned()), values1);
82        assert_eq!(combiner.combine(values2.iter().cloned()), values2);
83    }
84
85    #[test]
86    fn sum_combiner_test() {
87        let combiner = SumCombiner::default();
88        let values1 = vec!(5, 1, 9);
89        let values2: Vec<i32> = Vec::new();
90        assert_eq!(combiner.combine(values1.iter().cloned()), 15);
91        assert_eq!(combiner.combine(values2.iter().cloned()), 0);
92    }
93}