1use super::inner::MemoInner;
2use crate::{
3 graph::{
4 AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
5 ToAnySource, ToAnySubscriber,
6 },
7 owner::{Storage, StorageAccess, SyncStorage},
8 signal::{
9 guards::{Mapped, Plain, ReadGuard},
10 ArcReadSignal, ArcRwSignal,
11 },
12 traits::{DefinedAt, Get, IsDisposed, ReadUntracked},
13};
14use core::fmt::Debug;
15use std::{
16 hash::Hash,
17 panic::Location,
18 sync::{Arc, Weak},
19};
20
21pub struct ArcMemo<T, S = SyncStorage>
92where
93 S: Storage<T>,
94{
95 #[cfg(any(debug_assertions, leptos_debuginfo))]
96 defined_at: &'static Location<'static>,
97 inner: Arc<MemoInner<T, S>>,
98}
99
100impl<T: 'static> ArcMemo<T, SyncStorage>
101where
102 SyncStorage: Storage<T>,
103{
104 #[track_caller]
109 #[cfg_attr(
110 feature = "tracing",
111 tracing::instrument(level = "trace", skip_all)
112 )]
113 pub fn new(fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static) -> Self
114 where
115 T: PartialEq,
116 {
117 Self::new_with_compare(fun, |lhs, rhs| lhs.as_ref() != rhs.as_ref())
118 }
119
120 #[track_caller]
127 #[cfg_attr(
128 feature = "tracing",
129 tracing::instrument(level = "trace", skip_all)
130 )]
131 pub fn new_with_compare(
132 fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,
133 changed: fn(Option<&T>, Option<&T>) -> bool,
134 ) -> Self {
135 Self::new_owning(move |prev: Option<T>| {
136 let new_value = fun(prev.as_ref());
137 let changed = changed(prev.as_ref(), Some(&new_value));
138 (new_value, changed)
139 })
140 }
141
142 #[track_caller]
150 #[cfg_attr(
151 feature = "tracing",
152 tracing::instrument(level = "trace", skip_all)
153 )]
154 pub fn new_owning(
155 fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,
156 ) -> Self {
157 let inner = Arc::new_cyclic(|weak| {
158 let subscriber = AnySubscriber(
159 weak.as_ptr() as usize,
160 Weak::clone(weak) as Weak<dyn Subscriber + Send + Sync>,
161 );
162
163 MemoInner::new(Arc::new(fun), subscriber)
164 });
165 Self {
166 #[cfg(any(debug_assertions, leptos_debuginfo))]
167 defined_at: Location::caller(),
168 inner,
169 }
170 }
171}
172
173impl<T, S> Clone for ArcMemo<T, S>
174where
175 S: Storage<T>,
176{
177 fn clone(&self) -> Self {
178 Self {
179 #[cfg(any(debug_assertions, leptos_debuginfo))]
180 defined_at: self.defined_at,
181 inner: Arc::clone(&self.inner),
182 }
183 }
184}
185
186impl<T, S> DefinedAt for ArcMemo<T, S>
187where
188 S: Storage<T>,
189{
190 #[inline(always)]
191 fn defined_at(&self) -> Option<&'static Location<'static>> {
192 #[cfg(any(debug_assertions, leptos_debuginfo))]
193 {
194 Some(self.defined_at)
195 }
196 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
197 {
198 None
199 }
200 }
201}
202
203impl<T, S> Debug for ArcMemo<T, S>
204where
205 S: Storage<T>,
206{
207 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
208 f.debug_struct("ArcMemo")
209 .field("type", &std::any::type_name::<T>())
210 .field("data", &Arc::as_ptr(&self.inner))
211 .finish()
212 }
213}
214
215impl<T, S> PartialEq for ArcMemo<T, S>
216where
217 S: Storage<T>,
218{
219 fn eq(&self, other: &Self) -> bool {
220 Arc::ptr_eq(&self.inner, &other.inner)
221 }
222}
223
224impl<T, S> Eq for ArcMemo<T, S> where S: Storage<T> {}
225
226impl<T, S> Hash for ArcMemo<T, S>
227where
228 S: Storage<T>,
229{
230 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
231 std::ptr::hash(&Arc::as_ptr(&self.inner), state);
232 }
233}
234
235impl<T: 'static, S> ReactiveNode for ArcMemo<T, S>
236where
237 S: Storage<T>,
238{
239 fn mark_dirty(&self) {
240 self.inner.mark_dirty();
241 }
242
243 fn mark_check(&self) {
244 self.inner.mark_check();
245 }
246
247 fn mark_subscribers_check(&self) {
248 self.inner.mark_subscribers_check();
249 }
250
251 fn update_if_necessary(&self) -> bool {
252 self.inner.update_if_necessary()
253 }
254}
255
256impl<T: 'static, S> IsDisposed for ArcMemo<T, S>
257where
258 S: Storage<T>,
259{
260 #[inline(always)]
261 fn is_disposed(&self) -> bool {
262 false
263 }
264}
265
266impl<T: 'static, S> ToAnySource for ArcMemo<T, S>
267where
268 S: Storage<T>,
269{
270 fn to_any_source(&self) -> AnySource {
271 AnySource(
272 Arc::as_ptr(&self.inner) as usize,
273 Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,
274 #[cfg(any(debug_assertions, leptos_debuginfo))]
275 self.defined_at,
276 )
277 }
278}
279
280impl<T: 'static, S> Source for ArcMemo<T, S>
281where
282 S: Storage<T>,
283{
284 fn add_subscriber(&self, subscriber: AnySubscriber) {
285 self.inner.add_subscriber(subscriber);
286 }
287
288 fn remove_subscriber(&self, subscriber: &AnySubscriber) {
289 self.inner.remove_subscriber(subscriber);
290 }
291
292 fn clear_subscribers(&self) {
293 self.inner.clear_subscribers();
294 }
295}
296
297impl<T: 'static, S> ToAnySubscriber for ArcMemo<T, S>
298where
299 S: Storage<T>,
300{
301 fn to_any_subscriber(&self) -> AnySubscriber {
302 AnySubscriber(
303 Arc::as_ptr(&self.inner) as usize,
304 Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,
305 )
306 }
307}
308
309impl<T: 'static, S> Subscriber for ArcMemo<T, S>
310where
311 S: Storage<T>,
312{
313 fn add_source(&self, source: AnySource) {
314 self.inner.add_source(source);
315 }
316
317 fn clear_sources(&self, subscriber: &AnySubscriber) {
318 self.inner.clear_sources(subscriber);
319 }
320}
321
322impl<T: 'static, S> ReadUntracked for ArcMemo<T, S>
323where
324 S: Storage<T>,
325{
326 type Value = ReadGuard<T, Mapped<Plain<Option<S::Wrapped>>, T>>;
327
328 fn try_read_untracked(&self) -> Option<Self::Value> {
329 self.update_if_necessary();
330
331 Mapped::try_new(Arc::clone(&self.inner.value), |t| {
332 t.as_ref().unwrap().as_borrowed()
335 })
336 .map(ReadGuard::new)
337 }
338}
339
340impl<T> From<ArcReadSignal<T>> for ArcMemo<T, SyncStorage>
341where
342 T: Clone + PartialEq + Send + Sync + 'static,
343{
344 #[track_caller]
345 fn from(value: ArcReadSignal<T>) -> Self {
346 ArcMemo::new(move |_| value.get())
347 }
348}
349
350impl<T> From<ArcRwSignal<T>> for ArcMemo<T, SyncStorage>
351where
352 T: Clone + PartialEq + Send + Sync + 'static,
353{
354 #[track_caller]
355 fn from(value: ArcRwSignal<T>) -> Self {
356 ArcMemo::new(move |_| value.get())
357 }
358}