mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
feat: keyed store fields, with keyed iteration
This commit is contained in:
parent
69c4090d32
commit
d7b2f9d05b
11 changed files with 964 additions and 60 deletions
|
@ -3,6 +3,11 @@
|
||||||
<head>
|
<head>
|
||||||
<link data-trunk rel="rust" data-wasm-opt="z"/>
|
<link data-trunk rel="rust" data-wasm-opt="z"/>
|
||||||
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
|
<link data-trunk rel="icon" type="image/ico" href="/public/favicon.ico"/>
|
||||||
|
<style>
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body></body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
|
|
||||||
use chrono::{Local, NaiveDate};
|
use chrono::{Local, NaiveDate};
|
||||||
use leptos::prelude::*;
|
use leptos::prelude::*;
|
||||||
use reactive_stores::{Field, Store, StoreFieldIterator};
|
use reactive_stores::{Field, Store};
|
||||||
use reactive_stores_macro::Store;
|
use reactive_stores_macro::Store;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@ static NEXT_ID: AtomicUsize = AtomicUsize::new(3);
|
||||||
#[derive(Debug, Store, Serialize, Deserialize)]
|
#[derive(Debug, Store, Serialize, Deserialize)]
|
||||||
struct Todos {
|
struct Todos {
|
||||||
user: String,
|
user: String,
|
||||||
|
#[store(key: usize = |todo| todo.id)]
|
||||||
todos: Vec<Todo>,
|
todos: Vec<Todo>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +95,7 @@ pub fn App() -> impl IntoView {
|
||||||
<input type="submit"/>
|
<input type="submit"/>
|
||||||
</form>
|
</form>
|
||||||
<ol>
|
<ol>
|
||||||
<For each=move || store.todos().iter() key=|row| row.id().get() let:todo>
|
<For each=move || store.todos().iter_keyed() key=|row| row.id().get() let:todo>
|
||||||
<TodoRow store todo/>
|
<TodoRow store todo/>
|
||||||
</For>
|
</For>
|
||||||
|
|
||||||
|
@ -111,7 +112,7 @@ fn TodoRow(
|
||||||
let status = todo.status();
|
let status = todo.status();
|
||||||
let title = todo.label();
|
let title = todo.label();
|
||||||
|
|
||||||
let editing = RwSignal::new(false);
|
let editing = RwSignal::new(true);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<li style:text-decoration=move || {
|
<li style:text-decoration=move || {
|
||||||
|
@ -133,12 +134,9 @@ fn TodoRow(
|
||||||
prop:value=move || title.get()
|
prop:value=move || title.get()
|
||||||
on:change=move |ev| {
|
on:change=move |ev| {
|
||||||
title.set(event_target_value(&ev));
|
title.set(event_target_value(&ev));
|
||||||
editing.set(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
on:blur=move |_| editing.set(false)
|
|
||||||
autofocus
|
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<button on:click=move |_| {
|
<button on:click=move |_| {
|
||||||
status.write().next_step()
|
status.write().next_step()
|
||||||
}>
|
}>
|
||||||
|
@ -155,10 +153,11 @@ fn TodoRow(
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button on:click=move |_| {
|
<button on:click=move |_| {
|
||||||
|
let id = todo.id().get();
|
||||||
store
|
store
|
||||||
.todos()
|
.todos()
|
||||||
.update(|todos| {
|
.update(|todos| {
|
||||||
todos.remove(todo.id().get());
|
todos.remove(id);
|
||||||
});
|
});
|
||||||
}>"X"</button>
|
}>"X"</button>
|
||||||
<input
|
<input
|
||||||
|
|
|
@ -99,7 +99,8 @@ pub mod prelude {
|
||||||
|
|
||||||
// TODO remove this, it's just useful while developing
|
// TODO remove this, it's just useful while developing
|
||||||
#[allow(unused)]
|
#[allow(unused)]
|
||||||
fn log_warning(text: Arguments) {
|
#[doc(hidden)]
|
||||||
|
pub fn log_warning(text: Arguments) {
|
||||||
#[cfg(feature = "tracing")]
|
#[cfg(feature = "tracing")]
|
||||||
{
|
{
|
||||||
tracing::warn!(text);
|
tracing::warn!(text);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
path::{StorePath, StorePathSegment},
|
path::{StorePath, StorePathSegment},
|
||||||
AtIndex, StoreField, Subfield,
|
AtIndex, AtKeyed, KeyMap, KeyedSubfield, StoreField, Subfield,
|
||||||
};
|
};
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
signal::ArcTrigger,
|
signal::ArcTrigger,
|
||||||
|
@ -9,6 +9,8 @@ use reactive_graph::{
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use std::{
|
use std::{
|
||||||
|
fmt::Debug,
|
||||||
|
hash::Hash,
|
||||||
ops::{Deref, DerefMut, IndexMut},
|
ops::{Deref, DerefMut, IndexMut},
|
||||||
panic::Location,
|
panic::Location,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -25,6 +27,7 @@ where
|
||||||
get_trigger: Arc<dyn Fn(StorePath) -> ArcTrigger + Send + Sync>,
|
get_trigger: Arc<dyn Fn(StorePath) -> ArcTrigger + Send + Sync>,
|
||||||
read: Arc<dyn Fn() -> Option<StoreFieldReader<T>> + Send + Sync>,
|
read: Arc<dyn Fn() -> Option<StoreFieldReader<T>> + Send + Sync>,
|
||||||
write: Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,
|
write: Arc<dyn Fn() -> Option<StoreFieldWriter<T>> + Send + Sync>,
|
||||||
|
keys: Arc<dyn Fn() -> Option<KeyMap> + Send + Sync>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct StoreFieldReader<T>(Box<dyn Deref<Target = T>>);
|
pub struct StoreFieldReader<T>(Box<dyn Deref<Target = T>>);
|
||||||
|
@ -98,6 +101,10 @@ impl<T> StoreField for ArcField<T> {
|
||||||
writer.untrack();
|
writer.untrack();
|
||||||
Some(writer)
|
Some(writer)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
(self.keys)()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for ArcField<T>
|
impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for ArcField<T>
|
||||||
|
@ -126,6 +133,10 @@ where
|
||||||
let value = value.clone();
|
let value = value.clone();
|
||||||
move || value.writer().map(StoreFieldWriter::new)
|
move || value.writer().map(StoreFieldWriter::new)
|
||||||
}),
|
}),
|
||||||
|
keys: Arc::new({
|
||||||
|
let value = value.clone();
|
||||||
|
move || value.keys()
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -156,6 +167,48 @@ where
|
||||||
let value = value.clone();
|
let value = value.clone();
|
||||||
move || value.writer().map(StoreFieldWriter::new)
|
move || value.writer().map(StoreFieldWriter::new)
|
||||||
}),
|
}),
|
||||||
|
keys: Arc::new({
|
||||||
|
let value = value.clone();
|
||||||
|
move || value.keys()
|
||||||
|
}),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> From<AtKeyed<Inner, Prev, K, T>> for ArcField<T::Output>
|
||||||
|
where
|
||||||
|
AtKeyed<Inner, Prev, K, T>: Clone,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev> + Send + Sync + 'static,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize> + 'static,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
#[track_caller]
|
||||||
|
fn from(value: AtKeyed<Inner, Prev, K, 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()),
|
||||||
|
get_trigger: Arc::new({
|
||||||
|
let value = value.clone();
|
||||||
|
move |path| value.get_trigger(path)
|
||||||
|
}),
|
||||||
|
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)
|
||||||
|
}),
|
||||||
|
keys: Arc::new({
|
||||||
|
let value = value.clone();
|
||||||
|
move || value.keys()
|
||||||
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -170,6 +223,7 @@ impl<T> Clone for ArcField<T> {
|
||||||
get_trigger: Arc::clone(&self.get_trigger),
|
get_trigger: Arc::clone(&self.get_trigger),
|
||||||
read: Arc::clone(&self.read),
|
read: Arc::clone(&self.read),
|
||||||
write: Arc::clone(&self.write),
|
write: Arc::clone(&self.write),
|
||||||
|
keys: Arc::clone(&self.keys),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
arc_field::{StoreFieldReader, StoreFieldWriter},
|
arc_field::{StoreFieldReader, StoreFieldWriter},
|
||||||
path::{StorePath, StorePathSegment},
|
path::{StorePath, StorePathSegment},
|
||||||
ArcField, AtIndex, StoreField, Subfield,
|
ArcField, AtIndex, AtKeyed, KeyMap, KeyedSubfield, StoreField, Subfield,
|
||||||
};
|
};
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
owner::{Storage, StoredValue, SyncStorage},
|
owner::{Storage, StoredValue, SyncStorage},
|
||||||
|
@ -9,7 +9,7 @@ use reactive_graph::{
|
||||||
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger},
|
traits::{DefinedAt, IsDisposed, ReadUntracked, Track, Trigger},
|
||||||
unwrap_signal,
|
unwrap_signal,
|
||||||
};
|
};
|
||||||
use std::{ops::IndexMut, panic::Location};
|
use std::{fmt::Debug, hash::Hash, ops::IndexMut, panic::Location};
|
||||||
|
|
||||||
pub struct Field<T, S = SyncStorage>
|
pub struct Field<T, S = SyncStorage>
|
||||||
where
|
where
|
||||||
|
@ -56,6 +56,10 @@ where
|
||||||
.try_get_value()
|
.try_get_value()
|
||||||
.and_then(|inner| inner.untracked_writer())
|
.and_then(|inner| inner.untracked_writer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.try_get_value().and_then(|n| n.keys())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev, T, S> From<Subfield<Inner, Prev, T>> for Field<T, S>
|
impl<Inner, Prev, T, S> From<Subfield<Inner, Prev, T>> for Field<T, S>
|
||||||
|
@ -94,6 +98,29 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T, S> From<AtKeyed<Inner, Prev, K, T>>
|
||||||
|
for Field<T::Output, S>
|
||||||
|
where
|
||||||
|
S: Storage<ArcField<T::Output>>,
|
||||||
|
AtKeyed<Inner, Prev, K, T>: Clone,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev> + Send + Sync + 'static,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize> + 'static,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
#[track_caller]
|
||||||
|
fn from(value: AtKeyed<Inner, Prev, K, T>) -> Self {
|
||||||
|
Field {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: Location::caller(),
|
||||||
|
inner: StoredValue::new_with_storage(value.into()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T, S> Clone for Field<T, S> {
|
impl<T, S> Clone for Field<T, S> {
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
*self
|
*self
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
path::{StorePath, StorePathSegment},
|
path::{StorePath, StorePathSegment},
|
||||||
store_field::StoreField,
|
store_field::StoreField,
|
||||||
|
KeyMap,
|
||||||
};
|
};
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
signal::{
|
signal::{
|
||||||
|
@ -20,10 +21,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AtIndex<Inner, Prev>
|
pub struct AtIndex<Inner, Prev> {
|
||||||
where
|
|
||||||
Inner: StoreField<Value = Prev>,
|
|
||||||
{
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
defined_at: &'static Location<'static>,
|
defined_at: &'static Location<'static>,
|
||||||
inner: Inner,
|
inner: Inner,
|
||||||
|
@ -33,7 +31,7 @@ where
|
||||||
|
|
||||||
impl<Inner, Prev> Clone for AtIndex<Inner, Prev>
|
impl<Inner, Prev> Clone for AtIndex<Inner, Prev>
|
||||||
where
|
where
|
||||||
Inner: StoreField<Value = Prev> + Clone,
|
Inner: Clone,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -46,15 +44,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev> Copy for AtIndex<Inner, Prev> where
|
impl<Inner, Prev> Copy for AtIndex<Inner, Prev> where Inner: Copy {}
|
||||||
Inner: StoreField<Value = Prev> + Copy
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Inner, Prev> AtIndex<Inner, Prev>
|
impl<Inner, Prev> AtIndex<Inner, Prev> {
|
||||||
where
|
|
||||||
Inner: StoreField<Value = Prev>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(inner: Inner, index: usize) -> Self {
|
pub fn new(inner: Inner, index: usize) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -117,6 +109,11 @@ where
|
||||||
guard.untrack();
|
guard.untrack();
|
||||||
Some(guard)
|
Some(guard)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.keys()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev> DefinedAt for AtIndex<Inner, Prev>
|
impl<Inner, Prev> DefinedAt for AtIndex<Inner, Prev>
|
||||||
|
@ -203,16 +200,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait StoreFieldIterator<Prev>: Sized {
|
pub trait StoreFieldIterator<Prev>
|
||||||
|
where
|
||||||
|
Self: StoreField<Value = Prev>,
|
||||||
|
{
|
||||||
|
fn at(self, index: usize) -> AtIndex<Self, Prev>;
|
||||||
|
|
||||||
fn iter(self) -> StoreFieldIter<Self, Prev>;
|
fn iter(self) -> StoreFieldIter<Self, Prev>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev> StoreFieldIterator<Prev> for Inner
|
impl<Inner, Prev> StoreFieldIterator<Prev> for Inner
|
||||||
where
|
where
|
||||||
Inner: StoreField<Value = Prev>,
|
Inner: StoreField<Value = Prev> + Clone,
|
||||||
Prev::Output: Sized,
|
Prev::Output: Sized,
|
||||||
Prev: IndexMut<usize> + AsRef<[Prev::Output]>,
|
Prev: IndexMut<usize> + AsRef<[Prev::Output]>,
|
||||||
{
|
{
|
||||||
|
#[track_caller]
|
||||||
|
fn at(self, index: usize) -> AtIndex<Inner, Prev> {
|
||||||
|
AtIndex::new(self.clone(), index)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn iter(self) -> StoreFieldIter<Inner, Prev> {
|
fn iter(self) -> StoreFieldIter<Inner, Prev> {
|
||||||
// reactively track changes to this field
|
// reactively track changes to this field
|
||||||
let trigger = self.get_trigger(self.path().into_iter().collect());
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
@ -248,13 +256,7 @@ where
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
if self.idx < self.len {
|
if self.idx < self.len {
|
||||||
let field = AtIndex {
|
let field = AtIndex::new(self.inner.clone(), self.idx);
|
||||||
#[cfg(debug_assertions)]
|
|
||||||
defined_at: Location::caller(),
|
|
||||||
index: self.idx,
|
|
||||||
inner: self.inner.clone(),
|
|
||||||
ty: PhantomData,
|
|
||||||
};
|
|
||||||
self.idx += 1;
|
self.idx += 1;
|
||||||
Some(field)
|
Some(field)
|
||||||
} else {
|
} else {
|
||||||
|
|
697
reactive_stores/src/keyed.rs
Normal file
697
reactive_stores/src/keyed.rs
Normal file
|
@ -0,0 +1,697 @@
|
||||||
|
use crate::{
|
||||||
|
path::{StorePath, StorePathSegment},
|
||||||
|
store_field::StoreField,
|
||||||
|
KeyMap,
|
||||||
|
};
|
||||||
|
use reactive_graph::{
|
||||||
|
signal::{
|
||||||
|
guards::{Mapped, MappedMut, MappedMutArc, WriteGuard},
|
||||||
|
ArcTrigger,
|
||||||
|
},
|
||||||
|
traits::{
|
||||||
|
DefinedAt, IsDisposed, ReadUntracked, Track, Trigger, UntrackableGuard,
|
||||||
|
Writeable,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
collections::VecDeque,
|
||||||
|
fmt::Debug,
|
||||||
|
hash::Hash,
|
||||||
|
iter,
|
||||||
|
ops::{Deref, DerefMut, IndexMut},
|
||||||
|
panic::Location,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: &'static Location<'static>,
|
||||||
|
path_segment: StorePathSegment,
|
||||||
|
inner: Inner,
|
||||||
|
read: fn(&Prev) -> &T,
|
||||||
|
write: fn(&mut Prev) -> &mut T,
|
||||||
|
key_fn: fn(<&T as IntoIterator>::Item) -> K,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Clone for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: self.defined_at,
|
||||||
|
path_segment: self.path_segment,
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
read: self.read,
|
||||||
|
write: self.write,
|
||||||
|
key_fn: self.key_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Copy for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: Copy,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
#[track_caller]
|
||||||
|
pub fn new(
|
||||||
|
inner: Inner,
|
||||||
|
path_segment: StorePathSegment,
|
||||||
|
key_fn: fn(<&T as IntoIterator>::Item) -> K,
|
||||||
|
read: fn(&Prev) -> &T,
|
||||||
|
write: fn(&mut Prev) -> &mut T,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: Location::caller(),
|
||||||
|
inner,
|
||||||
|
path_segment,
|
||||||
|
read,
|
||||||
|
write,
|
||||||
|
key_fn,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> StoreField for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
type Reader = Mapped<Inner::Reader, T>;
|
||||||
|
type Writer = KeyedSubfieldWriteGuard<
|
||||||
|
Inner,
|
||||||
|
Prev,
|
||||||
|
K,
|
||||||
|
T,
|
||||||
|
MappedMut<WriteGuard<ArcTrigger, Inner::Writer>, T>,
|
||||||
|
>;
|
||||||
|
type UntrackedWriter =
|
||||||
|
MappedMut<WriteGuard<ArcTrigger, Inner::UntrackedWriter>, T>;
|
||||||
|
|
||||||
|
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
|
||||||
|
self.inner
|
||||||
|
.path()
|
||||||
|
.into_iter()
|
||||||
|
.chain(iter::once(self.path_segment))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
|
||||||
|
self.inner.get_trigger(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reader(&self) -> Option<Self::Reader> {
|
||||||
|
let inner = self.inner.reader()?;
|
||||||
|
Some(Mapped::new_with_guard(inner, self.read))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writer(&self) -> Option<Self::Writer> {
|
||||||
|
let path = self.path().into_iter().collect::<StorePath>();
|
||||||
|
let trigger = self.get_trigger(path.clone());
|
||||||
|
let guard = WriteGuard::new(trigger, self.inner.writer()?);
|
||||||
|
let guard = MappedMut::new(guard, self.read, self.write);
|
||||||
|
Some(KeyedSubfieldWriteGuard {
|
||||||
|
inner: self.clone(),
|
||||||
|
guard: Some(guard),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
let inner = WriteGuard::new(trigger, self.inner.untracked_writer()?);
|
||||||
|
Some(MappedMut::new(inner, self.read, self.write))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.keys()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn latest_keys(&self) -> Vec<K> {
|
||||||
|
self.reader()
|
||||||
|
.expect("trying to update keys")
|
||||||
|
.deref()
|
||||||
|
.into_iter()
|
||||||
|
.map(|n| (self.key_fn)(n))
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
|
||||||
|
where
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
inner: KeyedSubfield<Inner, Prev, K, T>,
|
||||||
|
guard: Option<Guard>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T, Guard> Deref
|
||||||
|
for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
|
||||||
|
where
|
||||||
|
Guard: Deref,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
type Target = Guard::Target;
|
||||||
|
|
||||||
|
fn deref(&self) -> &Self::Target {
|
||||||
|
self.guard
|
||||||
|
.as_ref()
|
||||||
|
.expect("should be Some(_) until dropped")
|
||||||
|
.deref()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T, Guard> DerefMut
|
||||||
|
for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
|
||||||
|
where
|
||||||
|
Guard: DerefMut,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
|
self.guard
|
||||||
|
.as_mut()
|
||||||
|
.expect("should be Some(_) until dropped")
|
||||||
|
.deref_mut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T, Guard> UntrackableGuard
|
||||||
|
for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
|
||||||
|
where
|
||||||
|
Guard: UntrackableGuard,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn untrack(&mut self) {
|
||||||
|
if let Some(inner) = self.guard.as_mut() {
|
||||||
|
inner.untrack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T, Guard> Drop
|
||||||
|
for KeyedSubfieldWriteGuard<Inner, Prev, K, T, Guard>
|
||||||
|
where
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn drop(&mut self) {
|
||||||
|
// dropping the inner guard will
|
||||||
|
// 1) synchronously release its write lock on the store's value
|
||||||
|
// 2) trigger an (asynchronous) reactive update
|
||||||
|
drop(self.guard.take());
|
||||||
|
|
||||||
|
// now that the write lock is release, we can get a read lock to refresh this keyed field
|
||||||
|
// based on the new value
|
||||||
|
self.inner.update_keys();
|
||||||
|
|
||||||
|
// reactive updates happen on the next tick
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> DefinedAt for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
{
|
||||||
|
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
Some(self.defined_at)
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> IsDisposed for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: IsDisposed,
|
||||||
|
{
|
||||||
|
fn is_disposed(&self) -> bool {
|
||||||
|
self.inner.is_disposed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Trigger for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn trigger(&self) {
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
trigger.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Track for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev> + Track + 'static,
|
||||||
|
Prev: 'static,
|
||||||
|
T: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
fn track(&self) {
|
||||||
|
self.inner.track();
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
trigger.track();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> ReadUntracked for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
type Value = <Self as StoreField>::Reader;
|
||||||
|
|
||||||
|
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||||
|
self.reader()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Writeable for KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
T: 'static,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
type Value = T;
|
||||||
|
|
||||||
|
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
|
||||||
|
self.writer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_write_untracked(
|
||||||
|
&self,
|
||||||
|
) -> Option<impl DerefMut<Target = Self::Value>> {
|
||||||
|
self.writer().map(|mut writer| {
|
||||||
|
writer.untrack();
|
||||||
|
writer
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: &'static Location<'static>,
|
||||||
|
inner: KeyedSubfield<Inner, Prev, K, T>,
|
||||||
|
key: K,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Clone for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
K: Debug + Clone,
|
||||||
|
{
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: self.defined_at,
|
||||||
|
inner: self.inner.clone(),
|
||||||
|
key: self.key.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Copy for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Copy,
|
||||||
|
K: Debug + Copy,
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
#[track_caller]
|
||||||
|
pub fn new(inner: KeyedSubfield<Inner, Prev, K, T>, key: K) -> Self {
|
||||||
|
Self {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
defined_at: Location::caller(),
|
||||||
|
inner,
|
||||||
|
key,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> StoreField for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
type Value = T::Output;
|
||||||
|
type Reader = MappedMutArc<
|
||||||
|
<KeyedSubfield<Inner, Prev, K, T> as StoreField>::Reader,
|
||||||
|
T::Output,
|
||||||
|
>;
|
||||||
|
type Writer = WriteGuard<
|
||||||
|
ArcTrigger,
|
||||||
|
MappedMutArc<
|
||||||
|
<KeyedSubfield<Inner, Prev, K, T> as StoreField>::Writer,
|
||||||
|
T::Output,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
type UntrackedWriter = WriteGuard<
|
||||||
|
ArcTrigger,
|
||||||
|
MappedMutArc<
|
||||||
|
<KeyedSubfield<Inner, Prev, K, T> as StoreField>::Writer,
|
||||||
|
T::Output,
|
||||||
|
>,
|
||||||
|
>;
|
||||||
|
|
||||||
|
fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
|
||||||
|
let inner = self.inner.path().into_iter().collect::<StorePath>();
|
||||||
|
let keys = self
|
||||||
|
.inner
|
||||||
|
.keys()
|
||||||
|
.expect("using keys on a store with no keys");
|
||||||
|
let this = keys
|
||||||
|
.with_field_keys(
|
||||||
|
inner.clone(),
|
||||||
|
|keys| keys.get(&self.key),
|
||||||
|
|| self.inner.latest_keys(),
|
||||||
|
)
|
||||||
|
.flatten()
|
||||||
|
.map(|(path, _)| path);
|
||||||
|
inner.into_iter().chain(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_trigger(&self, path: StorePath) -> ArcTrigger {
|
||||||
|
self.inner.get_trigger(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reader(&self) -> Option<Self::Reader> {
|
||||||
|
let inner = self.inner.reader()?;
|
||||||
|
|
||||||
|
let inner_path = self.inner.path().into_iter().collect();
|
||||||
|
let keys = self
|
||||||
|
.inner
|
||||||
|
.keys()
|
||||||
|
.expect("using keys on a store with no keys");
|
||||||
|
let index = keys
|
||||||
|
.with_field_keys(
|
||||||
|
inner_path,
|
||||||
|
|keys| keys.get(&self.key),
|
||||||
|
|| self.inner.latest_keys(),
|
||||||
|
)
|
||||||
|
.flatten()
|
||||||
|
.map(|(_, idx)| idx)
|
||||||
|
.expect("reading from a keyed field that has not yet been created");
|
||||||
|
|
||||||
|
Some(MappedMutArc::new(
|
||||||
|
inner,
|
||||||
|
move |n| &n[index],
|
||||||
|
move |n| &mut n[index],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn writer(&self) -> Option<Self::Writer> {
|
||||||
|
let inner = self.inner.writer()?;
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
|
||||||
|
let inner_path = self.inner.path().into_iter().collect::<StorePath>();
|
||||||
|
let keys = self
|
||||||
|
.inner
|
||||||
|
.keys()
|
||||||
|
.expect("using keys on a store with no keys");
|
||||||
|
let index = keys
|
||||||
|
.with_field_keys(
|
||||||
|
inner_path.clone(),
|
||||||
|
|keys| keys.get(&self.key),
|
||||||
|
|| self.inner.latest_keys(),
|
||||||
|
)
|
||||||
|
.flatten()
|
||||||
|
.map(|(_, idx)| idx)
|
||||||
|
.expect("reading from a keyed field that has not yet been created");
|
||||||
|
|
||||||
|
Some(WriteGuard::new(
|
||||||
|
trigger,
|
||||||
|
MappedMutArc::new(
|
||||||
|
inner,
|
||||||
|
move |n| &n[index],
|
||||||
|
move |n| &mut n[index],
|
||||||
|
),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
|
||||||
|
let mut guard = self.writer()?;
|
||||||
|
guard.untrack();
|
||||||
|
Some(guard)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.keys()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> DefinedAt for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
fn defined_at(&self) -> Option<&'static Location<'static>> {
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
{
|
||||||
|
Some(self.defined_at)
|
||||||
|
}
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
{
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> IsDisposed for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: IsDisposed,
|
||||||
|
{
|
||||||
|
fn is_disposed(&self) -> bool {
|
||||||
|
self.inner.is_disposed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Trigger for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
fn trigger(&self) {
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
trigger.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Track for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
fn track(&self) {
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
trigger.track();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> ReadUntracked for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
type Value = <Self as StoreField>::Reader;
|
||||||
|
|
||||||
|
fn try_read_untracked(&self) -> Option<Self::Value> {
|
||||||
|
self.reader()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Writeable for AtKeyed<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
KeyedSubfield<Inner, Prev, K, T>: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized + 'static,
|
||||||
|
{
|
||||||
|
type Value = T::Output;
|
||||||
|
|
||||||
|
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
|
||||||
|
self.writer()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn try_write_untracked(
|
||||||
|
&self,
|
||||||
|
) -> Option<impl DerefMut<Target = Self::Value>> {
|
||||||
|
self.writer().map(|mut writer| {
|
||||||
|
writer.untrack();
|
||||||
|
writer
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
{
|
||||||
|
pub fn update_keys(&self) {
|
||||||
|
let inner_path = self.path().into_iter().collect();
|
||||||
|
let keys = self
|
||||||
|
.inner
|
||||||
|
.keys()
|
||||||
|
.expect("updating keys on a store with no keys");
|
||||||
|
keys.with_field_keys(
|
||||||
|
inner_path,
|
||||||
|
|keys| {
|
||||||
|
keys.update(self.latest_keys());
|
||||||
|
},
|
||||||
|
|| self.latest_keys(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> KeyedSubfield<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Self: Clone,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
Inner: StoreField<Value = Prev>,
|
||||||
|
Prev: 'static,
|
||||||
|
K: Debug + Send + Sync + PartialEq + Eq + Hash + 'static,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
T::Output: Sized,
|
||||||
|
{
|
||||||
|
#[track_caller]
|
||||||
|
pub fn iter_keyed(self) -> StoreFieldKeyedIter<Inner, Prev, K, T> {
|
||||||
|
// reactively track changes to this field
|
||||||
|
let trigger = self.get_trigger(self.path().into_iter().collect());
|
||||||
|
trigger.track();
|
||||||
|
|
||||||
|
// get the current length of the field by accessing slice
|
||||||
|
let reader = self
|
||||||
|
.reader()
|
||||||
|
.expect("creating iterator from unavailable store field");
|
||||||
|
let keys = reader
|
||||||
|
.into_iter()
|
||||||
|
.map(|item| (self.key_fn)(item))
|
||||||
|
.collect::<VecDeque<_>>();
|
||||||
|
|
||||||
|
// return the iterator
|
||||||
|
StoreFieldKeyedIter { inner: self, keys }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct StoreFieldKeyedIter<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
T: IndexMut<usize>,
|
||||||
|
{
|
||||||
|
inner: KeyedSubfield<Inner, Prev, K, T>,
|
||||||
|
keys: VecDeque<K>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<Inner, Prev, K, T> Iterator for StoreFieldKeyedIter<Inner, Prev, K, T>
|
||||||
|
where
|
||||||
|
Inner: StoreField<Value = Prev> + Clone + 'static,
|
||||||
|
T: IndexMut<usize> + 'static,
|
||||||
|
T::Output: Sized + 'static,
|
||||||
|
for<'a> &'a T: IntoIterator,
|
||||||
|
{
|
||||||
|
type Item = AtKeyed<Inner, Prev, K, T>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.keys
|
||||||
|
.pop_front()
|
||||||
|
.map(|key| AtKeyed::new(self.inner.clone(), key))
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,4 @@
|
||||||
|
use or_poisoned::OrPoisoned;
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
owner::{LocalStorage, Storage, StoredValue, SyncStorage},
|
owner::{LocalStorage, Storage, StoredValue, SyncStorage},
|
||||||
signal::{
|
signal::{
|
||||||
|
@ -8,7 +9,10 @@ use reactive_graph::{
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
use std::{
|
use std::{
|
||||||
|
any::Any,
|
||||||
|
collections::HashMap,
|
||||||
fmt::Debug,
|
fmt::Debug,
|
||||||
|
hash::Hash,
|
||||||
panic::Location,
|
panic::Location,
|
||||||
sync::{Arc, RwLock},
|
sync::{Arc, RwLock},
|
||||||
};
|
};
|
||||||
|
@ -16,6 +20,7 @@ use std::{
|
||||||
mod arc_field;
|
mod arc_field;
|
||||||
mod field;
|
mod field;
|
||||||
mod iter;
|
mod iter;
|
||||||
|
mod keyed;
|
||||||
mod option;
|
mod option;
|
||||||
mod patch;
|
mod patch;
|
||||||
mod path;
|
mod path;
|
||||||
|
@ -25,9 +30,10 @@ mod subfield;
|
||||||
pub use arc_field::ArcField;
|
pub use arc_field::ArcField;
|
||||||
pub use field::Field;
|
pub use field::Field;
|
||||||
pub use iter::*;
|
pub use iter::*;
|
||||||
|
pub use keyed::*;
|
||||||
pub use option::*;
|
pub use option::*;
|
||||||
pub use patch::*;
|
pub use patch::*;
|
||||||
use path::StorePath;
|
use path::{StorePath, StorePathSegment};
|
||||||
pub use store_field::{StoreField, Then};
|
pub use store_field::{StoreField, Then};
|
||||||
pub use subfield::Subfield;
|
pub use subfield::Subfield;
|
||||||
|
|
||||||
|
@ -51,11 +57,93 @@ impl TriggerMap {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct FieldKeys<K> {
|
||||||
|
current_key: usize,
|
||||||
|
keys: HashMap<K, (StorePathSegment, usize)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> FieldKeys<K>
|
||||||
|
where
|
||||||
|
K: Debug + Hash + PartialEq + Eq,
|
||||||
|
{
|
||||||
|
pub fn new(from_iter: impl IntoIterator<Item = K>) -> Self {
|
||||||
|
let mut current_key = 0;
|
||||||
|
let mut keys = HashMap::new();
|
||||||
|
for (idx, key) in from_iter.into_iter().enumerate() {
|
||||||
|
let segment = current_key.into();
|
||||||
|
keys.insert(key, (segment, idx));
|
||||||
|
current_key += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
Self {
|
||||||
|
current_key: 0,
|
||||||
|
keys,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> FieldKeys<K>
|
||||||
|
where
|
||||||
|
K: Hash + PartialEq + Eq,
|
||||||
|
{
|
||||||
|
pub fn get(&self, key: &K) -> Option<(StorePathSegment, usize)> {
|
||||||
|
self.keys.get(key).copied()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&mut self, iter: impl IntoIterator<Item = K>) {
|
||||||
|
for (idx, key) in iter.into_iter().enumerate() {
|
||||||
|
if let Some((_, old_idx)) = self.keys.get_mut(&key) {
|
||||||
|
*old_idx = idx;
|
||||||
|
} else {
|
||||||
|
self.current_key += 1;
|
||||||
|
self.keys.insert(
|
||||||
|
key,
|
||||||
|
(StorePathSegment::from(self.current_key), idx),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: remove old keys and triggers, so it doesn't grow constantly
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K> Default for FieldKeys<K> {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
current_key: Default::default(),
|
||||||
|
keys: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Clone)]
|
||||||
|
pub struct KeyMap(Arc<RwLock<HashMap<StorePath, Box<dyn Any + Send + Sync>>>>);
|
||||||
|
|
||||||
|
impl KeyMap {
|
||||||
|
pub fn with_field_keys<K, T>(
|
||||||
|
&self,
|
||||||
|
path: StorePath,
|
||||||
|
fun: impl FnOnce(&mut FieldKeys<K>) -> T,
|
||||||
|
initialize: impl FnOnce() -> Vec<K>,
|
||||||
|
) -> Option<T>
|
||||||
|
where
|
||||||
|
K: Debug + Hash + PartialEq + Eq + Send + Sync + 'static,
|
||||||
|
{
|
||||||
|
let mut guard = self.0.write().or_poisoned();
|
||||||
|
let entry = guard
|
||||||
|
.entry(path)
|
||||||
|
.or_insert_with(|| Box::new(FieldKeys::new(initialize())));
|
||||||
|
let entry = entry.downcast_mut::<FieldKeys<K>>()?;
|
||||||
|
Some(fun(entry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct ArcStore<T> {
|
pub struct ArcStore<T> {
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
defined_at: &'static Location<'static>,
|
defined_at: &'static Location<'static>,
|
||||||
pub(crate) value: Arc<RwLock<T>>,
|
pub(crate) value: Arc<RwLock<T>>,
|
||||||
signals: Arc<RwLock<TriggerMap>>,
|
signals: Arc<RwLock<TriggerMap>>,
|
||||||
|
keys: KeyMap,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T> ArcStore<T> {
|
impl<T> ArcStore<T> {
|
||||||
|
@ -65,6 +153,7 @@ impl<T> ArcStore<T> {
|
||||||
defined_at: Location::caller(),
|
defined_at: Location::caller(),
|
||||||
value: Arc::new(RwLock::new(value)),
|
value: Arc::new(RwLock::new(value)),
|
||||||
signals: Default::default(),
|
signals: Default::default(),
|
||||||
|
keys: Default::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,6 +176,7 @@ impl<T> Clone for ArcStore<T> {
|
||||||
defined_at: self.defined_at,
|
defined_at: self.defined_at,
|
||||||
value: Arc::clone(&self.value),
|
value: Arc::clone(&self.value),
|
||||||
signals: Arc::clone(&self.signals),
|
signals: Arc::clone(&self.signals),
|
||||||
|
keys: self.keys.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
path::{StorePath, StorePathSegment},
|
path::{StorePath, StorePathSegment},
|
||||||
ArcStore, Store,
|
ArcStore, KeyMap, Store,
|
||||||
};
|
};
|
||||||
use or_poisoned::OrPoisoned;
|
use or_poisoned::OrPoisoned;
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
|
@ -32,12 +32,20 @@ pub trait StoreField: Sized {
|
||||||
|
|
||||||
fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;
|
fn path(&self) -> impl IntoIterator<Item = StorePathSegment>;
|
||||||
|
|
||||||
|
fn track_field(&self) {
|
||||||
|
let path = self.path().into_iter().collect();
|
||||||
|
let trigger = self.get_trigger(path);
|
||||||
|
trigger.track();
|
||||||
|
}
|
||||||
|
|
||||||
fn reader(&self) -> Option<Self::Reader>;
|
fn reader(&self) -> Option<Self::Reader>;
|
||||||
|
|
||||||
fn writer(&self) -> Option<Self::Writer>;
|
fn writer(&self) -> Option<Self::Writer>;
|
||||||
|
|
||||||
fn untracked_writer(&self) -> Option<Self::UntrackedWriter>;
|
fn untracked_writer(&self) -> Option<Self::UntrackedWriter>;
|
||||||
|
|
||||||
|
fn keys(&self) -> Option<KeyMap>;
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn then<T>(
|
fn then<T>(
|
||||||
self,
|
self,
|
||||||
|
@ -86,6 +94,10 @@ where
|
||||||
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
|
fn untracked_writer(&self) -> Option<Self::UntrackedWriter> {
|
||||||
UntrackedWriteGuard::try_new(Arc::clone(&self.value))
|
UntrackedWriteGuard::try_new(Arc::clone(&self.value))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
Some(self.keys.clone())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> StoreField for Store<T, S>
|
impl<T, S> StoreField for Store<T, S>
|
||||||
|
@ -125,6 +137,10 @@ where
|
||||||
.try_get_value()
|
.try_get_value()
|
||||||
.and_then(|n| n.untracked_writer())
|
.and_then(|n| n.untracked_writer())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.try_get_value().and_then(|inner| inner.keys())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
@ -190,6 +206,11 @@ where
|
||||||
let inner = self.inner.untracked_writer()?;
|
let inner = self.inner.untracked_writer()?;
|
||||||
Some(MappedMut::new(inner, self.map_fn, self.map_fn_mut))
|
Some(MappedMut::new(inner, self.map_fn, self.map_fn_mut))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.keys()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T, S> DefinedAt for Then<T, S>
|
impl<T, S> DefinedAt for Then<T, S>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
path::{StorePath, StorePathSegment},
|
path::{StorePath, StorePathSegment},
|
||||||
store_field::StoreField,
|
store_field::StoreField,
|
||||||
|
KeyMap,
|
||||||
};
|
};
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
signal::{
|
signal::{
|
||||||
|
@ -15,10 +16,7 @@ use reactive_graph::{
|
||||||
use std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};
|
use std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Subfield<Inner, Prev, T>
|
pub struct Subfield<Inner, Prev, T> {
|
||||||
where
|
|
||||||
Inner: StoreField<Value = Prev>,
|
|
||||||
{
|
|
||||||
#[cfg(debug_assertions)]
|
#[cfg(debug_assertions)]
|
||||||
defined_at: &'static Location<'static>,
|
defined_at: &'static Location<'static>,
|
||||||
path_segment: StorePathSegment,
|
path_segment: StorePathSegment,
|
||||||
|
@ -30,7 +28,7 @@ where
|
||||||
|
|
||||||
impl<Inner, Prev, T> Clone for Subfield<Inner, Prev, T>
|
impl<Inner, Prev, T> Clone for Subfield<Inner, Prev, T>
|
||||||
where
|
where
|
||||||
Inner: StoreField<Value = Prev> + Clone,
|
Inner: Clone,
|
||||||
{
|
{
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
@ -45,15 +43,9 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev, T> Copy for Subfield<Inner, Prev, T> where
|
impl<Inner, Prev, T> Copy for Subfield<Inner, Prev, T> where Inner: Copy {}
|
||||||
Inner: StoreField<Value = Prev> + Copy
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<Inner, Prev, T> Subfield<Inner, Prev, T>
|
impl<Inner, Prev, T> Subfield<Inner, Prev, T> {
|
||||||
where
|
|
||||||
Inner: StoreField<Value = Prev>,
|
|
||||||
{
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
inner: Inner,
|
inner: Inner,
|
||||||
|
@ -111,6 +103,11 @@ where
|
||||||
let inner = WriteGuard::new(trigger, self.inner.untracked_writer()?);
|
let inner = WriteGuard::new(trigger, self.inner.untracked_writer()?);
|
||||||
Some(MappedMut::new(inner, self.read, self.write))
|
Some(MappedMut::new(inner, self.read, self.write))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[inline(always)]
|
||||||
|
fn keys(&self) -> Option<KeyMap> {
|
||||||
|
self.inner.keys()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>
|
impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>
|
||||||
|
@ -131,7 +128,7 @@ where
|
||||||
|
|
||||||
impl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>
|
impl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>
|
||||||
where
|
where
|
||||||
Inner: StoreField<Value = Prev> + IsDisposed,
|
Inner: IsDisposed,
|
||||||
{
|
{
|
||||||
fn is_disposed(&self) -> bool {
|
fn is_disposed(&self) -> bool {
|
||||||
self.inner.is_disposed()
|
self.inner.is_disposed()
|
||||||
|
|
|
@ -7,7 +7,7 @@ use syn::{
|
||||||
punctuated::Punctuated,
|
punctuated::Punctuated,
|
||||||
token::Comma,
|
token::Comma,
|
||||||
Field, Fields, Generics, Ident, Index, Meta, Result, Token, Type, Variant,
|
Field, Fields, Generics, Ident, Index, Meta, Result, Token, Type, Variant,
|
||||||
Visibility, WhereClause,
|
Visibility, WhereClause, ExprClosure,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[proc_macro_error]
|
#[proc_macro_error]
|
||||||
|
@ -79,17 +79,17 @@ impl Parse for Model {
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
enum SubfieldMode {
|
enum SubfieldMode {
|
||||||
Keyed(Ident, Type),
|
Keyed(ExprClosure, Type),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Parse for SubfieldMode {
|
impl Parse for SubfieldMode {
|
||||||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
|
||||||
let mode: Ident = input.parse()?;
|
let mode: Ident = input.parse()?;
|
||||||
if mode == "key" {
|
if mode == "key" {
|
||||||
let _eq: Token!(=) = input.parse()?;
|
|
||||||
let ident: Ident = input.parse()?;
|
|
||||||
let _col: Token!(:) = input.parse()?;
|
let _col: Token!(:) = input.parse()?;
|
||||||
let ty: Type = input.parse()?;
|
let ty: Type = input.parse()?;
|
||||||
|
let _eq: Token!(=) = input.parse()?;
|
||||||
|
let ident: ExprClosure = input.parse()?;
|
||||||
Ok(SubfieldMode::Keyed(ident, ty))
|
Ok(SubfieldMode::Keyed(ident, ty))
|
||||||
} else {
|
} else {
|
||||||
Err(input.error("expected `key = <ident>: <Type>`"))
|
Err(input.error("expected `key = <ident>: <Type>`"))
|
||||||
|
@ -281,15 +281,20 @@ fn field_to_tokens(
|
||||||
if modes.len() == 1 {
|
if modes.len() == 1 {
|
||||||
let mode = &modes[0];
|
let mode = &modes[0];
|
||||||
// Can replace with a match if additional modes added
|
// Can replace with a match if additional modes added
|
||||||
// TODO keyed_by
|
let SubfieldMode::Keyed(keyed_by, key_ty) = mode;
|
||||||
let SubfieldMode::Keyed(_keyed_by, key_ty) = mode;
|
|
||||||
let signature = quote! {
|
let signature = quote! {
|
||||||
fn #ident(self) -> #library_path::KeyedField<#any_store_field, #name #generics, #ty, #key_ty>
|
fn #ident(self) -> #library_path::KeyedSubfield<#any_store_field, #name #generics, #key_ty, #ty>
|
||||||
};
|
};
|
||||||
return if include_body {
|
return if include_body {
|
||||||
quote! {
|
quote! {
|
||||||
#signature {
|
#signature {
|
||||||
todo!()
|
#library_path::KeyedSubfield::new(
|
||||||
|
self,
|
||||||
|
#idx.into(),
|
||||||
|
#keyed_by,
|
||||||
|
|prev| &prev.#locator,
|
||||||
|
|prev| &mut prev.#locator,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -474,7 +479,9 @@ fn variant_to_tokens(
|
||||||
);
|
);
|
||||||
|
|
||||||
let ignore_before = (0..idx).map(|_| quote! { _, });
|
let ignore_before = (0..idx).map(|_| quote! { _, });
|
||||||
|
let ignore_before2 = ignore_before.clone();
|
||||||
let ignore_after = (idx..number_of_fields.saturating_sub(1)).map(|_| quote !{_, });
|
let ignore_after = (idx..number_of_fields.saturating_sub(1)).map(|_| quote !{_, });
|
||||||
|
let ignore_after2 = ignore_after.clone();
|
||||||
|
|
||||||
// default subfield
|
// default subfield
|
||||||
if include_body {
|
if include_body {
|
||||||
|
@ -497,7 +504,11 @@ fn variant_to_tokens(
|
||||||
.expect("accessed an enum field that is no longer matched")
|
.expect("accessed an enum field that is no longer matched")
|
||||||
},
|
},
|
||||||
|prev| {
|
|prev| {
|
||||||
todo!()
|
match prev {
|
||||||
|
#name::#orig_ident(#(#ignore_before2)* this, #(#ignore_after2)*) => Some(this),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
.expect("accessed an enum field that is no longer matched")
|
||||||
},
|
},
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
|
|
Loading…
Reference in a new issue