1use std::{
2 cell::RefCell,
3 collections::HashMap,
4 hash::Hash,
5 ops::{
6 Deref,
7 DerefMut,
8 },
9 rc::Rc,
10};
11
12use freya_core::{
13 integration::FxHashSet,
14 prelude::*,
15};
16
17#[cfg(feature = "tracing")]
18pub trait RadioChannel<T>: 'static + PartialEq + Eq + Clone + Hash + std::fmt::Debug + Ord {
19 fn derive_channel(self, _radio: &T) -> Vec<Self> {
20 vec![self]
21 }
22}
23
24#[cfg(not(feature = "tracing"))]
25pub trait RadioChannel<T>: 'static + PartialEq + Eq + Clone + Hash {
26 fn derive_channel(self, _radio: &T) -> Vec<Self> {
27 vec![self]
28 }
29}
30
31pub struct RadioStation<Value, Channel>
33where
34 Channel: RadioChannel<Value>,
35 Value: 'static,
36{
37 value: State<Value>,
38 listeners: State<HashMap<Channel, Rc<RefCell<FxHashSet<ReactiveContext>>>>>,
39}
40
41impl<Value, Channel> Clone for RadioStation<Value, Channel>
42where
43 Channel: RadioChannel<Value>,
44{
45 fn clone(&self) -> Self {
46 *self
47 }
48}
49
50impl<Value, Channel> Copy for RadioStation<Value, Channel> where Channel: RadioChannel<Value> {}
51
52impl<Value, Channel> RadioStation<Value, Channel>
53where
54 Channel: RadioChannel<Value>,
55{
56 pub(crate) fn create(init_value: Value) -> Self {
57 RadioStation {
58 value: State::create(init_value),
59 listeners: State::create(HashMap::default()),
60 }
61 }
62
63 pub fn create_global(init_value: Value) -> Self {
65 RadioStation {
66 value: State::create_global(init_value),
67 listeners: State::create_global(HashMap::default()),
68 }
69 }
70
71 pub(crate) fn is_listening(
72 &self,
73 channel: &Channel,
74 reactive_context: &ReactiveContext,
75 ) -> bool {
76 let listeners = self.listeners.peek();
77 listeners
78 .get(channel)
79 .map(|contexts| contexts.borrow().contains(reactive_context))
80 .unwrap_or_default()
81 }
82
83 pub(crate) fn listen(&self, channel: Channel, mut reactive_context: ReactiveContext) {
84 let mut listeners = self.listeners.write_unchecked();
85 let listeners = listeners.entry(channel).or_default();
86 reactive_context.subscribe(listeners);
87 }
88
89 pub(crate) fn notify_listeners(&self, channel: &Channel) {
90 let listeners = self.listeners.write_unchecked();
91
92 #[cfg(feature = "tracing")]
93 tracing::info!("Notifying {channel:?}");
94
95 for (listener_channel, listeners) in listeners.iter() {
96 if listener_channel == channel {
97 for reactive_context in listeners.borrow().iter() {
98 reactive_context.notify();
99 }
100 }
101 }
102 }
103
104 pub fn read(&'_ self) -> ReadRef<'_, Value> {
112 self.value.read()
113 }
114
115 pub fn peek(&'_ self) -> ReadRef<'_, Value> {
123 self.value.peek()
124 }
125
126 pub(crate) fn cleanup(&self) {
127 let mut listeners = self.listeners.write_unchecked();
128
129 listeners.retain(|_, listeners| !listeners.borrow().is_empty());
131
132 #[cfg(feature = "tracing")]
133 {
134 use itertools::Itertools;
135 use tracing::{
136 Level,
137 info,
138 span,
139 };
140
141 let mut channels_subscribers = HashMap::<&Channel, usize>::new();
142
143 for (channel, listeners) in listeners.iter() {
144 *channels_subscribers.entry(&channel).or_default() = listeners.borrow().len();
145 }
146
147 let span = span!(Level::DEBUG, "Radio Station Metrics");
148 let _enter = span.enter();
149
150 for (channel, count) in channels_subscribers.iter().sorted() {
151 info!(" {count} subscribers for {channel:?}")
152 }
153 }
154 }
155
156 pub fn write_channel(&mut self, channel: Channel) -> RadioGuard<Value, Channel> {
163 let value = self.value.write_unchecked();
164 RadioGuard {
165 channels: channel.clone().derive_channel(&*value),
166 station: *self,
167 value,
168 }
169 }
170}
171
172pub struct RadioAntenna<Value, Channel>
173where
174 Channel: RadioChannel<Value>,
175 Value: 'static,
176{
177 pub(crate) channel: Channel,
178 station: RadioStation<Value, Channel>,
179}
180
181impl<Value, Channel> RadioAntenna<Value, Channel>
182where
183 Channel: RadioChannel<Value>,
184{
185 pub(crate) fn new(
186 channel: Channel,
187 station: RadioStation<Value, Channel>,
188 ) -> RadioAntenna<Value, Channel> {
189 RadioAntenna { channel, station }
190 }
191}
192impl<Value, Channel> Clone for RadioAntenna<Value, Channel>
193where
194 Channel: RadioChannel<Value>,
195{
196 fn clone(&self) -> Self {
197 Self {
198 channel: self.channel.clone(),
199 station: self.station,
200 }
201 }
202}
203
204pub struct RadioGuard<Value, Channel>
205where
206 Channel: RadioChannel<Value>,
207 Value: 'static,
208{
209 station: RadioStation<Value, Channel>,
210 channels: Vec<Channel>,
211 value: WriteRef<'static, Value>,
212}
213
214impl<Value, Channel> Drop for RadioGuard<Value, Channel>
215where
216 Channel: RadioChannel<Value>,
217{
218 fn drop(&mut self) {
219 for channel in &mut self.channels {
220 self.station.notify_listeners(channel)
221 }
222 if !self.channels.is_empty() {
223 self.station.cleanup();
224 }
225 }
226}
227
228impl<Value, Channel> Deref for RadioGuard<Value, Channel>
229where
230 Channel: RadioChannel<Value>,
231{
232 type Target = WriteRef<'static, Value>;
233
234 fn deref(&self) -> &Self::Target {
235 &self.value
236 }
237}
238
239impl<Value, Channel> DerefMut for RadioGuard<Value, Channel>
240where
241 Channel: RadioChannel<Value>,
242{
243 fn deref_mut(&mut self) -> &mut WriteRef<'static, Value> {
244 &mut self.value
245 }
246}
247
248pub struct Radio<Value, Channel>
250where
251 Channel: RadioChannel<Value>,
252 Value: 'static,
253{
254 antenna: State<RadioAntenna<Value, Channel>>,
255}
256
257impl<Value, Channel> Clone for Radio<Value, Channel>
258where
259 Channel: RadioChannel<Value>,
260{
261 fn clone(&self) -> Self {
262 *self
263 }
264}
265impl<Value, Channel> Copy for Radio<Value, Channel> where Channel: RadioChannel<Value> {}
266
267impl<Value, Channel> PartialEq for Radio<Value, Channel>
268where
269 Channel: RadioChannel<Value>,
270{
271 fn eq(&self, other: &Self) -> bool {
272 self.antenna == other.antenna
273 }
274}
275
276impl<Value, Channel> MutView<'static, Value> for Radio<Value, Channel>
277where
278 Channel: RadioChannel<Value>,
279{
280 fn read(&mut self) -> ReadRef<'static, Value> {
281 self.subscribe_if_not();
282 self.antenna.peek().station.value.peek()
283 }
284
285 fn peek(&mut self) -> ReadRef<'static, Value> {
286 self.antenna.peek().station.value.peek()
287 }
288
289 fn write(&mut self) -> WriteRef<'static, Value> {
290 let value = self.antenna.peek().station.value.write_unchecked();
291 let channel = self.antenna.peek().channel.clone();
292 for channel in channel.derive_channel(&value) {
293 self.antenna.peek().station.notify_listeners(&channel)
294 }
295 value
296 }
297
298 fn write_if(&mut self, with: impl FnOnce(WriteRef<'static, Value>) -> bool) {
299 let changed = with(self.antenna.peek().station.value.write_unchecked());
300 let channel = changed.then_some(self.antenna.peek().channel.clone());
301 if let Some(channel) = channel {
302 let value = self.antenna.peek().station.value.write_unchecked();
303 for channel in channel.derive_channel(&value) {
304 self.antenna.peek().station.notify_listeners(&channel)
305 }
306 self.antenna.peek().station.cleanup();
307 }
308 }
309}
310
311impl<Value, Channel> Radio<Value, Channel>
312where
313 Channel: RadioChannel<Value>,
314{
315 pub(crate) fn new(antenna: State<RadioAntenna<Value, Channel>>) -> Radio<Value, Channel> {
316 Radio { antenna }
317 }
318
319 pub(crate) fn subscribe_if_not(&self) {
320 if let Some(rc) = ReactiveContext::try_current() {
321 let antenna = &self.antenna.write_unchecked();
322 let channel = antenna.channel.clone();
323 let is_listening = antenna.station.is_listening(&channel, &rc);
324
325 if !is_listening {
327 antenna.station.listen(channel, rc);
328 }
329 }
330 }
331
332 pub fn read(&'_ self) -> ReadRef<'_, Value> {
340 self.subscribe_if_not();
341 self.antenna.peek().station.value.peek()
342 }
343
344 pub fn with(&self, cb: impl FnOnce(ReadRef<Value>)) {
354 self.subscribe_if_not();
355 let value = self.antenna.peek().station.value;
356 let borrow = value.read();
357 cb(borrow);
358 }
359
360 pub fn write(&mut self) -> RadioGuard<Value, Channel> {
368 let value = self.antenna.peek().station.value.write_unchecked();
369 let channel = self.antenna.peek().channel.clone();
370 RadioGuard {
371 channels: channel.derive_channel(&*value),
372 station: self.antenna.read().station,
373 value,
374 }
375 }
376
377 pub fn write_with(&mut self, cb: impl FnOnce(RadioGuard<Value, Channel>)) {
387 let guard = self.write();
388 cb(guard);
389 }
390
391 pub fn write_channel(&mut self, channel: Channel) -> RadioGuard<Value, Channel> {
398 let value = self.antenna.peek().station.value.write_unchecked();
399 RadioGuard {
400 channels: channel.derive_channel(&*value),
401 station: self.antenna.read().station,
402 value,
403 }
404 }
405
406 pub fn write_channel_with(
416 &mut self,
417 channel: Channel,
418 cb: impl FnOnce(RadioGuard<Value, Channel>),
419 ) {
420 let guard = self.write_channel(channel);
421 cb(guard);
422 }
423
424 pub fn write_with_channel_selection(
439 &mut self,
440 cb: impl FnOnce(&mut Value) -> ChannelSelection<Channel>,
441 ) -> ChannelSelection<Channel> {
442 let value = self.antenna.peek().station.value.write_unchecked();
443 let mut guard = RadioGuard {
444 channels: Vec::default(),
445 station: self.antenna.read().station,
446 value,
447 };
448 let channel_selection = cb(&mut guard.value);
449 let channel = match channel_selection.clone() {
450 ChannelSelection::Current => Some(self.antenna.peek().channel.clone()),
451 ChannelSelection::Silence => None,
452 ChannelSelection::Select(c) => Some(c),
453 };
454 if let Some(channel) = channel {
455 for channel in channel.derive_channel(&guard.value) {
456 self.antenna.peek().station.notify_listeners(&channel)
457 }
458 self.antenna.peek().station.cleanup();
459 }
460
461 channel_selection
462 }
463
464 pub fn write_silently(&mut self) -> RadioGuard<Value, Channel> {
468 let value = self.antenna.peek().station.value.write_unchecked();
469 RadioGuard {
470 channels: Vec::default(),
471 station: self.antenna.read().station,
472 value,
473 }
474 }
475}
476
477impl<Channel> Copy for ChannelSelection<Channel> where Channel: Copy {}
478
479#[derive(Clone)]
480pub enum ChannelSelection<Channel> {
481 Current,
483 Select(Channel),
485 Silence,
487}
488
489impl<Channel> ChannelSelection<Channel> {
490 pub fn current(&mut self) {
492 *self = Self::Current
493 }
494
495 pub fn select(&mut self, channel: Channel) {
497 *self = Self::Select(channel)
498 }
499
500 pub fn silence(&mut self) {
502 *self = Self::Silence
503 }
504
505 pub fn is_current(&self) -> bool {
507 matches!(self, Self::Current)
508 }
509
510 pub fn is_select(&self) -> Option<&Channel> {
512 match self {
513 Self::Select(channel) => Some(channel),
514 _ => None,
515 }
516 }
517
518 pub fn is_silence(&self) -> bool {
520 matches!(self, Self::Silence)
521 }
522}
523
524pub fn use_share_radio<Value, Channel>(radio: impl FnOnce() -> RadioStation<Value, Channel>)
529where
530 Channel: RadioChannel<Value>,
531 Value: 'static,
532{
533 use_provide_context(radio);
534}
535
536pub fn use_radio<Value, Channel>(channel: Channel) -> Radio<Value, Channel>
540where
541 Channel: RadioChannel<Value>,
542 Value: 'static,
543{
544 let station = use_consume::<RadioStation<Value, Channel>>();
545
546 let mut radio = use_hook(|| {
547 let antenna = RadioAntenna::new(channel.clone(), station);
548 Radio::new(State::create(antenna))
549 });
550
551 if radio.antenna.peek().channel != channel {
552 radio.antenna.write().channel = channel;
553 }
554
555 radio
556}
557
558pub fn use_init_radio_station<Value, Channel>(
559 init_value: impl FnOnce() -> Value,
560) -> RadioStation<Value, Channel>
561where
562 Channel: RadioChannel<Value>,
563 Value: 'static,
564{
565 use_provide_context(|| RadioStation::create(init_value()))
566}
567
568pub fn use_radio_station<Value, Channel>() -> RadioStation<Value, Channel>
569where
570 Channel: RadioChannel<Value>,
571 Value: 'static,
572{
573 use_consume::<RadioStation<Value, Channel>>()
574}
575
576pub trait DataReducer {
577 type Channel;
578 type Action;
579
580 fn reduce(&mut self, action: Self::Action) -> ChannelSelection<Self::Channel>;
581}
582
583pub trait RadioReducer {
584 type Action;
585 type Channel;
586
587 fn apply(&mut self, action: Self::Action) -> ChannelSelection<Self::Channel>;
588}
589
590impl<Data: DataReducer<Channel = Channel, Action = Action>, Channel: RadioChannel<Data>, Action>
591 RadioReducer for Radio<Data, Channel>
592{
593 type Action = Action;
594 type Channel = Channel;
595
596 fn apply(&mut self, action: Action) -> ChannelSelection<Channel> {
597 self.write_with_channel_selection(|data| data.reduce(action))
598 }
599}
600
601pub trait DataAsyncReducer {
602 type Channel;
603 type Action;
604
605 #[allow(async_fn_in_trait)]
606 async fn async_reduce(
607 _radio: &mut Radio<Self, Self::Channel>,
608 _action: Self::Action,
609 ) -> ChannelSelection<Self::Channel>
610 where
611 Self::Channel: RadioChannel<Self>,
612 Self: Sized;
613}
614
615pub trait RadioAsyncReducer {
616 type Action;
617
618 fn async_apply(&mut self, _action: Self::Action)
619 where
620 Self::Action: 'static;
621}
622
623impl<
624 Data: DataAsyncReducer<Channel = Channel, Action = Action>,
625 Channel: RadioChannel<Data>,
626 Action,
627> RadioAsyncReducer for Radio<Data, Channel>
628{
629 type Action = Action;
630
631 fn async_apply(&mut self, action: Self::Action)
632 where
633 Self::Action: 'static,
634 {
635 let mut radio = *self;
636 spawn(async move {
637 let channel = Data::async_reduce(&mut radio, action).await;
638 radio.write_with_channel_selection(|_| channel);
639 });
640 }
641}