reactive_graph/
trait_options.rs

1use crate::{
2    traits::{
3        DefinedAt, Get, GetUntracked, Read, ReadUntracked, Track, With,
4        WithUntracked,
5    },
6    unwrap_signal,
7};
8use std::panic::Location;
9
10impl<T> DefinedAt for Option<T>
11where
12    T: DefinedAt,
13{
14    fn defined_at(&self) -> Option<&'static Location<'static>> {
15        self.as_ref().map(DefinedAt::defined_at).unwrap_or(None)
16    }
17}
18
19impl<T> Track for Option<T>
20where
21    T: Track,
22{
23    fn track(&self) {
24        if let Some(signal) = self {
25            signal.track();
26        }
27    }
28}
29
30/// An alternative [`ReadUntracked`](crate) trait that works with `Option<Readable>` types.
31pub trait ReadUntrackedOptional: Sized + DefinedAt {
32    /// The guard type that will be returned, which can be dereferenced to the value.
33    type Value;
34
35    /// Returns the guard, or `None` if the signal has already been disposed.
36    #[track_caller]
37    fn try_read_untracked(&self) -> Option<Self::Value>;
38
39    /// Returns the guard.
40    ///
41    /// # Panics
42    /// Panics if you try to access a signal that has been disposed.
43    #[track_caller]
44    fn read_untracked(&self) -> Self::Value {
45        self.try_read_untracked()
46            .unwrap_or_else(unwrap_signal!(self))
47    }
48}
49
50impl<T> ReadUntrackedOptional for Option<T>
51where
52    Self: DefinedAt,
53    T: ReadUntracked,
54{
55    type Value = Option<<T as ReadUntracked>::Value>;
56
57    fn try_read_untracked(&self) -> Option<Self::Value> {
58        Some(if let Some(signal) = self {
59            Some(signal.try_read_untracked()?)
60        } else {
61            None
62        })
63    }
64}
65
66/// An alternative [`Read`](crate) trait that works with `Option<Readable>` types.
67pub trait ReadOptional: DefinedAt {
68    /// The guard type that will be returned, which can be dereferenced to the value.
69    type Value;
70
71    /// Subscribes to the signal, and returns the guard, or `None` if the signal has already been disposed.
72    #[track_caller]
73    fn try_read(&self) -> Option<Self::Value>;
74
75    /// Subscribes to the signal, and returns the guard.
76    ///
77    /// # Panics
78    /// Panics if you try to access a signal that has been disposed.
79    #[track_caller]
80    fn read(&self) -> Self::Value {
81        self.try_read().unwrap_or_else(unwrap_signal!(self))
82    }
83}
84
85impl<T> ReadOptional for Option<T>
86where
87    Self: DefinedAt,
88    T: Read,
89{
90    type Value = Option<<T as Read>::Value>;
91
92    fn try_read(&self) -> Option<Self::Value> {
93        Some(if let Some(readable) = self {
94            Some(readable.try_read()?)
95        } else {
96            None
97        })
98    }
99}
100
101/// An alternative [`WithUntracked`](crate) trait that works with `Option<Withable>` types.
102pub trait WithUntrackedOptional: DefinedAt {
103    /// The type of the value contained in the signal.
104    type Value: ?Sized;
105
106    /// Applies the closure to the value, and returns the result,
107    /// or `None` if the signal has already been disposed.
108    #[track_caller]
109    fn try_with_untracked<U>(
110        &self,
111        fun: impl FnOnce(Option<&Self::Value>) -> U,
112    ) -> Option<U>;
113
114    /// Applies the closure to the value, and returns the result.
115    ///
116    /// # Panics
117    /// Panics if you try to access a signal that has been disposed.
118    #[track_caller]
119    fn with_untracked<U>(
120        &self,
121        fun: impl FnOnce(Option<&Self::Value>) -> U,
122    ) -> U {
123        self.try_with_untracked(fun)
124            .unwrap_or_else(unwrap_signal!(self))
125    }
126}
127
128impl<T> WithUntrackedOptional for Option<T>
129where
130    Self: DefinedAt,
131    T: WithUntracked,
132    <T as WithUntracked>::Value: Sized,
133{
134    type Value = <T as WithUntracked>::Value;
135
136    fn try_with_untracked<U>(
137        &self,
138        fun: impl FnOnce(Option<&Self::Value>) -> U,
139    ) -> Option<U> {
140        if let Some(signal) = self {
141            Some(signal.try_with_untracked(|val| fun(Some(val)))?)
142        } else {
143            Some(fun(None))
144        }
145    }
146}
147
148/// An alternative [`With`](crate) trait that works with `Option<Withable>` types.
149pub trait WithOptional: DefinedAt {
150    /// The type of the value contained in the signal.
151    type Value: ?Sized;
152
153    /// Subscribes to the signal, applies the closure to the value, and returns the result,
154    /// or `None` if the signal has already been disposed.
155    #[track_caller]
156    fn try_with<U>(
157        &self,
158        fun: impl FnOnce(Option<&Self::Value>) -> U,
159    ) -> Option<U>;
160
161    /// Subscribes to the signal, applies the closure to the value, and returns the result.
162    ///
163    /// # Panics
164    /// Panics if you try to access a signal that has been disposed.
165    #[track_caller]
166    fn with<U>(&self, fun: impl FnOnce(Option<&Self::Value>) -> U) -> U {
167        self.try_with(fun).unwrap_or_else(unwrap_signal!(self))
168    }
169}
170
171impl<T> WithOptional for Option<T>
172where
173    Self: DefinedAt,
174    T: With,
175    <T as With>::Value: Sized,
176{
177    type Value = <T as With>::Value;
178
179    fn try_with<U>(
180        &self,
181        fun: impl FnOnce(Option<&Self::Value>) -> U,
182    ) -> Option<U> {
183        if let Some(signal) = self {
184            Some(signal.try_with(|val| fun(Some(val)))?)
185        } else {
186            Some(fun(None))
187        }
188    }
189}
190
191impl<T> GetUntracked for Option<T>
192where
193    Self: DefinedAt,
194    T: GetUntracked,
195{
196    type Value = Option<<T as GetUntracked>::Value>;
197
198    fn try_get_untracked(&self) -> Option<Self::Value> {
199        Some(if let Some(signal) = self {
200            Some(signal.try_get_untracked()?)
201        } else {
202            None
203        })
204    }
205}
206
207impl<T> Get for Option<T>
208where
209    Self: DefinedAt,
210    T: Get,
211{
212    type Value = Option<<T as Get>::Value>;
213
214    fn try_get(&self) -> Option<Self::Value> {
215        Some(if let Some(signal) = self {
216            Some(signal.try_get()?)
217        } else {
218            None
219        })
220    }
221}
222
223/// Helper trait to implement flatten() on `Option<&Option<T>>`.
224pub trait FlattenOptionRefOption {
225    /// The type of the value contained in the double option.
226    type Value;
227
228    /// Converts from `Option<&Option<T>>` to `Option<&T>`.
229    fn flatten(&self) -> Option<&Self::Value>;
230}
231
232impl<'a, T> FlattenOptionRefOption for Option<&'a Option<T>> {
233    type Value = T;
234
235    fn flatten(&self) -> Option<&'a T> {
236        self.map(Option::as_ref).flatten()
237    }
238}