rx_rs/core/rx_ref.rs
1use super::rx_observable::RxObservable;
2use super::rx_subject::RxSubject;
3use super::rx_val::RxVal;
4
5/// A read-write reactive value that holds a current state.
6///
7/// RxRef provides both read and write access to a reactive value. It exposes
8/// a read-only RxVal via the `.val()` method, and allows updating the value
9/// via the `.set()` method.
10///
11/// When the value is set, all subscribers to the RxVal are notified.
12///
13/// # Example
14/// ```
15/// use rx_rs::core::{RxRef, DisposableTracker};
16///
17/// let mut tracker = DisposableTracker::new();
18/// let client_id = RxRef::new(None);
19///
20/// // Subscribe to changes
21/// client_id.val().subscribe(tracker.tracker(), |maybe_id| {
22/// match maybe_id {
23/// Some(id) => println!("Client ID assigned: {}", id),
24/// None => println!("No client ID yet"),
25/// }
26/// }); // Prints "No client ID yet" immediately
27///
28/// // Update the value
29/// client_id.set(Some(42)); // Prints "Client ID assigned: 42"
30/// ```
31#[derive(Clone)]
32pub struct RxRef<T> {
33 inner: RxVal<T>,
34}
35
36impl<T: 'static> RxRef<T>
37where
38 T: Clone + PartialEq,
39{
40 /// Creates a new RxRef with the given initial value.
41 ///
42 /// # Arguments
43 /// * `value` - The initial value
44 ///
45 /// # Example
46 /// ```
47 /// use rx_rs::core::RxRef;
48 ///
49 /// let connection_state = RxRef::new("Disconnected");
50 /// ```
51 pub fn new(value: T) -> Self {
52 Self {
53 inner: RxVal::new(value),
54 }
55 }
56
57 /// Sets a new value and notifies all subscribers.
58 ///
59 /// All subscribers to the RxVal obtained via `.val()` will be called
60 /// with the new value.
61 ///
62 /// # Arguments
63 /// * `value` - The new value to set
64 ///
65 /// # Example
66 /// ```
67 /// use rx_rs::core::RxRef;
68 ///
69 /// let state = RxRef::new(0);
70 /// state.set(42);
71 /// assert_eq!(state.get(), 42);
72 /// ```
73 pub fn set(&self, value: T) {
74 self.inner.update(value);
75 }
76
77 /// Gets the current value.
78 ///
79 /// This is a convenience method that delegates to the underlying RxVal.
80 ///
81 /// # Example
82 /// ```
83 /// use rx_rs::core::RxRef;
84 ///
85 /// let counter = RxRef::new(5);
86 /// assert_eq!(counter.get(), 5);
87 /// ```
88 pub fn get(&self) -> T {
89 self.inner.get()
90 }
91
92 /// Returns a read-only view of this reactive value.
93 ///
94 /// The returned RxVal can be cloned and passed around, allowing multiple
95 /// parts of the code to subscribe to changes without having write access.
96 ///
97 /// # Example
98 /// ```
99 /// use rx_rs::core::{RxRef, DisposableTracker};
100 ///
101 /// let mut tracker = DisposableTracker::new();
102 /// let state = RxRef::new("initial");
103 /// let read_only = state.val();
104 ///
105 /// read_only.subscribe(tracker.tracker(), |val| {
106 /// println!("State: {}", val);
107 /// });
108 /// ```
109 pub fn val(&self) -> RxVal<T> {
110 self.inner.clone()
111 }
112
113 /// Modifies the value using a closure and notifies subscribers.
114 ///
115 /// This is useful when you need to update the value based on its current
116 /// state, without having to clone it first.
117 ///
118 /// # Arguments
119 /// * `f` - Function that receives a mutable reference to the current value
120 ///
121 /// # Example
122 /// ```
123 /// use rx_rs::core::RxRef;
124 ///
125 /// let counter = RxRef::new(0);
126 /// counter.modify(|count| *count += 1);
127 /// assert_eq!(counter.get(), 1);
128 /// ```
129 pub fn modify<F>(&self, f: F)
130 where
131 F: FnOnce(&mut T),
132 {
133 let mut current = self.get();
134 f(&mut current);
135 self.set(current);
136 }
137
138 /// Returns the number of active subscribers.
139 pub fn subscriber_count(&self) -> usize {
140 self.inner.subscriber_count()
141 }
142
143 /// Returns a pointer address for debugging identity.
144 /// Use this to check if two RxRef instances share the same underlying data.
145 pub fn debug_ptr(&self) -> usize {
146 self.inner.debug_ptr()
147 }
148
149 /// Converts this RxRef into a stream (RxObservable).
150 ///
151 /// The returned observable emits the current value immediately on subscription,
152 /// and then emits all future changes.
153 ///
154 /// This is a convenience method that delegates to the underlying RxVal's `.stream()`.
155 ///
156 /// # Example
157 /// ```
158 /// use rx_rs::core::{RxRef, DisposableTracker};
159 ///
160 /// let mut tracker = DisposableTracker::new();
161 /// let state = RxRef::new(0);
162 /// let stream = state.stream();
163 ///
164 /// stream.subscribe(tracker.tracker(), |value| {
165 /// println!("State: {}", value);
166 /// }); // Prints "State: 0" immediately
167 ///
168 /// state.set(42); // Prints "State: 42"
169 /// ```
170 pub fn stream(&self) -> RxObservable<T> {
171 self.inner.stream()
172 }
173
174 /// Maps the values of this RxRef using a transformation function.
175 ///
176 /// Returns a new RxVal that always contains the transformed value.
177 /// When the source RxRef changes, the transformation is applied and
178 /// the resulting RxVal is updated.
179 ///
180 /// # Arguments
181 /// * `f` - Function to transform values from A to B
182 ///
183 /// # Example
184 /// ```
185 /// use rx_rs::core::RxRef;
186 ///
187 /// let number = RxRef::new(5);
188 /// let doubled = number.map(|x| x * 2);
189 ///
190 /// assert_eq!(doubled.get(), 10);
191 ///
192 /// number.set(10);
193 /// assert_eq!(doubled.get(), 20);
194 /// ```
195 pub fn map<B, F>(&self, f: F) -> RxVal<B>
196 where
197 B: Clone + PartialEq + 'static,
198 F: Fn(&T) -> B + 'static,
199 {
200 self.inner.map(f)
201 }
202
203 /// Flat-maps the values of this RxRef using a function that returns RxVal<B>.
204 ///
205 /// When the source RxRef changes, the function is called to produce a new RxVal<B>,
206 /// and the result RxVal is updated to reflect the current value of that inner RxVal.
207 /// The result also updates when the inner RxVal changes.
208 ///
209 /// This is a convenience method that delegates to the underlying RxVal's `.flat_map()`.
210 ///
211 /// # Arguments
212 /// * `f` - Function to transform values from A to RxVal<B>
213 ///
214 /// # Example
215 /// ```
216 /// use rx_rs::core::RxRef;
217 ///
218 /// let outer = RxRef::new(1);
219 /// let inner1 = RxRef::new(10);
220 /// let inner2 = RxRef::new(20);
221 ///
222 /// let inner1_clone = inner1.clone();
223 /// let inner2_clone = inner2.clone();
224 ///
225 /// let flattened = outer.flat_map(move |&x| {
226 /// if x == 1 { inner1_clone.val() } else { inner2_clone.val() }
227 /// });
228 ///
229 /// assert_eq!(flattened.get(), 10);
230 ///
231 /// outer.set(2);
232 /// assert_eq!(flattened.get(), 20);
233 /// ```
234 pub fn flat_map<B, F>(&self, f: F) -> RxVal<B>
235 where
236 B: Clone + PartialEq + 'static,
237 F: Fn(&T) -> RxVal<B> + 'static,
238 {
239 self.inner.flat_map(f)
240 }
241
242 /// Flat-maps using a function that returns RxRef<B>.
243 pub fn flat_map_ref<B, F>(&self, f: F) -> RxVal<B>
244 where
245 B: Clone + PartialEq + 'static,
246 F: Fn(&T) -> RxRef<B> + 'static,
247 {
248 self.inner.flat_map_ref(f)
249 }
250
251 /// Flat-maps using a function that returns RxObservable<B>.
252 pub fn flat_map_observable<B, F>(&self, f: F) -> RxObservable<B>
253 where
254 B: Clone + 'static,
255 F: Fn(&T) -> RxObservable<B> + 'static,
256 {
257 self.inner.flat_map_observable(f)
258 }
259
260 /// Flat-maps using a function that returns RxSubject<B>.
261 pub fn flat_map_subject<B, F>(&self, f: F) -> RxObservable<B>
262 where
263 B: Clone + 'static,
264 F: Fn(&T) -> RxSubject<B> + 'static,
265 {
266 self.inner.flat_map_subject(f)
267 }
268
269 /// Combines this RxRef with another RxVal.
270 pub fn zip_val<U>(&self, other: RxVal<U>) -> RxVal<(T, U)>
271 where
272 U: Clone + PartialEq + 'static,
273 {
274 self.inner.zip_val(other)
275 }
276
277 /// Combines this RxRef with another RxRef.
278 pub fn zip_ref<U>(&self, other: RxRef<U>) -> RxVal<(T, U)>
279 where
280 U: Clone + PartialEq + 'static,
281 {
282 self.inner.zip_ref(other)
283 }
284}