incular_runtime/
application_activations.rs1use incular_platform::ApplicationActivation;
4use incular_widgets::{RouteInformation, RouteInformationProvider};
5use std::{
6 cell::{Cell, RefCell},
7 collections::VecDeque,
8 fmt,
9 rc::{Rc, Weak},
10};
11
12pub type ApplicationActivationListener = Rc<dyn Fn(ApplicationActivation)>;
14
15#[derive(Default)]
16struct ActivationState {
17 pending: VecDeque<ApplicationActivation>,
18 listeners: Vec<Weak<dyn Fn(ApplicationActivation)>>,
19}
20
21#[derive(Default)]
26struct ActivationDrain {
27 queue: RefCell<ActivationState>,
28 active: Cell<bool>,
29}
30
31#[derive(Clone, Default)]
65pub struct ApplicationActivationService {
66 state: Rc<ActivationDrain>,
67}
68
69impl fmt::Debug for ApplicationActivationService {
70 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
71 let state = self.state.queue.borrow();
72 formatter
73 .debug_struct("ApplicationActivationService")
74 .field("pending", &state.pending.len())
75 .field("listeners", &state.listeners.len())
76 .finish()
77 }
78}
79
80impl ApplicationActivationService {
81 pub fn subscribe(
84 &self,
85 listener: impl Fn(ApplicationActivation) + 'static,
86 ) -> ApplicationActivationSubscription {
87 let listener: ApplicationActivationListener = Rc::new(listener);
88 self.state
89 .queue
90 .borrow_mut()
91 .listeners
92 .push(Rc::downgrade(&listener));
93 drain(&self.state);
94 ApplicationActivationSubscription {
95 _listener: listener,
96 }
97 }
98
99 #[doc(hidden)]
100 pub fn publish(&self, activation: ApplicationActivation) {
101 self.state.queue.borrow_mut().pending.push_back(activation);
102 drain(&self.state);
103 }
104
105 #[must_use]
106 pub fn pending_count(&self) -> usize {
107 self.state.queue.borrow().pending.len()
108 }
109}
110
111struct DispatchGuard {
116 shared: Rc<ActivationDrain>,
117}
118impl DispatchGuard {
119 fn acquire(shared: &Rc<ActivationDrain>) -> Option<Self> {
120 if shared.active.get() {
121 return None;
122 }
123 {
124 let mut queue = shared.queue.borrow_mut();
125 queue
126 .listeners
127 .retain(|listener| listener.strong_count() != 0);
128 if queue.listeners.is_empty() || queue.pending.is_empty() {
129 return None;
130 }
131 }
132 shared.active.set(true);
135 Some(Self {
136 shared: shared.clone(),
137 })
138 }
139}
140impl Drop for DispatchGuard {
141 fn drop(&mut self) {
142 self.shared.active.set(false);
143 }
144}
145
146fn drain(state: &Rc<ActivationDrain>) {
147 let Some(_drain) = DispatchGuard::acquire(state) else {
148 return;
149 };
150
151 loop {
152 let next = {
153 let mut queue = state.queue.borrow_mut();
154 queue
155 .listeners
156 .retain(|listener| listener.strong_count() != 0);
157 if queue.listeners.is_empty() {
158 return;
159 }
160 queue.pending.pop_front().map(|activation| {
161 (
162 activation,
163 queue
164 .listeners
165 .iter()
166 .filter_map(Weak::upgrade)
167 .collect::<Vec<_>>(),
168 )
169 })
170 };
171 let Some((activation, listeners)) = next else {
172 return;
173 };
174 for listener in listeners {
175 listener(activation.clone());
176 }
177 }
178}
179
180pub struct ApplicationActivationSubscription {
182 _listener: ApplicationActivationListener,
183}
184
185impl fmt::Debug for ApplicationActivationSubscription {
186 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
187 formatter.write_str("ApplicationActivationSubscription(..)")
188 }
189}
190
191pub struct ActivationRouteBridge {
196 _subscription: ApplicationActivationSubscription,
197 delivered_routes: Rc<Cell<u64>>,
198}
199
200impl fmt::Debug for ActivationRouteBridge {
201 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
202 formatter
203 .debug_struct("ActivationRouteBridge")
204 .field("delivered_routes", &self.delivered_routes.get())
205 .finish()
206 }
207}
208
209impl ActivationRouteBridge {
210 pub(crate) fn new(
211 activations: &ApplicationActivationService,
212 provider: Rc<dyn RouteInformationProvider>,
213 map: impl Fn(&str) -> Option<RouteInformation> + 'static,
214 ) -> Self {
215 let delivered_routes = Rc::new(Cell::new(0_u64));
216 let delivered = delivered_routes.clone();
217 let subscription = activations.subscribe(move |activation| {
218 for url in activation.urls() {
219 if let Some(information) = map(url.as_str()) {
220 provider.set_platform_route_information(information);
221 delivered.set(delivered.get().saturating_add(1));
222 }
223 }
224 });
225 Self {
226 _subscription: subscription,
227 delivered_routes,
228 }
229 }
230
231 #[must_use]
232 pub fn delivered_routes(&self) -> u64 {
233 self.delivered_routes.get()
234 }
235}