x_bow/path_ext.rs
1pub mod bind_for_each;
2pub mod for_each;
3pub mod for_each_async;
4pub mod signal_stream;
5
6use std::cell::{Ref, RefMut};
7
8use futures_core::{Future, Stream};
9
10use crate::{
11 borrow_mut_guard::BorrowMutGuard, until_change::UntilChange, Path, ReferencePath, Trackable,
12};
13
14/// An extension trait with methods for [Path].
15///
16/// Most of the time, you'll be working with methods from this trait
17/// (rather than the ones from [Path]).
18///
19/// This trait is implemented for every type that implements [Path].
20pub trait PathExt: Path {
21 /// Borrow the data at this path immutably.
22 ///
23 /// Returns None if the data identified by this path does not exist at
24 /// the moment. For example...
25 /// * The path might point to data in an enum variant,
26 /// but the enum might be in a different variant.
27 /// * The path might point to data associated with a specific key in
28 /// a HashMap, but the HashMap might not have data at that key.
29 ///
30 /// If there is an existing mutable borrow anywhere in the
31 /// store, this method will panic.
32 ///
33 /// See also: [borrow][crate::PathExtGuaranteed::borrow] - like this method,
34 /// but for cases where we know None won't be returned.
35 /// ```
36 /// # use x_bow::{Store, PathExt, Trackable};
37 /// #[derive(Trackable)]
38 /// #[track(deep)]
39 /// enum MyEnum {
40 /// Variant1(i32),
41 /// Variant2(String)
42 /// }
43 /// let store = Store::new(MyEnum::Variant1(5));
44 ///
45 /// let path_to_var1 = store.build_path().Variant1_0();
46 /// // Borrow the `i32` inside the first enum variant.
47 /// assert_eq!(path_to_var1.borrow_opt().as_deref(), Some(&5));
48 ///
49 /// let path_to_var2 = store.build_path().Variant2_0();
50 /// // Can't borrow the `String`; the enum is in another variant.
51 /// assert!(path_to_var2.borrow_opt().is_none());
52 /// ```
53 ///
54 /// #### Time Complexity
55 /// O(L) where L is the length of this path
56 fn borrow_opt(&self) -> Option<Ref<'_, <Self as Path>::Out>> {
57 self.path_borrow()
58 }
59
60 /// Borrow the data at this path mutably, notifying all the relevant
61 /// change listeners when the returned borrow guard is dropped.
62 ///
63 /// Returns None if the data identified by this path does not exist at
64 /// the moment. For example...
65 /// * The path might point to data in an enum variant,
66 /// but the enum might be in a different variant.
67 /// * The path might point to data associated with a specific key in
68 /// a HashMap, but the HashMap might not have data at that key.
69 ///
70 /// If there is an existing mutable or immutable borrow anywhere in the
71 /// store, this method will panic.
72 ///
73 /// See also: [borrow_mut][crate::PathExtGuaranteed::borrow_mut] -
74 /// like this method, but for cases where we know None won't be returned.
75 ///
76 /// ```
77 /// # use x_bow::{Store, PathExt, Trackable};
78 /// #[derive(Trackable)]
79 /// #[track(deep)]
80 /// enum MyEnum {
81 /// Variant1(i32),
82 /// Variant2(String)
83 /// }
84 /// let store = Store::new(MyEnum::Variant1(5));
85 ///
86 /// let path_to_var1 = store.build_path().Variant1_0();
87 /// // The enum is in the first variant so we are sure we can borrow
88 /// // and mutate the `i32`.
89 /// *path_to_var1.borrow_opt_mut().unwrap() += 1;
90 /// assert_eq!(path_to_var1.borrow_opt().as_deref(), Some(&6));
91 ///
92 /// let path_to_var2 = store.build_path().Variant2_0();
93 /// assert!(path_to_var2.borrow_opt_mut().is_none());
94 /// ```
95 ///
96 /// #### Time Complexity
97 /// O(L + D_1 + D_2 + ... + D_N)
98 /// where L is the length of this path
99 /// and D_i is the length of the path of the ith subscriber
100 /// (N is the number of subscribers that will be woken by the mutation,
101 /// **not** the total number of subscribers in the store)
102 fn borrow_opt_mut(&self) -> Option<BorrowMutGuard<'_, Self>> {
103 self.path_borrow_mut()
104 .map(|inner| BorrowMutGuard::new(inner, self.store_wakers(), self))
105 }
106
107 /// Borrow the data at this path mutably **without notifying** any listener.
108 ///
109 /// Use this in conjunction with the [notify_changed][PathExt::notify_changed] method.
110 fn borrow_opt_mut_without_notifying(&self) -> Option<RefMut<'_, <Self as Path>::Out>> {
111 self.path_borrow_mut()
112 }
113
114 /// Notify all listeners that the data at this location has changed.
115 ///
116 /// This method is not needed when using [borrow_opt_mut][PathExt::borrow_opt_mut] or
117 /// [borrow_mut][crate::PathExtGuaranteed::borrow_mut]; in those cases
118 /// notifications are sent automatically.
119 ///
120 /// This method is useful in the relatively rare situations when you need
121 /// [borrow_opt_mut_without_notifying][PathExt::borrow_opt_mut_without_notifying].
122 ///
123 /// #### Time Complexity
124 /// Same as [borrow_opt_mut][PathExt::borrow_opt_mut].
125 fn notify_changed(&self) {
126 crate::borrow_mut_guard::notify(self.store_wakers(), self);
127 }
128
129 /// Clone the data identified by this path.
130 ///
131 /// Equivalent to `path.borrow_opt().as_deref().map(Clone::clone)`.
132 fn get_opt(&self) -> Option<Self::Out>
133 where
134 Self::Out: Clone,
135 {
136 self.borrow_opt().as_deref().map(Clone::clone)
137 }
138
139 /// Set the data identified by this path, notifying listeners.
140 ///
141 /// If the data cannot be accessed, Err is returned with the input.
142 fn set_opt(&self, data: Self::Out) -> Result<(), Self::Out>
143 where
144 Self::Out: Sized,
145 {
146 match self.borrow_opt_mut().as_deref_mut() {
147 Some(state) => {
148 *state = data;
149 Ok(())
150 }
151 None => Err(data),
152 }
153 }
154
155 /// Get a [Stream][futures_core::Stream] that fires everytime a mutable
156 /// borrow is taken of this or any encompassing piece of data.
157 ///
158 /// In other words, whenever someone call [borrow_opt_mut][Self::borrow_opt_mut]
159 /// or [borrow_mut][crate::PathExtGuaranteed::borrow_mut] on this path
160 /// (the same one you're calling `until_change` on) or any path that is a
161 /// prefix of this one, the stream will fire.
162 ///
163 /// ```
164 /// # use x_bow::{Trackable, Store, PathExt};
165 /// #[derive(Default, Trackable)]
166 /// struct MyStruct {
167 /// field_1: i32,
168 /// field_2: u64
169 /// }
170 /// let store = Store::new(MyStruct::default());
171 ///
172 /// // path to the `MyStruct` itself
173 /// let path = store.build_path();
174 ///
175 /// let stream = path.until_change();
176 ///
177 /// path.borrow_opt_mut(); // will fire the stream
178 /// path.field_1().borrow_opt_mut(); // will fire the stream
179 /// path.field_2().borrow_opt_mut(); // won't fire the stream
180 /// ```
181 ///
182 /// ### Stream Behavior
183 /// #### Spurious Fires
184 /// The stream may fire spuriously, although the chance of this happening
185 /// is [extremely low][crate#hash-collision].
186 ///
187 /// #### Batching
188 /// If multiple changes happen in quick succession
189 /// ("quick succession" means in-between calls to [Stream::poll_next], to
190 /// be precise), the stream may only fire once.
191 ///
192 /// This means the stream can be used for detecting changes,
193 /// but **not** for counting how many changes happened.
194 ///
195 /// #### Time Complexity
196 /// On creation and each [poll][futures_core::stream::Stream::poll_next]:
197 /// O(L) where L is the length of this path
198 #[must_use = "the returned Stream is lazy; poll it or use StreamExt on it"]
199 fn until_change(&self) -> UntilChange<'_> {
200 UntilChange::new(self.store_wakers(), self)
201 }
202
203 /// Get a [Stream][futures_core::Stream] that fires once
204 /// every time the data changes, yielding [Ref]s to the data.
205 ///
206 /// If the `fire_immediately` argument is `true`, then the stream will fire
207 /// on the first poll too.
208 ///
209 /// The stream ends when the data cannot be borrowed (when [borrow_opt][Self::borrow_opt]
210 /// returns None).
211 ///
212 /// This stream is useful for linking the data to some side-effect.
213 /// For example, you can sync a UI widget content with the data...
214 /// ```
215 /// use futures_lite::StreamExt;
216 /// # use x_bow::{Path, PathExt};
217 /// # use std::cell::Ref;
218 /// # async fn example(path: &impl Path<Out = String>) {
219 /// # struct ui_element;
220 /// # impl ui_element {
221 /// # fn set_text(&self, t: &str) {}
222 /// # }
223 /// // set the text with the current data
224 /// // and update the text whenever the data change
225 /// path.signal_stream(true).for_each(|data: Ref<'_, String>| {
226 /// let s: &str = &**data;
227 /// ui_element.set_text(s);
228 /// }).await;
229 /// # }
230 /// ```
231 /// **Note** that if this is all you need, use [for_each][PathExt::for_each]
232 /// instead.
233 ///
234 /// ### Panic Opportunity
235 /// This method returns a [Ref]. It is ‼️**extremely important**‼️ that you
236 /// use the Ref and drop it as soon as possible. While the Ref is held,
237 /// any attempt to mutably borrow any data in the store will **panic**.
238 /// Holding the Ref for a long time (especially across a `await` points)
239 /// is thus a very bad idea.
240 ///
241 /// ### Equivalent Behavior with Streams
242 /// This method is merely for convenience. You can achieve the same
243 /// functionality yourself like so...
244 /// ```
245 /// # use futures_lite::StreamExt;
246 /// # use x_bow::{Path, PathExt};
247 /// # use std::cell::Ref;
248 /// # async fn example(path: &impl Path) {
249 /// let stream = path.signal_stream(true);
250 /// // is equivalent to
251 /// let stream = futures_lite::stream::once(()) // fire immediately in the beginning...
252 /// .chain(path.until_change()) // and after every change
253 /// .map(|_| path.borrow_opt()) // borrow the data into a Ref
254 /// .take_while(Option::is_some) // end the stream if the data is unavailable
255 /// .map(Option::unwrap); // we've already checked that the data isn't none
256 /// # }
257 /// ```
258 #[must_use = "the returned Stream is lazy; poll it or use StreamExt on it"]
259 fn signal_stream(&self, fire_immediately: bool) -> signal_stream::SignalStream<'_, Self> {
260 signal_stream::SignalStream::new(self, self.until_change(), fire_immediately)
261 }
262
263 /// Execute the given function with the data as argument. Repeat every time
264 /// the data changes.
265 ///
266 /// The return future never finishes.
267 ///
268 /// ```
269 /// # use x_bow::{Path, PathExt};
270 /// # async fn example(path: &impl Path<Out = String>) {
271 /// # struct ui_element;
272 /// # impl ui_element {
273 /// # fn set_text(&self, t: &str) {}
274 /// # }
275 /// // set the text with the current data
276 /// // and update the text whenever the data change
277 /// path.for_each(|s: &String| {
278 /// ui_element.set_text(s);
279 /// }).await;
280 /// # }
281 /// ```
282 ///
283 /// This is a less powerful (and less panic-inducing) alternative to the
284 /// [signal_stream][PathExt::signal_stream] method.
285 #[must_use = "the returned Future is lazy; await it to make it do work"]
286 fn for_each<C: FnMut(&Self::Out)>(&self, closure: C) -> for_each::ForEach<'_, Self, C> {
287 for_each::ForEach::new(self, self.until_change(), closure)
288 }
289
290 /// Execute the given async function (a function returning Future)
291 /// with the data as argument. Repeat every time the data changes.
292 ///
293 /// If the async function is still executing when the data changes,
294 /// the execution is canceled (by dropping the Future) so that the new
295 /// one can start.
296 ///
297 /// The return future never finishes.
298 ///
299 /// ```
300 /// # use x_bow::{Path, PathExt};
301 /// # async fn example(path_to_url: &impl Path<Out = String>) {
302 /// # struct ui_element;
303 /// # impl ui_element {
304 /// # fn set_text(&self, t: &str) {}
305 /// # }
306 /// # async fn fetch_content(url: &String) -> String {
307 /// # todo!();
308 /// # }
309 /// // when the URL changes, fetch the content and set the text
310 /// path_to_url.for_each_async(|url: &String| {
311 /// let url = url.clone();
312 /// async move {
313 /// let content = fetch_content(&url).await;
314 /// ui_element.set_text(&content);
315 /// }
316 /// }).await;
317 /// # }
318 /// ```
319 #[must_use = "the returned Future is lazy; await it to make it do work"]
320 fn for_each_async<F: Future<Output = ()>, C: FnMut(&Self::Out) -> F>(
321 &self,
322 closure: C,
323 ) -> for_each_async::ForEachAsync<'_, Self, F, C> {
324 for_each_async::ForEachAsync::new(self, self.until_change(), closure)
325 }
326
327 /// For making a two-way binding.
328 ///
329 /// The given `on_change` function is called once in the beginning and
330 /// whenever the data changes (just like with [for_each][PathExt::for_each]).
331 ///
332 /// Whenever the given `incoming_changes` [Stream] yield a value,
333 /// the data will be updated to that value. The `on_change` function
334 /// won't be called for changes that come in this way.
335 ///
336 /// The future finishes when the `incoming_changes` stream yield None.
337 ///
338 /// ```
339 /// # use x_bow::{Path, PathExt};
340 /// # use futures_lite::stream::{Stream, StreamExt, once};
341 /// # async fn example(path: &impl Path<Out = String>) {
342 /// # struct text_field_widget;
343 /// # impl text_field_widget {
344 /// # fn set_text(&self, t: &str) {}
345 /// # fn until_change(&self) -> impl Stream<Item = ()> {once(())}
346 /// # fn get_text(&self) -> String {todo!()}
347 /// # }
348 /// path.bind_for_each(
349 /// // set the text with the current data
350 /// // and update the text whenever the data change
351 /// |s: &String| {
352 /// text_field_widget.set_text(s);
353 /// },
354 /// // whenever the user type into the field, update the data
355 /// text_field_widget
356 /// .until_change()
357 /// .map(|_| text_field_widget.get_text())
358 /// ).await;
359 /// # }
360 /// ```
361 #[must_use = "the returned Future is lazy; await it to make it do work"]
362 fn bind_for_each<C: FnMut(&Self::Out), I: Stream<Item = Self::Out>>(
363 &self,
364 on_change: C,
365 incoming_changes: I,
366 ) -> bind_for_each::BindForEach<'_, Self, C, I> {
367 bind_for_each::BindForEach::new(self, self.until_change(), on_change, incoming_changes)
368 }
369
370 /// Get a [Stream][futures_core::Stream] that fires everytime a mutable
371 /// borrow is taken of this piece of data or anything inside it.
372 ///
373 /// In other words, whenever someone call [borrow_opt_mut][PathExt::borrow_opt_mut]
374 /// or [borrow_mut][crate::PathExtGuaranteed::borrow_mut] on this path
375 /// (the same one you're calling `until_bubbling_change` on) or any path that this
376 /// path is a prefix of, the stream will fire.
377 ///
378 /// ```
379 /// # use x_bow::{Trackable, Store, PathExt};
380 /// #[derive(Trackable)]
381 /// #[track(deep)]
382 /// struct MyStruct<T> {
383 /// field_1: T,
384 /// field_2: u64
385 /// }
386 /// let store = Store::new(MyStruct {
387 /// field_1: MyStruct {
388 /// field_1: String::new(),
389 /// field_2: 123
390 /// },
391 /// field_2: 456
392 /// });
393 ///
394 /// // path to the root `MyStruct` itself.
395 /// let root = store.build_path();
396 ///
397 /// // path to the `field_1` in the root `MyStruct`.
398 /// let listening = root.field_1();
399 /// let stream = listening.until_bubbling_change();
400 ///
401 /// root.field_1().borrow_opt_mut(); // will fire the stream
402 /// root.field_1().field_1().borrow_opt_mut(); // will fire the stream
403 /// root.field_1().field_2().borrow_opt_mut(); // will fire the stream
404 /// root.borrow_opt_mut(); // won't fire the stream
405 /// root.field_2().borrow_opt_mut(); // won't fire the stream
406 /// ```
407 ///
408 /// ### Stream Behavior
409 /// The returned stream may spuriously fire or batch changes.
410 /// See [until_change][PathExt::until_change] documentation.
411 ///
412 /// #### Time Complexity
413 /// On creation:
414 /// O(L) where L is the length of this path
415 ///
416 /// On each poll:
417 /// O(1)
418 #[must_use = "the returned Stream is lazy; poll it or use StreamExt on it"]
419 fn until_bubbling_change(&self) -> UntilChange<'_> {
420 UntilChange::new_bubbling(self.store_wakers(), self)
421 }
422
423 /// Gives a [PathBuilder][crate::Trackable::PathBuilder] for creating a
424 /// path that continues from this path.
425 ///
426 /// This is useful when you are handling the type that implements `Path`
427 /// directly. Most of the time, though, you will already be working with
428 /// `PathBuilder`s.
429 ///
430 /// ```
431 /// # use x_bow::{Path, PathExt, PathExtGuaranteed, Store, Trackable, IntoPath};
432 /// fn modify_string(path: impl Path<Out = String>) {
433 /// path.set_opt("haha".to_string()).ok();
434 /// }
435 /// #[derive(Trackable)]
436 /// struct MyStruct {
437 /// s: String
438 /// }
439 /// let store = Store::new(MyStruct {
440 /// s: String::from("hello world")
441 /// });
442 /// modify_string(store.build_path().s().into_path());
443 /// assert_eq!(&**store.build_path().s().borrow(), "haha");
444 /// ```
445 fn build_path(self) -> <Self::Out as Trackable>::PathBuilder<Self>
446 where
447 Self: Sized,
448 Self::Out: Trackable,
449 {
450 <Self::Out as Trackable>::new_path_builder(self)
451 }
452
453 /// Create a new Path from borrowing this path.
454 ///
455 /// ```
456 /// # use x_bow::{Trackable, Store, PathExt};
457 /// # #[derive(Default, Trackable)]
458 /// # struct MyStruct {
459 /// # field_1: i32,
460 /// # field_2: u64
461 /// # }
462 /// let store = Store::new(MyStruct::default());
463 ///
464 /// // `path_a` lives for as long as the store.
465 /// let path_a = store.build_path();
466 ///
467 /// // `path_b` composes `path_a`.
468 /// // `path_b` lives for as long as the store.
469 /// let path_b = path_a.field_1();
470 ///
471 /// // `path_c` borrows `path_a`.
472 /// // if you drop `path_a`, `path_c` will no longer be usable.
473 /// let path_c = path_a.as_ref_path().field_1();
474 /// ```
475 ///
476 /// Note that the new path will only live for as long as the borrow.
477 /// This means it won't be useful for use cases requiring long-lived path
478 /// (such as implementing an undo-redo log).
479 fn as_ref_path(&self) -> <Self::Out as Trackable>::PathBuilder<ReferencePath<'_, Self>>
480 where
481 Self::Out: Trackable,
482 {
483 <Self::Out as Trackable>::new_path_builder(ReferencePath::new(self))
484 }
485}
486
487/// Extension trait is blanket-implemented.
488impl<P: Path + ?Sized> PathExt for P {}