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 {}