Files
dioxus/packages/stores/src/impls/index.rs
T
Evan AlmloffandJonathan Kelley 58c41f8561 Stores: Externally tracked state and reactive collections (#4483)
* map signals mutably

* boxed signals

* remove mutable borrow type from boxed writable

* improve map mut example

* restore boxed_mut helper

* make boxed readable/writable types copy

* Move away from the ReadOnlySignal alias in examples

* automatically convert Write

* Fix some tests

* ReadSignal and WriteSignal

* move read only signal alias

* make maps copy

* store prototype

* implement derive macro

* Handle generics in the derive macro

* Make Selector boxable

* derive boxed conversion

* Get rid of store type

* Remove hardcoded type from derive macro

* selector -> store

* Refactor store implementation

* more vec methods

* todo mvc store example

* Implement Storable for HashMap

* fix write

* restore sorting logic in todomvc

* derive readable and writable

* improve todomvc store example

* Result store

* Option store

* collect dead subscribers

* fix clippy and formatting

* recognize some common foreign types automatically

* fix foreign stores in props

* slice and array selectors

* clean up store example

* writable is child owned

* box selector

* read and write to builtins

* re-use the proc macro in the derive macro

* Switch to a extension trait based approach

* switch to extension traits in the derive macro

* transposed method

* derive enums

* Expose Store::new

* remove stores from the prelude

* More consistent naming scheme for SelectorScope methods

* document hashmap methods

* document index extension

* document option ext

* document result ext

* document slice extension

* document the vec extension

* make more methods borrow the store mutably

* Move to impls to have higher priority over the builtin write extension traits

* fix deadlock

* finish stores documentation

* Document the store macro

* cleanup docs and organization

* finish derive docs

* fix typo

* fix clippy

* fix doc tests

* fix derive macro with generics

* fix clippy

* test and fix empty structs

* clean up todomvc store example

* default to a boxed store writer to make it easier to type when providing to the context api

* fix cargo doc

* fix clippy in store example

* relax generational box lifetime bounds

* relax bounds

* relax signal bounds

* fix compilation

* read store

* relax bounds on default store impls

* fix enum derive

* Convert to readsignal and writesignal automatically

* simplify type matching logic

* add write store

* unsized stores

* shrink the path vec type

* use more concrete types

* remove opaque types

* remove copy bounds

* use lens as generic name everywhere

* more consistent borrow semantics

* use a concrete type for the signal metadata and mention references in the map docs

* improve todomvc store docs

* document store derive macro internals

* refactor macro

* improve selector node documentation

* fix clippy

* add btreemap

* fix doc tests

* more option methods

* fix doc

* fix clippy

* add a bunch more result methods

* add a bunch of bounds to the iterators

* fix clippy

* remove option from subscribers

* add store macro to generate extension traits

* fix store example

* fix deadlock

* fix docs

* fix doc tests

* fix clippy

* add stores to public api

* add store macro, and then update todomvc_store example

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
2025-08-05 11:30:21 -07:00

137 lines
3.8 KiB
Rust

use std::{
hash::Hash,
ops::{self, Index, IndexMut},
};
use crate::{store::Store, ReadStore};
use dioxus_signals::{
AnyStorage, BorrowError, BorrowMutError, ReadSignal, Readable, UnsyncStorage, Writable,
WriteLock, WriteSignal,
};
impl<Lens, T> Store<T, Lens> {
/// Index into the store, returning a store that allows access to the item at the given index. The
/// new store will only update when the item at the index changes.
///
/// # Example
/// ```rust, no_run
/// use dioxus_stores::*;
/// let store = use_store(|| vec![1, 2, 3]);
/// let indexed_store = store.index(1);
/// // The indexed store can access the store methods of the indexed store.
/// assert_eq!(indexed_store(), 2);
/// ```
pub fn index<Idx>(self, index: Idx) -> Store<T::Output, IndexWrite<Idx, Lens>>
where
T: IndexMut<Idx> + 'static,
Idx: Hash + 'static,
Lens: Readable<Target = T> + 'static,
{
self.into_selector()
.hash_child_unmapped(&index)
.map_writer(move |write| IndexWrite { index, write })
.into()
}
}
/// A specific index in a `Readable` / `Writable` type
#[derive(Clone, Copy)]
pub struct IndexWrite<Index, Write> {
index: Index,
write: Write,
}
impl<Index, Write> Readable for IndexWrite<Index, Write>
where
Write: Readable,
Write::Target: ops::Index<Index> + 'static,
Index: Clone,
{
type Target = <Write::Target as ops::Index<Index>>::Output;
type Storage = Write::Storage;
fn try_read_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
where
Self::Target: 'static,
{
self.write.try_read_unchecked().map(|value| {
Self::Storage::map(value, |value: &Write::Target| {
value.index(self.index.clone())
})
})
}
fn try_peek_unchecked(&self) -> Result<dioxus_signals::ReadableRef<'static, Self>, BorrowError>
where
Self::Target: 'static,
{
self.write.try_peek_unchecked().map(|value| {
Self::Storage::map(value, |value: &Write::Target| {
value.index(self.index.clone())
})
})
}
fn subscribers(&self) -> dioxus_core::Subscribers
where
Self::Target: 'static,
{
self.write.subscribers()
}
}
impl<Index, Write> Writable for IndexWrite<Index, Write>
where
Write: Writable,
Write::Target: ops::IndexMut<Index> + 'static,
Index: Clone,
{
type WriteMetadata = Write::WriteMetadata;
fn try_write_unchecked(
&self,
) -> Result<dioxus_signals::WritableRef<'static, Self>, BorrowMutError>
where
Self::Target: 'static,
{
self.write.try_write_unchecked().map(|value| {
WriteLock::map(value, |value: &mut Write::Target| {
value.index_mut(self.index.clone())
})
})
}
}
impl<Idx, T, Write> ::std::convert::From<Store<T, IndexWrite<Idx, Write>>>
for Store<T, WriteSignal<T>>
where
Write: Writable<Storage = UnsyncStorage> + 'static,
Write::WriteMetadata: 'static,
Write::Target: ops::IndexMut<Idx, Output = T> + 'static,
Idx: Clone + 'static,
T: 'static,
{
fn from(value: Store<T, IndexWrite<Idx, Write>>) -> Self {
value
.into_selector()
.map_writer(|writer| WriteSignal::new(writer))
.into()
}
}
impl<Idx, T, Write> ::std::convert::From<Store<T, IndexWrite<Idx, Write>>> for ReadStore<T>
where
Write: Readable<Storage = UnsyncStorage> + 'static,
Write::Target: ops::Index<Idx, Output = T> + 'static,
Idx: Clone + 'static,
T: 'static,
{
fn from(value: Store<T, IndexWrite<Idx, Write>>) -> Self {
value
.into_selector()
.map_writer(|writer| ReadSignal::new(writer))
.into()
}
}