use super::inner::MemoInner;
use crate::{
graph::{
AnySource, AnySubscriber, ReactiveNode, Source, Subscriber,
ToAnySource, ToAnySubscriber,
},
owner::{Storage, StorageAccess, SyncStorage},
signal::{
guards::{Mapped, Plain, ReadGuard},
ArcReadSignal, ArcRwSignal,
},
traits::{DefinedAt, Get, IsDisposed, ReadUntracked},
};
use core::fmt::Debug;
use std::{
hash::Hash,
panic::Location,
sync::{Arc, Weak},
};
pub struct ArcMemo<T, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: &'static Location<'static>,
inner: Arc<MemoInner<T, S>>,
}
impl<T: 'static> ArcMemo<T, SyncStorage>
where
SyncStorage: Storage<T>,
{
#[track_caller]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn new(fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static) -> Self
where
T: PartialEq,
{
Self::new_with_compare(fun, |lhs, rhs| lhs.as_ref() != rhs.as_ref())
}
#[track_caller]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn new_with_compare(
fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static,
changed: fn(Option<&T>, Option<&T>) -> bool,
) -> Self {
Self::new_owning(move |prev: Option<T>| {
let new_value = fun(prev.as_ref());
let changed = changed(prev.as_ref(), Some(&new_value));
(new_value, changed)
})
}
#[track_caller]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
pub fn new_owning(
fun: impl Fn(Option<T>) -> (T, bool) + Send + Sync + 'static,
) -> Self {
let inner = Arc::new_cyclic(|weak| {
let subscriber = AnySubscriber(
weak.as_ptr() as usize,
Weak::clone(weak) as Weak<dyn Subscriber + Send + Sync>,
);
MemoInner::new(Arc::new(fun), subscriber)
});
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner,
}
}
}
impl<T, S> Clone for ArcMemo<T, S>
where
S: Storage<T>,
{
fn clone(&self) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: self.defined_at,
inner: Arc::clone(&self.inner),
}
}
}
impl<T, S> DefinedAt for ArcMemo<T, S>
where
S: Storage<T>,
{
#[inline(always)]
fn defined_at(&self) -> Option<&'static Location<'static>> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
{
Some(self.defined_at)
}
#[cfg(not(any(debug_assertions, leptos_debuginfo)))]
{
None
}
}
}
impl<T, S> Debug for ArcMemo<T, S>
where
S: Storage<T>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("ArcMemo")
.field("type", &std::any::type_name::<T>())
.field("data", &Arc::as_ptr(&self.inner))
.finish()
}
}
impl<T, S> PartialEq for ArcMemo<T, S>
where
S: Storage<T>,
{
fn eq(&self, other: &Self) -> bool {
Arc::ptr_eq(&self.inner, &other.inner)
}
}
impl<T, S> Eq for ArcMemo<T, S> where S: Storage<T> {}
impl<T, S> Hash for ArcMemo<T, S>
where
S: Storage<T>,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
std::ptr::hash(&Arc::as_ptr(&self.inner), state);
}
}
impl<T: 'static, S> ReactiveNode for ArcMemo<T, S>
where
S: Storage<T>,
{
fn mark_dirty(&self) {
self.inner.mark_dirty();
}
fn mark_check(&self) {
self.inner.mark_check();
}
fn mark_subscribers_check(&self) {
self.inner.mark_subscribers_check();
}
fn update_if_necessary(&self) -> bool {
self.inner.update_if_necessary()
}
}
impl<T: 'static, S> IsDisposed for ArcMemo<T, S>
where
S: Storage<T>,
{
#[inline(always)]
fn is_disposed(&self) -> bool {
false
}
}
impl<T: 'static, S> ToAnySource for ArcMemo<T, S>
where
S: Storage<T>,
{
fn to_any_source(&self) -> AnySource {
AnySource(
Arc::as_ptr(&self.inner) as usize,
Arc::downgrade(&self.inner) as Weak<dyn Source + Send + Sync>,
#[cfg(any(debug_assertions, leptos_debuginfo))]
self.defined_at,
)
}
}
impl<T: 'static, S> Source for ArcMemo<T, S>
where
S: Storage<T>,
{
fn add_subscriber(&self, subscriber: AnySubscriber) {
self.inner.add_subscriber(subscriber);
}
fn remove_subscriber(&self, subscriber: &AnySubscriber) {
self.inner.remove_subscriber(subscriber);
}
fn clear_subscribers(&self) {
self.inner.clear_subscribers();
}
}
impl<T: 'static, S> ToAnySubscriber for ArcMemo<T, S>
where
S: Storage<T>,
{
fn to_any_subscriber(&self) -> AnySubscriber {
AnySubscriber(
Arc::as_ptr(&self.inner) as usize,
Arc::downgrade(&self.inner) as Weak<dyn Subscriber + Send + Sync>,
)
}
}
impl<T: 'static, S> Subscriber for ArcMemo<T, S>
where
S: Storage<T>,
{
fn add_source(&self, source: AnySource) {
self.inner.add_source(source);
}
fn clear_sources(&self, subscriber: &AnySubscriber) {
self.inner.clear_sources(subscriber);
}
}
impl<T: 'static, S> ReadUntracked for ArcMemo<T, S>
where
S: Storage<T>,
{
type Value = ReadGuard<T, Mapped<Plain<Option<S::Wrapped>>, T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.update_if_necessary();
Mapped::try_new(Arc::clone(&self.inner.value), |t| {
t.as_ref().unwrap().as_borrowed()
})
.map(ReadGuard::new)
}
}
impl<T> From<ArcReadSignal<T>> for ArcMemo<T, SyncStorage>
where
T: Clone + PartialEq + Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcReadSignal<T>) -> Self {
ArcMemo::new(move |_| value.get())
}
}
impl<T> From<ArcRwSignal<T>> for ArcMemo<T, SyncStorage>
where
T: Clone + PartialEq + Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcRwSignal<T>) -> Self {
ArcMemo::new(move |_| value.get())
}
}