feat: type-erased store Field structs

This commit is contained in:
Greg Johnston 2024-07-12 10:26:17 -04:00
parent c72c2f4803
commit 0a89f151be
9 changed files with 433 additions and 103 deletions

View file

@ -1,6 +1,7 @@
use leptos::logging::log;
use leptos::prelude::*;
use reactive_stores::{
AtIndex, Store, StoreField, StoreFieldIterator, Subfield,
AtIndex, Field, Store, StoreField, StoreFieldIterator, Subfield,
};
use reactive_stores_macro::Store;
@ -60,6 +61,10 @@ pub fn App() -> impl IntoView {
.collect_view()
};
Effect::new(move |_| {
log!("{:?}", *store.read());
});
view! {
<form on:submit=move |ev| {
ev.prevent_default();
@ -77,8 +82,7 @@ pub fn App() -> impl IntoView {
fn TodoRow(
store: Store<Todos>,
idx: usize,
// to be fair, this is gross
todo: AtIndex<Subfield<Store<Todos>, Todos, Vec<Todo>>, Vec<Todo>>,
#[prop(into)] todo: Field<Todo>,
) -> impl IntoView {
let completed = todo.completed();
let title = todo.label();

View file

@ -431,6 +431,81 @@ where
}
}
pub struct MappedArc<Inner, U>
where
Inner: Deref,
{
inner: Inner,
map_fn: Arc<dyn Fn(&Inner::Target) -> &U>,
}
impl<Inner, U> Clone for MappedArc<Inner, U>
where
Inner: Clone + Deref,
{
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
map_fn: self.map_fn.clone(),
}
}
}
impl<Inner, U> Debug for MappedArc<Inner, U>
where
Inner: Debug + Deref,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("MappedArc")
.field("inner", &self.inner)
.finish_non_exhaustive()
}
}
impl<Inner, U> MappedArc<Inner, U>
where
Inner: Deref,
{
pub fn new(
inner: Inner,
map_fn: impl Fn(&Inner::Target) -> &U + 'static,
) -> Self {
Self {
inner,
map_fn: Arc::new(map_fn),
}
}
}
impl<Inner, U> Deref for MappedArc<Inner, U>
where
Inner: Deref,
{
type Target = U;
fn deref(&self) -> &Self::Target {
(self.map_fn)(self.inner.deref())
}
}
impl<Inner, U: PartialEq> PartialEq for MappedArc<Inner, U>
where
Inner: Deref,
{
fn eq(&self, other: &Self) -> bool {
**self == **other
}
}
impl<Inner, U: Display> Display for MappedArc<Inner, U>
where
Inner: Deref,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Display::fmt(&**self, f)
}
}
pub struct MappedMutArc<Inner, U>
where
Inner: Deref,

View file

@ -0,0 +1,196 @@
use crate::{
path::{StorePath, StorePathSegment},
AtIndex, StoreField, Subfield,
};
use reactive_graph::{
signal::ArcTrigger,
traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
},
};
use std::{
ops::{Deref, DerefMut, IndexMut},
panic::Location,
sync::Arc,
};
pub struct ArcField<T>
where
T: 'static,
{
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
trigger: ArcTrigger,
path: StorePath,
read: Arc<dyn Fn() -> Option<StoreFieldReader<T>> + Send + Sync>,
write: Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,
}
pub struct StoreFieldReader<T>(Box<dyn Deref<Target = T>>);
impl<T> StoreFieldReader<T> {
pub fn new(inner: impl Deref<Target = T> + 'static) -> Self {
Self(Box::new(inner))
}
}
impl<T> Deref for StoreFieldReader<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
pub struct StoreFieldWriter<T>(Box<dyn UntrackableGuard<Target = T>>);
impl<T> StoreFieldWriter<T> {
pub fn new(inner: impl UntrackableGuard<Target = T> + 'static) -> Self {
Self(Box::new(inner))
}
}
impl<T> Deref for StoreFieldWriter<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
self.0.deref()
}
}
impl<T> DerefMut for StoreFieldWriter<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.0.deref_mut()
}
}
impl<T> UntrackableGuard for StoreFieldWriter<T> {
fn untrack(&mut self) {
self.0.untrack();
}
}
impl<T> StoreField<T> for ArcField<T> {
type Reader = StoreFieldReader<T>;
type Writer = StoreFieldWriter<T>;
fn get_trigger(&self, _path: StorePath) -> ArcTrigger {
self.trigger.clone()
}
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
std::iter::empty() // TODO
}
fn reader(&self) -> Option<Self::Reader> {
(self.read)().map(StoreFieldReader::new)
}
fn writer(&self) -> Option<Self::Writer> {
(self.write)().map(StoreFieldWriter::new)
}
}
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for ArcField<T>
where
T: Send + Sync,
Subfield<Inner, Prev, T>: Clone,
Inner: StoreField<Prev> + Send + Sync + 'static,
Prev: 'static,
{
#[track_caller]
fn from(value: Subfield<Inner, Prev, T>) -> Self {
ArcField {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
read: Arc::new({
let value = value.clone();
move || value.reader().map(StoreFieldReader::new)
}),
write: Arc::new({
let value = value.clone();
move || value.writer().map(StoreFieldWriter::new)
}),
}
}
}
impl<Inner, Prev> From<AtIndex<Inner, Prev>> for ArcField<Prev::Output>
where
AtIndex<Inner, Prev>: Clone,
Inner: StoreField<Prev> + Send + Sync + 'static,
Prev: IndexMut<usize> + Send + Sync + 'static,
Prev::Output: Sized + Send + Sync,
{
#[track_caller]
fn from(value: AtIndex<Inner, Prev>) -> Self {
ArcField {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
path: value.path().into_iter().collect(),
trigger: value.get_trigger(value.path().into_iter().collect()),
read: Arc::new({
let value = value.clone();
move || value.reader().map(StoreFieldReader::new)
}),
write: Arc::new({
let value = value.clone();
move || value.writer().map(StoreFieldWriter::new)
}),
}
}
}
impl<T> Clone for ArcField<T> {
fn clone(&self) -> Self {
Self {
#[cfg(debug_assertions)]
defined_at: self.defined_at,
path: self.path.clone(),
trigger: self.trigger.clone(),
read: Arc::clone(&self.read),
write: Arc::clone(&self.write),
}
}
}
impl<T> DefinedAt for ArcField<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T> Trigger for ArcField<T> {
fn trigger(&self) {
self.trigger.trigger();
}
}
impl<T> Track for ArcField<T> {
fn track(&self) {
self.trigger.track();
}
}
impl<T> ReadUntracked for ArcField<T> {
type Value = StoreFieldReader<T>;
fn try_read_untracked(&self) -> Option<Self::Value> {
(self.read)()
}
}
impl<T> IsDisposed for ArcField<T> {
fn is_disposed(&self) -> bool {
false
}
}

View file

@ -0,0 +1,141 @@
use crate::{
arc_field::{StoreFieldReader, StoreFieldWriter},
path::{StorePath, StorePathSegment},
ArcField, AtIndex, StoreField, Subfield,
};
use reactive_graph::{
owner::StoredValue,
signal::ArcTrigger,
traits::{
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
},
unwrap_signal,
};
use std::{
ops::{Deref, DerefMut, IndexMut},
panic::Location,
sync::Arc,
};
pub struct Field<T>
where
T: 'static,
{
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
inner: StoredValue<ArcField<T>>,
}
impl<T> StoreField<T> for Field<T> {
type Reader = StoreFieldReader<T>;
type Writer = StoreFieldWriter<T>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner
.try_get_value()
.map(|inner| inner.get_trigger(path))
.unwrap_or_else(unwrap_signal!(self))
}
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
self.inner
.try_get_value()
.map(|inner| inner.path().into_iter().collect::<Vec<_>>())
.unwrap_or_else(unwrap_signal!(self))
}
fn reader(&self) -> Option<Self::Reader> {
self.inner.try_get_value().and_then(|inner| inner.reader())
}
fn writer(&self) -> Option<Self::Writer> {
self.inner.try_get_value().and_then(|inner| inner.writer())
}
}
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Field<T>
where
T: Send + Sync,
Subfield<Inner, Prev, T>: Clone,
Inner: StoreField<Prev> + Send + Sync + 'static,
Prev: 'static,
{
#[track_caller]
fn from(value: Subfield<Inner, Prev, T>) -> Self {
Field {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: StoredValue::new(value.into()),
}
}
}
impl<Inner, Prev> From<AtIndex<Inner, Prev>> for Field<Prev::Output>
where
AtIndex<Inner, Prev>: Clone,
Inner: StoreField<Prev> + Send + Sync + 'static,
Prev: IndexMut<usize> + Send + Sync + 'static,
Prev::Output: Sized + Send + Sync,
{
#[track_caller]
fn from(value: AtIndex<Inner, Prev>) -> Self {
Field {
#[cfg(debug_assertions)]
defined_at: Location::caller(),
inner: StoredValue::new(value.into()),
}
}
}
impl<T> Clone for Field<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T> Copy for Field<T> {}
impl<T> DefinedAt for Field<T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<T> Trigger for Field<T> {
fn trigger(&self) {
if let Some(inner) = self.inner.try_get_value() {
inner.trigger();
}
}
}
impl<T> Track for Field<T> {
fn track(&self) {
if let Some(inner) = self.inner.try_get_value() {
inner.track();
}
}
}
impl<T> ReadUntracked for Field<T> {
type Value = StoreFieldReader<T>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.inner
.try_get_value()
.and_then(|inner| inner.try_read_untracked())
}
}
impl<T> IsDisposed for Field<T> {
fn is_disposed(&self) -> bool {
!self.inner.exists()
}
}

View file

@ -15,7 +15,7 @@ use reactive_graph::{
use std::{
iter,
marker::PhantomData,
ops::{DerefMut, Index, IndexMut},
ops::{DerefMut, IndexMut},
panic::Location,
sync::{Arc, RwLock},
};
@ -71,10 +71,9 @@ where
impl<Inner, Prev> StoreField<Prev::Output> for AtIndex<Inner, Prev>
where
Inner: StoreField<Prev>,
Prev: IndexMut<usize>,
Prev: IndexMut<usize> + 'static,
Prev::Output: Sized,
{
type Orig = Inner::Orig;
type Reader = MappedMutArc<Inner::Reader, Prev::Output>;
type Writer =
MappedMutArc<WriteGuard<ArcTrigger, Inner::Writer>, Prev::Output>;
@ -86,10 +85,6 @@ where
.chain(iter::once(self.index.into()))
}
fn data(&self) -> Arc<RwLock<Self::Orig>> {
self.inner.data()
}
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner.get_trigger(path)
}
@ -168,7 +163,7 @@ where
impl<Inner, Prev> ReadUntracked for AtIndex<Inner, Prev>
where
Inner: StoreField<Prev>,
Prev: IndexMut<usize>,
Prev: IndexMut<usize> + 'static,
Prev::Output: Sized,
{
type Value = <Self as StoreField<Prev::Output>>::Reader;

View file

@ -1,7 +1,7 @@
use reactive_graph::{
owner::StoredValue,
signal::{
guards::{Mapped, Plain, ReadGuard},
guards::{Plain, ReadGuard},
ArcTrigger,
},
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger},
@ -13,12 +13,15 @@ use std::{
sync::{Arc, RwLock},
};
mod arc_field;
mod field;
mod iter;
mod path;
mod read_store_field;
mod store_field;
mod subfield;
pub use arc_field::ArcField;
pub use field::Field;
pub use iter::*;
use path::StorePath;
pub use store_field::StoreField;

View file

@ -1,64 +0,0 @@
use reactive_graph::{
signal::{
guards::{Mapped, Plain, ReadGuard},
ArcTrigger,
},
traits::{DefinedAt, ReadUntracked, Track},
};
use std::{
panic::Location,
sync::{Arc, RwLock},
};
pub struct ArcReadStoreField<Orig, T>
where
T: 'static,
{
#[cfg(debug_assertions)]
defined_at: &'static Location<'static>,
data: Arc<RwLock<Orig>>,
trigger: ArcTrigger,
read: fn(&Orig) -> &T,
}
impl<Orig, T> Clone for ArcReadStoreField<Orig, T> {
fn clone(&self) -> Self {
Self {
#[cfg(debug_assertions)]
defined_at: self.defined_at,
data: Arc::clone(&self.data),
trigger: self.trigger.clone(),
read: self.read,
}
}
}
impl<Orig, T> DefinedAt for ArcReadStoreField<Orig, T> {
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(debug_assertions)]
{
Some(self.defined_at)
}
#[cfg(not(debug_assertions))]
{
None
}
}
}
impl<Orig, T> Track for ArcReadStoreField<Orig, T> {
fn track(&self) {
self.trigger.track();
}
}
impl<Orig, T> ReadUntracked for ArcReadStoreField<Orig, T>
where
Orig: 'static,
{
type Value = ReadGuard<T, Mapped<Plain<Orig>, T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
Mapped::try_new(Arc::clone(&self.data), self.read).map(ReadGuard::new)
}
}

View file

@ -19,12 +19,9 @@ use std::{
};
pub trait StoreField<T>: Sized {
type Orig;
type Reader: Deref<Target = T>;
type Writer: UntrackableGuard<Target = T>;
fn data(&self) -> Arc<RwLock<Self::Orig>>;
fn get_trigger(&self, path: StorePath) -> ArcTrigger;
fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;
@ -38,14 +35,9 @@ impl<T> StoreField<T> for ArcStore<T>
where
T: 'static,
{
type Orig = T;
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, ArcRwLockWriteGuardian<T>>;
fn data(&self) -> Arc<RwLock<Self::Orig>> {
Arc::clone(&self.value)
}
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
let triggers = &self.signals;
let trigger = triggers.write().or_poisoned().get_or_insert(path);
@ -72,17 +64,9 @@ impl<T> StoreField<T> for Store<T>
where
T: 'static,
{
type Orig = T;
type Reader = Plain<T>;
type Writer = WriteGuard<ArcTrigger, ArcRwLockWriteGuardian<T>>;
fn data(&self) -> Arc<RwLock<Self::Orig>> {
self.inner
.try_get_value()
.map(|n| n.data())
.unwrap_or_else(unwrap_signal!(self))
}
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner
.try_get_value()

View file

@ -4,10 +4,7 @@ use crate::{
};
use reactive_graph::{
signal::{
guards::{
Mapped, MappedMut, Plain, ReadGuard, UntrackedWriteGuard,
WriteGuard,
},
guards::{Mapped, MappedMut, WriteGuard},
ArcTrigger,
},
traits::{
@ -85,8 +82,8 @@ where
impl<Inner, Prev, T> StoreField<T> for Subfield<Inner, Prev, T>
where
Inner: StoreField<Prev>,
Prev: 'static,
{
type Orig = Inner::Orig;
type Reader = Mapped<Inner::Reader, T>;
type Writer = MappedMut<WriteGuard<ArcTrigger, Inner::Writer>, T>;
@ -97,10 +94,6 @@ where
.chain(iter::once(self.path_segment))
}
fn data(&self) -> Arc<RwLock<Self::Orig>> {
self.inner.data()
}
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
self.inner.get_trigger(path)
}
@ -145,6 +138,7 @@ where
impl<Inner, Prev, T> Trigger for Subfield<Inner, Prev, T>
where
Inner: StoreField<Prev>,
Prev: 'static,
{
fn trigger(&self) {
let trigger = self.get_trigger(self.path().into_iter().collect());
@ -154,7 +148,7 @@ where
impl<Inner, Prev, T> Track for Subfield<Inner, Prev, T>
where
Inner: StoreField<Prev> + Send + Sync + Clone + 'static,
Inner: StoreField<Prev> + 'static,
Prev: 'static,
T: 'static,
{
@ -167,6 +161,7 @@ where
impl<Inner, Prev, T> ReadUntracked for Subfield<Inner, Prev, T>
where
Inner: StoreField<Prev>,
Prev: 'static,
{
type Value = <Self as StoreField<T>>::Reader;
@ -179,6 +174,7 @@ impl<Inner, Prev, T> Writeable for Subfield<Inner, Prev, T>
where
T: 'static,
Inner: StoreField<Prev>,
Prev: 'static,
{
type Value = T;