Skip to main content

blackboard/
lib.rs

1use std::hash::Hash;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5///To be implemented by subscriptors, any FnMut(&mut T) closure automatically implements this
6pub trait Subscriptor<T> {
7    fn notify(&mut self, what: &mut T);
8}
9
10impl<T, F> Subscriptor<T> for F 
11    where F: FnMut(&mut T)
12{
13    ///Notifies the Subscriptor with a type
14    fn notify(&mut self, what: &mut T) {
15        self(what);
16    }
17}
18
19
20///Represents a blackboard where processes can post and get items in sections, and also subscribe
21///for changes in a specific section.
22pub struct BlackBoard<'a, Section: Hash + Eq, Type> {
23    map: HashMap<
24        Section,
25        (Vec<Type>, Vec<Rc<dyn Subscriptor<Type> + 'a>>)
26    >
27}
28
29impl<'a, Section, Type> BlackBoard<'a, Section, Type> 
30    where Section: Hash + Eq
31{
32    ///Generates a new, empty blackboard
33    pub fn new() -> Self {
34        BlackBoard { map: HashMap::new() }
35    }
36
37    ///Gets the section, if it exists.
38    pub fn get(&self, section: &Section) -> Option<&Vec<Type>> {
39        Some(
40            &self.map
41                .get(section)? //Get the section of return none if not exists
42                .0 //First element, the type
43            )
44    }
45
46    ///Returns a list of all the sections present right now in the BlackBoard
47    pub fn get_sections(&self) -> Vec<&Section> {
48        self.map.keys().collect()
49    }
50
51    ///Posts data on the desired section. Note that data is consumed
52    pub fn post(&mut self, section: Section, mut what: Type) {
53        let (things, subscriptors) = self.get_raw_section(section);
54
55        for subscriptor in subscriptors {
56            //Only notify valid ones
57            if let Some(subscriptor) = Rc::get_mut(subscriptor) {
58                subscriptor.notify(&mut what);
59            }
60        }
61
62        things.push(what);
63    }
64
65    ///Suscribes a function to the specified section. When a post to that section occurs, the
66    ///function passed in is called with the newly posted value as argument
67    pub fn subscribe(
68        &mut self,
69        section: Section,
70        subscriptor: impl Subscriptor<Type> + 'a
71        )
72    {
73        self.subscribe_rc(
74            section,
75            &Rc::new(subscriptor)
76        );
77    }
78
79    pub fn subscribe_rc(
80        &mut self,
81        section: Section,
82        subscriptor: &Rc<impl Subscriptor<Type> + 'a>
83        )
84    {
85        self.get_raw_section(section)
86            .1
87            .push(
88                Rc::clone(subscriptor) as Rc<dyn Subscriptor<Type>>
89            );
90    }
91
92    fn get_raw_section(&mut self, section: Section) -> 
93        &mut (Vec<Type>, Vec<Rc<dyn Subscriptor<Type> + 'a>>) 
94    {
95        self.map.entry(section)
96            .or_insert((
97                Vec::new(),
98                Vec::new(),
99                ))
100    }
101}
102
103//Its important to drop as soon as we can so closures dont mess up everything
104impl<'a, Section, Type> Drop for BlackBoard<'a, Section, Type>
105    where Section: Hash + Eq
106{
107    fn drop(&mut self) {}
108}
109
110#[cfg(test)]
111mod tests {
112    use super::*;
113
114    #[test]
115    fn post_and_get() {
116        let mut bb = BlackBoard::new();
117
118        bb.post("Street", 25);
119        bb.post("Home", 15);
120        bb.post("Street", 82);
121
122        assert!(
123            {
124                let mut all_are_in = true;
125                let expected = vec!["Street", "Home"];
126
127                for section in 
128                    bb.get_sections()
129                {
130                    if !expected.contains(&section) { 
131                        all_are_in = false;
132                        break;
133                    }
134                }
135
136                all_are_in
137            }
138        );
139
140        assert_eq!(
141            &vec![25, 82],
142            bb.get(&"Street").unwrap()
143        );
144        assert_eq!(
145            &vec![15],
146            bb.get(&"Home").unwrap()
147        );
148    }
149
150    #[test]
151    fn suscribe_test() {
152        let mut bb = BlackBoard::<&str, &str>::new();
153        
154        static mut changed: bool = false;
155        fn change(_: &&str) {
156            unsafe { changed = true }
157        }
158
159        bb.subscribe("Park", change);
160
161        bb.post("Park", "");
162
163        unsafe { assert!(changed); }
164    }
165}