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 get_trigger(&self, path: StorePath) -> StoreFieldTrigger {
88 self.inner.get_trigger(path)
89 }
90
91 fn reader(&self) -> Option<Self::Reader> {
92 let inner = self.inner.reader()?;
93 Some(Mapped::new_with_guard(inner, self.read))
94 }
95
96 fn writer(&self) -> Option<Self::Writer> {
97 let trigger = self.get_trigger(self.path().into_iter().collect());
98 let mut parent = self.inner.writer()?;
99 parent.untrack();
100
101 let mut full_path = self.path().into_iter().collect::<StorePath>();
102 full_path.pop();
103
104 let mut triggers = Vec::with_capacity(full_path.len());
107 triggers.push(trigger.this.clone());
108 loop {
109 let inner = self.get_trigger(full_path.clone());
110 triggers.push(inner.children.clone());
111 if full_path.is_empty() {
112 break;
113 }
114 full_path.pop();
115 }
116
117 triggers.reverse();
126
127 let guard = WriteGuard::new(triggers, parent);
128
129 Some(MappedMut::new(guard, self.read, self.write))
130 }
131
132 #[inline(always)]
133 fn keys(&self) -> Option<KeyMap> {
134 self.inner.keys()
135 }
136
137 #[track_caller]
138 fn track_field(&self) {
139 let mut full_path = self.path().into_iter().collect::<StorePath>();
140 loop {
145 let inner = self.get_trigger(full_path.clone());
146 inner.this.track();
147 if full_path.is_empty() {
148 break;
149 }
150 full_path.pop();
151 }
152 let trigger = self.get_trigger(self.path().into_iter().collect());
153 trigger.this.track();
154 trigger.children.track();
155 }
156}
157
158impl<Inner, Prev, T> DefinedAt for Subfield<Inner, Prev, T>
159where
160 Inner: StoreField<Value = Prev>,
161{
162 fn defined_at(&self) -> Option<&'static Location<'static>> {
163 #[cfg(any(debug_assertions, leptos_debuginfo))]
164 {
165 Some(self.defined_at)
166 }
167 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
168 {
169 None
170 }
171 }
172}
173
174impl<Inner, Prev, T> IsDisposed for Subfield<Inner, Prev, T>
175where
176 Inner: IsDisposed,
177{
178 fn is_disposed(&self) -> bool {
179 self.inner.is_disposed()
180 }
181}
182
183impl<Inner, Prev, T> Notify for Subfield<Inner, Prev, T>
184where
185 Inner: StoreField<Value = Prev>,
186 Prev: 'static,
187{
188 #[track_caller]
189 fn notify(&self) {
190 let trigger = self.get_trigger(self.path().into_iter().collect());
191 trigger.this.notify();
192 trigger.children.notify();
193 }
194}
195
196impl<Inner, Prev, T> Track for Subfield<Inner, Prev, T>
197where
198 Inner: StoreField<Value = Prev> + Track + 'static,
199 Prev: 'static,
200 T: 'static,
201{
202 #[track_caller]
203 fn track(&self) {
204 self.track_field();
205 }
206}
207
208impl<Inner, Prev, T> ReadUntracked for Subfield<Inner, Prev, T>
209where
210 Inner: StoreField<Value = Prev>,
211 Prev: 'static,
212{
213 type Value = <Self as StoreField>::Reader;
214
215 fn try_read_untracked(&self) -> Option<Self::Value> {
216 self.reader()
217 }
218}
219
220impl<Inner, Prev, T> Write for Subfield<Inner, Prev, T>
221where
222 T: 'static,
223 Inner: StoreField<Value = Prev>,
224 Prev: 'static,
225{
226 type Value = T;
227
228 fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
229 self.writer()
230 }
231
232 fn try_write_untracked(
233 &self,
234 ) -> Option<impl DerefMut<Target = Self::Value>> {
235 self.writer().map(|mut writer| {
236 writer.untrack();
237 writer
238 })
239 }
240}
241
242impl<Inner, Prev, T> From<Subfield<Inner, Prev, T>> for Signal<T>
243where
244 Inner: StoreField<Value = Prev> + Track + Send + Sync + 'static,
245 Prev: 'static,
246 T: Send + Sync + Clone + 'static,
247{
248 fn from(subfield: Subfield<Inner, Prev, T>) -> Self {
249 Signal::derive(move || subfield.get())
250 }
251}