1use crate::{
2 path::{StorePath, StorePathSegment},
3 store_field::StoreField,
4 KeyMap, StoreFieldTrigger,
5};
6use reactive_graph::{
7 signal::{
8 guards::{Mapped, MappedMut, WriteGuard},
9 ArcTrigger,
10 },
11 traits::{
12 DefinedAt, Get as _, IsDisposed, Notify, ReadUntracked, Track,
13 UntrackableGuard, Write,
14 },
15 wrappers::read::Signal,
16};
17use std::{iter, marker::PhantomData, ops::DerefMut, panic::Location};
18
19#[derive(Debug)]
21pub struct Subfield<Inner, Prev, T> {
22 #[cfg(any(debug_assertions, leptos_debuginfo))]
23 defined_at: &'static Location<'static>,
24 path_segment: StorePathSegment,
25 inner: Inner,
26 read: fn(&Prev) -> &T,
27 write: fn(&mut Prev) -> &mut T,
28 ty: PhantomData<T>,
29}
30
31impl<Inner, Prev, T> Clone for Subfield<Inner, Prev, T>
32where
33 Inner: Clone,
34{
35 fn clone(&self) -> Self {
36 Self {
37 #[cfg(any(debug_assertions, leptos_debuginfo))]
38 defined_at: self.defined_at,
39 path_segment: self.path_segment,
40 inner: self.inner.clone(),
41 read: self.read,
42 write: self.write,
43 ty: self.ty,
44 }
45 }
46}
47
48impl<Inner, Prev, T> Copy for Subfield<Inner, Prev, T> where Inner: Copy {}
49
50impl<Inner, Prev, T> Subfield<Inner, Prev, T> {
51 #[track_caller]
53 pub fn new(
54 inner: Inner,
55 path_segment: StorePathSegment,
56 read: fn(&Prev) -> &T,
57 write: fn(&mut Prev) -> &mut T,
58 ) -> Self {
59 Self {
60 #[cfg(any(debug_assertions, leptos_debuginfo))]
61 defined_at: Location::caller(),
62 inner,
63 path_segment,
64 read,
65 write,
66 ty: PhantomData,
67 }
68 }
69}
70
71impl<Inner, Prev, T> StoreField for Subfield<Inner, Prev, T>
72where
73 Inner: StoreField<Value = Prev>,
74 Prev: 'static,
75{
76 type Value = T;
77 type Reader = Mapped<Inner::Reader, T>;
78 type Writer = MappedMut<WriteGuard<Vec<ArcTrigger>, Inner::Writer>, T>;
79
80 fn path(&self) -> impl IntoIterator<Item = StorePathSegment> {
81 self.inner
82 .path()
83 .into_iter()
84 .chain(iter::once(self.path_segment))
85 }
86
87 fn path_unkeyed(&self) -> impl IntoIterator<Item = StorePathSegment> {
88 self.inner
89 .path_unkeyed()
90 .into_iter()
91 .chain(iter::once(self.path_segment))
92 }
93
94 fn get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
95 self.inner.get_trigger(path)
96 }
97
98 fn get_trigger_unkeyed(&self, path: StorePath) -> StoreFieldTrigger {
99 self.inner.get_trigger_unkeyed(path)
100 }
101
102 fn reader(&self) -> Option<Self::Reader> {
103 let inner = self.inner.reader()?;
104 Some(Mapped::new_with_guard(inner, self.read))
105 }
106
107 fn writer(&self) -> Option<Self::Writer> {
108 let mut parent = self.inner.writer()?;
109
110 parent.untrack();
115 let triggers = self.triggers_for_current_path();
116 let guard = WriteGuard::new(triggers, parent);
117 Some(MappedMut::new(guard, self.read, self.write))
118 }
119
120 #[inline(always)]
121 fn keys(&self) -> Option<KeyMap> {
122 self.inner.keys()
123 }
124
125 #[track_caller]
126 fn track_field(&self) {
127 let mut full_path = self.path().into_iter().collect::<StorePath>();
128 let trigger = self.get_trigger(self.path().into_iter().collect());
129 trigger.this.track();
130 trigger.children.track();
131
132 while !full_path.is_empty() {
137 full_path.pop();
138 let inner = self.get_trigger(full_path.clone());
139 inner.this.track();
140 }
141 }
142}
143
144impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>
145where
146 Inner: StoreField<Value = Prev>,
147{
148 fn defined_at(&self) -> Option<&'static Location<'static>> {
149 #[cfg(any(debug_assertions, leptos_debuginfo))]
150 {
151 Some(self.defined_at)
152 }
153 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
154 {
155 None
156 }
157 }
158}
159
160impl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>
161where
162 Inner: IsDisposed,
163{
164 fn is_disposed(&self) -> bool {
165 self.inner.is_disposed()
166 }
167}
168
169impl<Inner, Prev, T> Notify for Subfield<Inner, Prev, T>
170where
171 Inner: StoreField<Value = Prev>,
172 Prev: 'static,
173{
174 #[track_caller]
175 fn notify(&self) {
176 let trigger = self.get_trigger(self.path().into_iter().collect());
177 trigger.this.notify();
178 trigger.children.notify();
179 }
180}
181
182impl<Inner, Prev, T> Track for Subfield<Inner, Prev, T>
183where
184 Inner: StoreField<Value = Prev> + Track + 'static,
185 Prev: 'static,
186 T: 'static,
187{
188 #[track_caller]
189 fn track(&self) {
190 self.track_field();
191 }
192}
193
194impl<Inner, Prev, T> ReadUntracked for Subfield<Inner, Prev, T>
195where
196 Inner: StoreField<Value = Prev>,
197 Prev: 'static,
198{
199 type Value = <Self as StoreField>::Reader;
200
201 fn try_read_untracked(&self) -> Option<Self::Value> {
202 self.reader()
203 }
204}
205
206impl<Inner, Prev, T> Write for Subfield<Inner, Prev, T>
207where
208 T: 'static,
209 Inner: StoreField<Value = Prev>,
210 Prev: 'static,
211{
212 type Value = T;
213
214 fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
215 self.writer()
216 }
217
218 fn try_write_untracked(
219 &self,
220 ) -> Option<impl DerefMut<Target = Self::Value>> {
221 self.writer().map(|mut writer| {
222 writer.untrack();
223 writer
224 })
225 }
226}
227
228impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Signal<T>
229where
230 Inner: StoreField<Value = Prev> + Track + Send + Sync + 'static,
231 Prev: 'static,
232 T: Send + Sync + Clone + 'static,
233{
234 fn from(subfield: Subfield<Inner, Prev, T>) -> Self {
235 Signal::derive(move || subfield.get())
236 }
237}