freya_radio/
slice.rs

1use std::{
2    cell::{
3        Ref,
4        RefMut,
5    },
6    marker::PhantomData,
7    rc::Rc,
8};
9
10use freya_core::prelude::*;
11
12use crate::hooks::{
13    Radio,
14    RadioChannel,
15    RadioStation,
16};
17
18/// A read-only slice of a portion of the global radio state.
19///
20/// Components using a slice only re-render when that specific portion changes,
21/// as determined by the slice's channel.
22///
23/// # Example
24///
25/// ```rust, ignore
26/// let count_slice = radio.slice(AppChannel::Count, |state| &state.count);
27/// child_component(count_slice)
28/// ```
29pub struct RadioSlice<Value, SliceValue, Channel>
30where
31    Channel: RadioChannel<Value>,
32    Value: 'static,
33    SliceValue: 'static,
34{
35    channel: Channel,
36    station: RadioStation<Value, Channel>,
37    selector: Rc<dyn Fn(&Value) -> &SliceValue>,
38    _marker: PhantomData<SliceValue>,
39}
40
41impl<Value, SliceValue, Channel> Clone for RadioSlice<Value, SliceValue, Channel>
42where
43    Channel: RadioChannel<Value>,
44    SliceValue: 'static,
45{
46    fn clone(&self) -> Self {
47        Self {
48            channel: self.channel.clone(),
49            station: self.station,
50            selector: self.selector.clone(),
51            _marker: PhantomData,
52        }
53    }
54}
55
56impl<Value, SliceValue, Channel> PartialEq for RadioSlice<Value, SliceValue, Channel>
57where
58    Channel: RadioChannel<Value>,
59    SliceValue: 'static,
60{
61    fn eq(&self, other: &Self) -> bool {
62        self.channel == other.channel
63    }
64}
65
66impl<Value, SliceValue, Channel> RadioSlice<Value, SliceValue, Channel>
67where
68    Channel: RadioChannel<Value>,
69    SliceValue: 'static,
70{
71    pub(crate) fn new(
72        channel: Channel,
73        station: RadioStation<Value, Channel>,
74        selector: impl Fn(&Value) -> &SliceValue + 'static,
75    ) -> RadioSlice<Value, SliceValue, Channel> {
76        RadioSlice {
77            channel,
78            station,
79            selector: Rc::new(selector),
80            _marker: PhantomData,
81        }
82    }
83
84    pub(crate) fn subscribe_if_not(&self) {
85        if let Some(rc) = ReactiveContext::try_current() {
86            let is_listening = self.station.is_listening(&self.channel, &rc);
87
88            if !is_listening {
89                self.station.listen(self.channel.clone(), rc);
90            }
91        }
92    }
93
94    /// Read the slice value and subscribe to changes.
95    #[allow(invalid_reference_casting)]
96    pub fn read(&'_ self) -> ReadRef<'_, SliceValue> {
97        self.subscribe_if_not();
98        self.peek()
99    }
100
101    /// Read the slice value and subscribe to changes, with 'static lifetime.
102    #[allow(invalid_reference_casting)]
103    pub fn read_unchecked(&'_ self) -> ReadRef<'static, SliceValue> {
104        self.subscribe_if_not();
105        self.peek_unchecked()
106    }
107
108    /// Read the slice value without subscribing.
109    #[allow(invalid_reference_casting)]
110    pub fn peek(&'_ self) -> ReadRef<'_, SliceValue> {
111        self.peek_unchecked()
112    }
113
114    /// Read the slice value without subscribing, with 'static lifetime.
115    #[allow(invalid_reference_casting)]
116    pub fn peek_unchecked(&'_ self) -> ReadRef<'static, SliceValue> {
117        let inner = self.station.peek_unchecked();
118        inner.map(|v| {
119            Ref::map(v, |v| {
120                (self.selector)(unsafe { &mut *(v as *const Value as *mut Value) })
121            })
122        })
123    }
124}
125
126/// A mutable slice of a portion of the global radio state.
127///
128/// Like `RadioSlice`, components only re-render when the specific portion changes.
129///
130/// # Example
131///
132/// ```rust, ignore
133/// let mut count_slice = radio.slice_mut(AppChannel::Count, |state| &mut state.count);
134/// child_component(count_slice)
135/// ```
136pub struct RadioSliceMut<Value, SliceValue, Channel>
137where
138    Channel: RadioChannel<Value>,
139    Value: 'static,
140    SliceValue: 'static,
141{
142    channel: Channel,
143    pub(crate) station: RadioStation<Value, Channel>,
144    selector: Rc<dyn Fn(&mut Value) -> &mut SliceValue>,
145    _marker: PhantomData<SliceValue>,
146}
147
148impl<Value, SliceValue, Channel> Clone for RadioSliceMut<Value, SliceValue, Channel>
149where
150    Channel: RadioChannel<Value>,
151    SliceValue: 'static,
152{
153    fn clone(&self) -> Self {
154        Self {
155            channel: self.channel.clone(),
156            station: self.station,
157            selector: self.selector.clone(),
158            _marker: PhantomData,
159        }
160    }
161}
162
163impl<Value, SliceValue, Channel> PartialEq for RadioSliceMut<Value, SliceValue, Channel>
164where
165    Channel: RadioChannel<Value>,
166    SliceValue: 'static,
167{
168    fn eq(&self, other: &Self) -> bool {
169        self.channel == other.channel
170    }
171}
172
173impl<Value, SliceValue, Channel> RadioSliceMut<Value, SliceValue, Channel>
174where
175    Channel: RadioChannel<Value>,
176    SliceValue: 'static,
177{
178    pub(crate) fn new(
179        channel: Channel,
180        station: RadioStation<Value, Channel>,
181        selector: impl Fn(&mut Value) -> &mut SliceValue + 'static,
182    ) -> RadioSliceMut<Value, SliceValue, Channel> {
183        RadioSliceMut {
184            channel,
185            station,
186            selector: Rc::new(selector),
187            _marker: PhantomData,
188        }
189    }
190
191    pub(crate) fn subscribe_if_not(&self) {
192        if let Some(rc) = ReactiveContext::try_current() {
193            let is_listening = self.station.is_listening(&self.channel, &rc);
194
195            if !is_listening {
196                self.station.listen(self.channel.clone(), rc);
197            }
198        }
199    }
200
201    /// Read the slice value and subscribe to changes.
202    #[allow(invalid_reference_casting)]
203    pub fn read(&'_ self) -> ReadRef<'_, SliceValue> {
204        self.subscribe_if_not();
205        self.peek()
206    }
207
208    /// Read the slice value and subscribe to changes, with 'static lifetime.
209    #[allow(invalid_reference_casting)]
210    pub fn read_unchecked(&'_ self) -> ReadRef<'static, SliceValue> {
211        self.subscribe_if_not();
212        self.peek_unchecked()
213    }
214
215    /// Read the slice value without subscribing.
216    #[allow(invalid_reference_casting)]
217    pub fn peek(&'_ self) -> ReadRef<'_, SliceValue> {
218        self.peek_unchecked()
219    }
220
221    /// Read the slice value without subscribing, with 'static lifetime.
222    #[allow(invalid_reference_casting)]
223    pub fn peek_unchecked(&'_ self) -> ReadRef<'static, SliceValue> {
224        let inner = self.station.peek_unchecked();
225        inner.map(|v| {
226            Ref::map(v, |v| {
227                (self.selector)(unsafe { &mut *(v as *const Value as *mut Value) })
228            })
229        })
230    }
231
232    /// Write the slice value, with 'static lifetime.
233    pub fn write_unchecked(&'_ self) -> WriteRef<'static, SliceValue> {
234        self.notify();
235        self.write_unchecked_no_notify()
236    }
237
238    /// Write the slice value without notifying.
239    pub fn write_unchecked_no_notify(&'_ self) -> WriteRef<'static, SliceValue> {
240        let value = self.station.value.write_unchecked();
241        let selector = self.selector.clone();
242        value.map(|v| RefMut::map(v, |v| selector(v)))
243    }
244
245    /// Notify listeners for this slice's channel.
246    pub fn notify(&self) {
247        let value = self.station.peek_unchecked();
248        for channel in self.channel.clone().derive_channel(&value) {
249            self.station.notify_listeners(&channel)
250        }
251        self.station.cleanup();
252    }
253
254    /// Write the slice value.
255    pub fn write(&'_ mut self) -> WriteRef<'_, SliceValue> {
256        self.write_unchecked()
257    }
258}
259
260impl<Value, Channel> Radio<Value, Channel>
261where
262    Channel: RadioChannel<Value>,
263    Value: 'static,
264{
265    /// Create a read-only slice of a specific portion of the state.
266    ///
267    /// # Example
268    ///
269    /// ```rust, ignore
270    /// let count_slice = radio.slice(AppChannel::Count, |state| &state.count);
271    /// ```
272    pub fn slice<SliceValue>(
273        &self,
274        channel: Channel,
275        selector: impl Fn(&Value) -> &SliceValue + 'static,
276    ) -> RadioSlice<Value, SliceValue, Channel> {
277        let station = self.antenna.peek().station;
278        RadioSlice::new(channel, station, selector)
279    }
280
281    /// Create a read-only slice using the current radio's channel.
282    ///
283    /// # Example
284    ///
285    /// ```rust, ignore
286    /// let count_slice = radio.slice_current(|state| &state.count);
287    /// ```
288    pub fn slice_current<SliceValue>(
289        &self,
290        selector: impl Fn(&Value) -> &SliceValue + 'static,
291    ) -> RadioSlice<Value, SliceValue, Channel> {
292        let channel = self.antenna.peek().channel.clone();
293        self.slice(channel, selector)
294    }
295
296    /// Create a mutable slice of a specific portion of the state.
297    ///
298    /// # Example
299    ///
300    /// ```rust, ignore
301    /// let mut count_slice = radio.slice_mut(AppChannel::Count, |state| &mut state.count);
302    /// ```
303    pub fn slice_mut<SliceValue>(
304        &self,
305        channel: Channel,
306        selector: impl Fn(&mut Value) -> &mut SliceValue + 'static,
307    ) -> RadioSliceMut<Value, SliceValue, Channel> {
308        let station = self.antenna.peek().station;
309        RadioSliceMut::new(channel, station, selector)
310    }
311
312    /// Create a mutable slice using the current radio's channel.
313    ///
314    /// # Example
315    ///
316    /// ```rust, ignore
317    /// let mut count_slice = radio.slice_mut_current(|state| &mut state.count);
318    /// ```
319    pub fn slice_mut_current<SliceValue>(
320        &self,
321        selector: impl Fn(&mut Value) -> &mut SliceValue + 'static,
322    ) -> RadioSliceMut<Value, SliceValue, Channel> {
323        let channel = self.antenna.peek().channel.clone();
324        self.slice_mut(channel, selector)
325    }
326}