grpc_webnext_client/
state.rs1use std::cell::RefCell;
10use std::rc::Rc;
11
12use futures::channel::mpsc;
13use futures::stream::Stream;
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
17pub enum ConnectivityState {
18 Idle,
21 Connecting,
23 Ready,
25 TransientFailure,
27}
28
29#[derive(Default)]
30struct Inner {
31 state: Option<ConnectivityState>,
32 watchers: Vec<mpsc::UnboundedSender<ConnectivityState>>,
33}
34
35#[derive(Clone, Default)]
37pub(crate) struct StateWatch {
38 inner: Rc<RefCell<Inner>>,
39}
40
41impl StateWatch {
42 pub(crate) fn get(&self) -> ConnectivityState {
43 self.inner.borrow().state.unwrap_or(ConnectivityState::Idle)
44 }
45
46 pub(crate) fn set(&self, state: ConnectivityState) {
49 let mut inner = self.inner.borrow_mut();
50 if inner.state == Some(state) {
51 return;
52 }
53 inner.state = Some(state);
54 inner.watchers.retain(|w| w.unbounded_send(state).is_ok());
57 }
58
59 pub(crate) fn watch(&self) -> impl Stream<Item = ConnectivityState> + 'static {
60 let (tx, rx) = mpsc::unbounded();
61 self.inner.borrow_mut().watchers.push(tx);
62 rx
63 }
64}
65
66#[cfg(test)]
67mod tests {
68 use super::*;
69 use futures::StreamExt;
70
71 #[test]
72 fn starts_idle() {
73 assert_eq!(StateWatch::default().get(), ConnectivityState::Idle);
74 }
75
76 #[test]
77 fn watchers_see_transitions_but_not_repeats() {
78 futures::executor::block_on(async {
79 let watch = StateWatch::default();
80 let mut seen = watch.watch();
81
82 watch.set(ConnectivityState::Connecting);
83 watch.set(ConnectivityState::Connecting); watch.set(ConnectivityState::Ready);
85 watch.set(ConnectivityState::Idle);
86 drop(watch);
87
88 let all: Vec<_> = seen.by_ref().collect().await;
89 assert_eq!(
90 all,
91 vec![
92 ConnectivityState::Connecting,
93 ConnectivityState::Ready,
94 ConnectivityState::Idle
95 ]
96 );
97 });
98 }
99
100 #[test]
101 fn a_watcher_added_later_sees_only_what_follows() {
102 futures::executor::block_on(async {
103 let watch = StateWatch::default();
104 watch.set(ConnectivityState::Ready);
105 let mut late = watch.watch();
106 watch.set(ConnectivityState::Idle);
107 drop(watch);
108
109 let all: Vec<_> = late.by_ref().collect().await;
110 assert_eq!(all, vec![ConnectivityState::Idle]);
111 });
112 }
113
114 #[test]
115 fn a_dropped_watcher_does_not_accumulate() {
116 let watch = StateWatch::default();
117 drop(watch.watch());
118 watch.set(ConnectivityState::Connecting);
119 watch.set(ConnectivityState::Ready);
120 assert!(watch.inner.borrow().watchers.is_empty());
121 }
122}