1use std::hash::Hash;
2use std::collections::HashMap;
3use std::rc::Rc;
4
5pub 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 fn notify(&mut self, what: &mut T) {
15 self(what);
16 }
17}
18
19
20pub 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 pub fn new() -> Self {
34 BlackBoard { map: HashMap::new() }
35 }
36
37 pub fn get(&self, section: &Section) -> Option<&Vec<Type>> {
39 Some(
40 &self.map
41 .get(section)? .0 )
44 }
45
46 pub fn get_sections(&self) -> Vec<&Section> {
48 self.map.keys().collect()
49 }
50
51 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 if let Some(subscriptor) = Rc::get_mut(subscriptor) {
58 subscriptor.notify(&mut what);
59 }
60 }
61
62 things.push(what);
63 }
64
65 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
103impl<'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(§ion) {
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}