use super::{
guards::{Plain, ReadGuard},
subscriber_traits::AsSubscriberSet,
ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, WriteSignal,
};
use crate::{
graph::{ReactiveNode, SubscriberSet},
owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
signal::guards::{UntrackedWriteGuard, WriteGuard},
traits::{
DefinedAt, Dispose, IntoInner, IsDisposed, Notify, ReadUntracked,
UntrackableGuard, Write,
},
unwrap_signal,
};
use core::fmt::Debug;
use guardian::ArcRwLockWriteGuardian;
use std::{
hash::Hash,
panic::Location,
sync::{Arc, RwLock},
};
pub struct RwSignal<T, S = SyncStorage> {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: &'static Location<'static>,
inner: ArenaItem<ArcRwSignal<T>, S>,
}
impl<T, S> Dispose for RwSignal<T, S> {
fn dispose(self) {
self.inner.dispose()
}
}
impl<T> RwSignal<T>
where
T: Send + Sync + 'static,
{
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
#[track_caller]
pub fn new(value: T) -> Self {
Self::new_with_storage(value)
}
}
impl<T, S> RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>>,
{
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
#[track_caller]
pub fn new_with_storage(value: T) -> Self {
Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcRwSignal::new(value)),
}
}
}
impl<T> RwSignal<T, LocalStorage>
where
T: 'static,
{
#[cfg_attr(
feature = "tracing",
tracing::instrument(level = "trace", skip_all)
)]
#[track_caller]
pub fn new_local(value: T) -> Self {
Self::new_with_storage(value)
}
}
impl<T, S> RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>> + Storage<ArcReadSignal<T>>,
{
#[inline(always)]
#[track_caller]
pub fn read_only(&self) -> ReadSignal<T, S> {
ReadSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
self.inner
.try_get_value()
.map(|inner| inner.read_only())
.unwrap_or_else(unwrap_signal!(self)),
),
}
}
}
impl<T, S> RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,
{
#[inline(always)]
#[track_caller]
pub fn write_only(&self) -> WriteSignal<T, S> {
WriteSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(
self.inner
.try_get_value()
.map(|inner| inner.write_only())
.unwrap_or_else(unwrap_signal!(self)),
),
}
}
}
impl<T, S> RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>>
+ Storage<ArcWriteSignal<T>>
+ Storage<ArcReadSignal<T>>,
{
#[track_caller]
#[inline(always)]
pub fn split(&self) -> (ReadSignal<T, S>, WriteSignal<T, S>) {
(self.read_only(), self.write_only())
}
#[track_caller]
pub fn unite(
read: ReadSignal<T, S>,
write: WriteSignal<T, S>,
) -> Option<Self> {
match (read.inner.try_get_value(), write.inner.try_get_value()) {
(Some(read), Some(write)) => {
if Arc::ptr_eq(&read.inner, &write.inner) {
Some(Self {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(ArcRwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
value: Arc::clone(&read.value),
inner: Arc::clone(&read.inner),
}),
})
} else {
None
}
}
_ => None,
}
}
}
impl<T, S> Copy for RwSignal<T, S> {}
impl<T, S> Clone for RwSignal<T, S> {
fn clone(&self) -> Self {
*self
}
}
impl<T, S> Debug for RwSignal<T, S>
where
S: Debug,
{
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("RwSignal")
.field("type", &std::any::type_name::<T>())
.field("store", &self.inner)
.finish()
}
}
impl<T, S> Default for RwSignal<T, S>
where
T: Default + 'static,
S: Storage<ArcRwSignal<T>>,
{
#[track_caller]
fn default() -> Self {
Self::new_with_storage(T::default())
}
}
impl<T, S> PartialEq for RwSignal<T, S> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T, S> Eq for RwSignal<T, S> {}
impl<T, S> Hash for RwSignal<T, S> {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.inner.hash(state);
}
}
impl<T, S> DefinedAt for RwSignal<T, S> {
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: 'static, S> IsDisposed for RwSignal<T, S> {
fn is_disposed(&self) -> bool {
self.inner.is_disposed()
}
}
impl<T, S> IntoInner for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
type Value = T;
#[inline(always)]
fn into_inner(self) -> Option<Self::Value> {
self.inner.into_inner()?.into_inner()
}
}
impl<T, S> AsSubscriberSet for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
type Output = Arc<RwLock<SubscriberSet>>;
fn as_subscriber_set(&self) -> Option<Self::Output> {
self.inner
.try_with_value(|inner| inner.as_subscriber_set())
.flatten()
}
}
impl<T, S> ReadUntracked for RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>>,
{
type Value = ReadGuard<T, Plain<T>>;
fn try_read_untracked(&self) -> Option<Self::Value> {
self.inner
.try_get_value()
.map(|inner| inner.read_untracked())
}
}
impl<T, S> Notify for RwSignal<T, S>
where
S: Storage<ArcRwSignal<T>>,
{
fn notify(&self) {
self.mark_dirty();
}
}
impl<T, S> Write for RwSignal<T, S>
where
T: 'static,
S: Storage<ArcRwSignal<T>>,
{
type Value = T;
fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
let guard = self.inner.try_with_value(|n| {
ArcRwLockWriteGuardian::take(Arc::clone(&n.value)).ok()
})??;
Some(WriteGuard::new(*self, guard))
}
#[allow(refining_impl_trait)]
fn try_write_untracked(&self) -> Option<UntrackedWriteGuard<Self::Value>> {
self.inner
.try_with_value(|n| n.try_write_untracked())
.flatten()
}
}
impl<T> From<ArcRwSignal<T>> for RwSignal<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
fn from(value: ArcRwSignal<T>) -> Self {
RwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<'a, T> From<&'a ArcRwSignal<T>> for RwSignal<T>
where
T: Send + Sync + 'static,
{
#[track_caller]
fn from(value: &'a ArcRwSignal<T>) -> Self {
value.clone().into()
}
}
impl<T> FromLocal<ArcRwSignal<T>> for RwSignal<T, LocalStorage>
where
T: 'static,
{
#[track_caller]
fn from_local(value: ArcRwSignal<T>) -> Self {
RwSignal {
#[cfg(any(debug_assertions, leptos_debuginfo))]
defined_at: Location::caller(),
inner: ArenaItem::new_with_storage(value),
}
}
}
impl<T, S> From<RwSignal<T, S>> for ArcRwSignal<T>
where
T: 'static,
S: Storage<ArcRwSignal<T>>,
{
#[track_caller]
fn from(value: RwSignal<T, S>) -> Self {
value
.inner
.try_get_value()
.unwrap_or_else(unwrap_signal!(value))
}
}