1+ use std:: collections:: { HashMap , HashSet } ;
2+
3+ pub struct Subscriptions {
4+ // Clients and their set of signals subscribed
5+ // client -> signal
6+ client_to_signal : HashMap < String , HashSet < String > > ,
7+ // Signals and its subscribed clients
8+ // signal -> client
9+ signal_to_client : HashMap < String , HashSet < String > >
10+ }
11+
12+ impl Subscriptions {
13+ pub fn new ( ) -> Self {
14+ return Self {
15+ client_to_signal : HashMap :: new ( ) ,
16+ signal_to_client : HashMap :: new ( )
17+ } ;
18+ }
19+
20+ /**
21+ * Subscribe client to signal
22+ */
23+ pub fn subscribe_client_to_signal ( & mut self , client : String , signal : String ) {
24+ // check if client exists, and add
25+ if let Some ( client_signals) = self . client_to_signal . get_mut ( & client) {
26+ client_signals. insert ( signal. clone ( ) ) ;
27+ } else {
28+ let mut new_client_signals = HashSet :: new ( ) ;
29+ new_client_signals. insert ( signal. clone ( ) ) ;
30+ self . client_to_signal . insert ( client. clone ( ) , new_client_signals) ;
31+ }
32+
33+ // check if signal exists and add signal
34+ if let Some ( signal_clients) = self . signal_to_client . get_mut ( & signal) {
35+ signal_clients. insert ( client. clone ( ) ) ;
36+ } else {
37+ let mut new_signal_clients = HashSet :: new ( ) ;
38+ new_signal_clients. insert ( client. clone ( ) ) ;
39+ self . signal_to_client . insert ( signal. clone ( ) , new_signal_clients) ;
40+ }
41+ }
42+
43+ /**
44+ * Remove client from signal
45+ */
46+ pub fn unsubscribe_client_from_signal ( & mut self , client : String , signal : String ) {
47+ // check if client exists
48+ if let Some ( client_signals) = self . client_to_signal . get_mut ( & client) {
49+ client_signals. remove ( & signal) ;
50+
51+ if client_signals. len ( ) <= 0 {
52+ self . client_to_signal . remove ( & client) ;
53+ }
54+ }
55+
56+ // check if signal exists and add signal
57+ if let Some ( signal_clients) = self . signal_to_client . get_mut ( & signal) {
58+ signal_clients. remove ( & client) ;
59+
60+ if signal_clients. len ( ) <= 0 {
61+ self . signal_to_client . remove ( & signal) ;
62+ }
63+ }
64+ }
65+
66+ pub fn get_clients_of_signal ( & self , signal : String ) -> Vec < & String > {
67+ return self . signal_to_client
68+ . get ( & signal)
69+ . map ( |clients| clients. iter ( ) . collect ( ) )
70+ . unwrap_or_default ( ) ;
71+ }
72+
73+ /**
74+ * Check if client is subscribed to signal
75+ */
76+ pub fn is_client_subscribed_to ( & self , client : String , signal : String ) -> bool {
77+ self . client_to_signal
78+ . get ( & client)
79+ . map_or ( false , |signals| signals. contains ( & signal) )
80+ }
81+
82+ /**
83+ * Check if signal is subscribed to by client
84+ */
85+ pub fn is_signal_subscribed_by ( & self , signal : String , client : String ) -> bool {
86+ self . signal_to_client
87+ . get ( & signal)
88+ . map_or ( false , |clients| clients. contains ( & client) )
89+ }
90+ }
91+
92+ /**
93+ * Unit tests
94+ */
95+ #[ cfg( test) ]
96+ mod tests {
97+ use super :: * ;
98+
99+ #[ test]
100+ fn test_subscribe_client_to_signal ( ) {
101+ let mut subs = Subscriptions :: new ( ) ;
102+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
103+
104+ assert ! ( subs. is_client_subscribed_to( "client1" . to_string( ) , "signal1" . to_string( ) ) ) ;
105+ assert ! ( subs. is_signal_subscribed_by( "signal1" . to_string( ) , "client1" . to_string( ) ) ) ;
106+ }
107+
108+ #[ test]
109+ fn test_subscribe_multiple_signals_to_client ( ) {
110+ let mut subs = Subscriptions :: new ( ) ;
111+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
112+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal2" . to_string ( ) ) ;
113+
114+ assert ! ( subs. is_client_subscribed_to( "client1" . to_string( ) , "signal1" . to_string( ) ) ) ;
115+ assert ! ( subs. is_client_subscribed_to( "client1" . to_string( ) , "signal2" . to_string( ) ) ) ;
116+ }
117+
118+ #[ test]
119+ fn test_subscribe_multiple_clients_to_signal ( ) {
120+ let mut subs = Subscriptions :: new ( ) ;
121+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
122+ subs. subscribe_client_to_signal ( "client2" . to_string ( ) , "signal1" . to_string ( ) ) ;
123+
124+ let clients = subs. get_clients_of_signal ( "signal1" . to_string ( ) ) ;
125+ assert_eq ! ( clients. len( ) , 2 ) ;
126+ assert ! ( clients. contains( &&"client1" . to_string( ) ) ) ;
127+ assert ! ( clients. contains( &&"client2" . to_string( ) ) ) ;
128+ }
129+
130+ #[ test]
131+ fn test_unsubscribe_client_from_signal ( ) {
132+ let mut subs = Subscriptions :: new ( ) ;
133+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
134+ subs. unsubscribe_client_from_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
135+
136+ assert ! ( !subs. is_client_subscribed_to( "client1" . to_string( ) , "signal1" . to_string( ) ) ) ;
137+ assert ! ( !subs. is_signal_subscribed_by( "signal1" . to_string( ) , "client1" . to_string( ) ) ) ;
138+ }
139+
140+ #[ test]
141+ fn test_unsubscribe_removes_empty_entries ( ) {
142+ let mut subs = Subscriptions :: new ( ) ;
143+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
144+ subs. unsubscribe_client_from_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
145+
146+ assert ! ( subs. get_clients_of_signal( "signal1" . to_string( ) ) . is_empty( ) ) ;
147+ }
148+
149+ #[ test]
150+ fn test_unsubscribe_one_of_multiple_signals ( ) {
151+ let mut subs = Subscriptions :: new ( ) ;
152+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
153+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal2" . to_string ( ) ) ;
154+ subs. unsubscribe_client_from_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
155+
156+ assert ! ( !subs. is_client_subscribed_to( "client1" . to_string( ) , "signal1" . to_string( ) ) ) ;
157+ assert ! ( subs. is_client_subscribed_to( "client1" . to_string( ) , "signal2" . to_string( ) ) ) ;
158+ }
159+
160+ #[ test]
161+ fn test_get_clients_of_nonexistent_signal ( ) {
162+ let subs = Subscriptions :: new ( ) ;
163+ let clients = subs. get_clients_of_signal ( "nonexistent" . to_string ( ) ) ;
164+ assert ! ( clients. is_empty( ) ) ;
165+ }
166+
167+ #[ test]
168+ fn test_double_subscribe_same_client_signal ( ) {
169+ let mut subs = Subscriptions :: new ( ) ;
170+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
171+ subs. subscribe_client_to_signal ( "client1" . to_string ( ) , "signal1" . to_string ( ) ) ;
172+
173+ let clients = subs. get_clients_of_signal ( "signal1" . to_string ( ) ) ;
174+ assert_eq ! ( clients. len( ) , 1 ) ;
175+ }
176+ }
0 commit comments