Files
dioxus/packages/signals/src/map_mut.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

162 lines
4.0 KiB
Rust

use std::ops::Deref;
use crate::{
read_impls, write_impls, Readable, ReadableExt, ReadableRef, Writable, WritableExt,
WritableRef, WriteLock,
};
use dioxus_core::{IntoAttributeValue, Subscribers};
use generational_box::{AnyStorage, BorrowResult};
/// A read only signal that has been mapped to a new type.
pub struct MappedMutSignal<
O: ?Sized,
V,
F = fn(&<V as Readable>::Target) -> &O,
FMut = fn(&mut <V as Readable>::Target) -> &mut O,
> {
value: V,
map_fn: F,
map_fn_mut: FMut,
_marker: std::marker::PhantomData<O>,
}
impl<V, O, F, FMut> Clone for MappedMutSignal<O, V, F, FMut>
where
V: Clone,
F: Clone,
FMut: Clone,
{
fn clone(&self) -> Self {
MappedMutSignal {
value: self.value.clone(),
map_fn: self.map_fn.clone(),
map_fn_mut: self.map_fn_mut.clone(),
_marker: std::marker::PhantomData,
}
}
}
impl<V, O, F, FMut> Copy for MappedMutSignal<O, V, F, FMut>
where
V: Copy,
F: Copy,
FMut: Copy,
{
}
impl<V, O, F, FMut> MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
{
/// Create a new mapped signal.
pub fn new(value: V, map_fn: F, map_fn_mut: FMut) -> Self {
MappedMutSignal {
value,
map_fn,
map_fn_mut,
_marker: std::marker::PhantomData,
}
}
}
impl<V, O, F, FMut> Readable for MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
type Target = O;
type Storage = V::Storage;
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
O: 'static,
{
let value = self.value.try_read_unchecked()?;
Ok(V::Storage::map(value, |v| (self.map_fn)(v)))
}
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
O: 'static,
{
let value = self.value.try_peek_unchecked()?;
Ok(V::Storage::map(value, |v| (self.map_fn)(v)))
}
fn subscribers(&self) -> Subscribers
where
O: 'static,
{
self.value.subscribers()
}
}
impl<V, O, F, FMut> Writable for MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Writable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
FMut: Fn(&mut V::Target) -> &mut O,
{
type WriteMetadata = V::WriteMetadata;
fn try_write_unchecked(
&self,
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
let value = self.value.try_write_unchecked()?;
Ok(WriteLock::map(value, |v| (self.map_fn_mut)(v)))
}
}
impl<V, O, F, FMut> IntoAttributeValue for MappedMutSignal<O, V, F, FMut>
where
O: Clone + IntoAttributeValue + 'static,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
fn into_value(self) -> dioxus_core::AttributeValue {
self.with(|f| f.clone().into_value())
}
}
impl<V, O, F, FMut> PartialEq for MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Readable + PartialEq,
F: PartialEq,
FMut: PartialEq,
{
fn eq(&self, other: &Self) -> bool {
self.value == other.value
&& self.map_fn == other.map_fn
&& self.map_fn_mut == other.map_fn_mut
}
}
/// Allow calling a signal with signal() syntax
///
/// Currently only limited to clone types, though could probably specialize for string/arc/rc
impl<V, O, F, FMut> Deref for MappedMutSignal<O, V, F, FMut>
where
O: Clone + 'static,
V: Readable + 'static,
V::Target: 'static,
F: Fn(&V::Target) -> &O + 'static,
FMut: 'static,
{
type Target = dyn Fn() -> O;
fn deref(&self) -> &Self::Target {
unsafe { ReadableExt::deref_impl(self) }
}
}
read_impls!(MappedMutSignal<T, V, F, FMut> where V: Readable<Target: 'static>, F: Fn(&V::Target) -> &T);
write_impls!(MappedMutSignal<T, V, F, FMut> where V: Writable<Target: 'static>, F: Fn(&V::Target) -> &T, FMut: Fn(&mut V::Target) -> &mut T);