1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
use std::{collections::HashMap, fmt::Debug, hash::Hash};

#[derive(Debug)]
pub struct BusLocalHub<Owner, ChannelId: Hash + PartialEq + Eq> {
    channels: HashMap<ChannelId, Vec<Owner>>,
}

impl<Owner, ChannelId: Hash + PartialEq + Eq> Default for BusLocalHub<Owner, ChannelId> {
    fn default() -> Self {
        Self {
            channels: HashMap::new(),
        }
    }
}

impl<Owner: Clone + Debug + PartialEq, ChannelId: Debug + Clone + Copy + Hash + PartialEq + Eq>
    BusLocalHub<Owner, ChannelId>
{
    /// subscribe to a channel. if it is first time subscription, return true; else return false
    pub fn subscribe(&mut self, owner: Owner, channel: ChannelId) -> bool {
        let entry = self.channels.entry(channel).or_default();
        if entry.contains(&owner) {
            false
        } else {
            entry.push(owner);
            entry.len() == 1
        }
    }

    /// unsubscribe from a channel. if it is last time unsubscription, return true; else return false
    pub fn unsubscribe(&mut self, owner: Owner, channel: ChannelId) -> bool {
        log::info!("channels: {:?}", self.channels);
        if let Some(entry) = self.channels.get_mut(&channel) {
            log::info!("remove owner {:?} with list {:?}", owner, entry);
            if let Some(pos) = entry.iter().position(|x| *x == owner) {
                log::info!("remove owner {:?}", owner);
                entry.swap_remove(pos);
                if entry.is_empty() {
                    self.channels.remove(&channel);
                    true
                } else {
                    false
                }
            } else {
                false
            }
        } else {
            false
        }
    }

    /// get all subscribers of a channel
    pub fn get_subscribers(&self, channel: ChannelId) -> Option<Vec<Owner>> {
        self.channels.get(&channel).cloned()
    }

    /// remove owner from all channels
    pub fn remove_owner(&mut self, owner: Owner) {
        let mut removed_channels = vec![];
        for (channel, entry) in self.channels.iter_mut() {
            if let Some(pos) = entry.iter().position(|x| x == &owner) {
                entry.swap_remove(pos);
            }
            if entry.is_empty() {
                removed_channels.push(*channel);
            }
        }
        for channel in removed_channels {
            self.channels.remove(&channel);
        }
    }
}

#[cfg(test)]
mod tests {
    use crate::group_owner_type;

    use super::*;

    group_owner_type!(DemoOwner);

    #[derive(Debug, PartialEq, Eq, Hash, Clone, Copy)]
    enum Channel {
        A,
        B,
        C,
    }

    #[test]
    fn test_subscribe_unsubscribe() {
        let mut hub: BusLocalHub<DemoOwner, Channel> = BusLocalHub::default();
        let owner1 = DemoOwner(1);
        let owner2 = DemoOwner(2);

        assert_eq!(hub.subscribe(owner1, Channel::A), true);
        assert_eq!(hub.subscribe(owner2, Channel::A), false);
        assert_eq!(hub.subscribe(owner1, Channel::B), true);
        assert_eq!(hub.subscribe(owner1, Channel::C), true);

        assert_eq!(hub.unsubscribe(owner1, Channel::A), false);
        assert_eq!(hub.unsubscribe(owner2, Channel::A), true);
        assert_eq!(hub.unsubscribe(owner1, Channel::B), true);
        assert_eq!(hub.unsubscribe(owner1, Channel::C), true);
    }

    #[test]
    fn test_get_subscribers() {
        let mut hub: BusLocalHub<DemoOwner, Channel> = BusLocalHub::default();
        let owner1 = DemoOwner(1);
        let owner2 = DemoOwner(2);

        hub.subscribe(owner1, Channel::A);
        hub.subscribe(owner1, Channel::B);
        hub.subscribe(owner2, Channel::A);

        assert_eq!(hub.get_subscribers(Channel::A), Some(vec![owner1, owner2]));
        assert_eq!(hub.get_subscribers(Channel::B), Some(vec![owner1]));
        assert_eq!(hub.get_subscribers(Channel::C), None);
    }

    #[test]
    fn test_remove_owner() {
        let mut hub: BusLocalHub<DemoOwner, Channel> = BusLocalHub::default();
        let owner1 = DemoOwner(1);
        let owner2 = DemoOwner(2);

        hub.subscribe(owner1, Channel::A);
        hub.subscribe(owner1, Channel::B);
        hub.subscribe(owner2, Channel::A);

        hub.remove_owner(owner1);

        assert_eq!(hub.get_subscribers(Channel::A), Some(vec![owner2]));
        assert_eq!(hub.get_subscribers(Channel::B), None);
    }
}