use super::ArcMemo;
use crate::{
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
signal::{
guards::{Mapped, Plain, ReadGuard},
ArcReadSignal,
},
traits::{DefinedAt, Dispose, Get, ReadUntracked, Track},
unwrap_signal,
};
use std::{fmt::Debug, hash::Hash, panic::Location};
pub struct Memo<T, S = SyncStorage>
where
S: Storage<T>,
{
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcMemo<T, S>, S>,
}
impl<T, S> Dispose for Memo<T, S>
where
S: Storage<T>,
{
fn dispose(self) {
self.inner.dispose()
}
}
impl<T> From<ArcMemo<T, SyncStorage>> for Memo<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcMemo<T, SyncStorage>) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<T> FromLocal<ArcMemo<T, LocalStorage>> for Memo<T, LocalStorage>
where
T: 'static,
{
#[track_caller]
fn from_local(value: ArcMemo<T, LocalStorage>) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<T> Memo<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "debug", skip_all)
)]
pub fn new(fun: impl Fn(Option<&T>) -> T + Send + Sync + 'static) -> Self
where
T: PartialEq,
{
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new(fun)),
}
}
#[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 {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new_with_compare(
fun, 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 {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcMemo::new_owning(fun)),
}
}
}
impl<T, S> Copy for Memo<T, S> where S: Storage<T> {}
impl<T, S> Clone for Memo<T, S>
where
S: Storage<T>,
{
fn clone(&self) -> Self {
*self
}
}
impl<T, S> Debug for Memo<T, S>
where
S: Debug + Storage<T>,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Memo")
.field("type", &std::any::type_name::<T>())
.field("store", &self.inner)
.finish()
}
}
impl<T, S> PartialEq for Memo<T, S>
where
S: Storage<T>,
{
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T, S> Eq for Memo<T, S> where S: Storage<T> {}
impl<T, S> Hash for Memo<T, S>
where
S: Storage<T>,
{
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<T, S> DefinedAt for Memo<T, S>
where
S: Storage<T>,
{
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> Track for Memo<T, S>
where
T: 'static,
S: Storage<ArcMemo<T, S>> + Storage<T>,
ArcMemo<T, S>: Track,
{
#[track_caller]
fn track(&self) {
if let Some(inner) = self.inner.try_get_value() {
inner.track();
}
}
}
impl<T, S> ReadUntracked for Memo<T, S>
where
T: 'static,
S: Storage<ArcMemo<T, S>> + Storage<T>,
{
type Value =
ReadGuard<T, Mapped<Plain<Option<<S as Storage<T>>::Wrapped>>, T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.inner
.try_get_value()
.map(|inner| inner.read_untracked())
}
}
impl<T, S> From<Memo<T, S>> for ArcMemo<T, S>
where
T: 'static,
S: Storage<ArcMemo<T, S>> + Storage<T>,
{
#[track_caller]
fn from(value: Memo<T, S>) -> Self {
value
.inner
.try_get_value()
.unwrap_or_else(unwrap_signal!(value))
}
}
impl<T> From<ArcReadSignal<T>> for Memo<T>
where
T: Clone + PartialEq + Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcReadSignal<T>) -> Self {
Memo::new(move |_| value.get())
}
}