1use super::{
2 guards::{Plain, ReadGuard},
3 subscriber_traits::AsSubscriberSet,
4 ArcReadSignal, ArcRwSignal, ArcWriteSignal, ReadSignal, WriteSignal,
5};
6use crate::{
7 graph::{ReactiveNode, SubscriberSet},
8 owner::{ArenaItem, FromLocal, LocalStorage, Storage, SyncStorage},
9 signal::guards::{UntrackedWriteGuard, WriteGuard},
10 traits::{
11 DefinedAt, Dispose, IntoInner, IsDisposed, Notify, ReadUntracked,
12 UntrackableGuard, Write,
13 },
14 unwrap_signal,
15};
16use core::fmt::Debug;
17use guardian::ArcRwLockWriteGuardian;
18use std::{
19 hash::Hash,
20 panic::Location,
21 sync::{Arc, RwLock},
22};
23
24pub struct RwSignal<T, S = SyncStorage> {
103 #[cfg(any(debug_assertions, leptos_debuginfo))]
104 defined_at: &'static Location<'static>,
105 inner: ArenaItem<ArcRwSignal<T>, S>,
106}
107
108impl<T, S> Dispose for RwSignal<T, S> {
109 fn dispose(self) {
110 self.inner.dispose()
111 }
112}
113
114impl<T> RwSignal<T>
115where
116 T: Send + Sync + 'static,
117{
118 #[cfg_attr(
120 feature = "tracing",
121 tracing::instrument(level = "trace", skip_all)
122 )]
123 #[track_caller]
124 pub fn new(value: T) -> Self {
125 Self::new_with_storage(value)
126 }
127}
128
129impl<T, S> RwSignal<T, S>
130where
131 T: 'static,
132 S: Storage<ArcRwSignal<T>>,
133{
134 #[cfg_attr(
136 feature = "tracing",
137 tracing::instrument(level = "trace", skip_all)
138 )]
139 #[track_caller]
140 pub fn new_with_storage(value: T) -> Self {
141 Self {
142 #[cfg(any(debug_assertions, leptos_debuginfo))]
143 defined_at: Location::caller(),
144 inner: ArenaItem::new_with_storage(ArcRwSignal::new(value)),
145 }
146 }
147}
148
149impl<T> RwSignal<T, LocalStorage>
150where
151 T: 'static,
152{
153 #[cfg_attr(
156 feature = "tracing",
157 tracing::instrument(level = "trace", skip_all)
158 )]
159 #[track_caller]
160 pub fn new_local(value: T) -> Self {
161 Self::new_with_storage(value)
162 }
163}
164
165impl<T, S> RwSignal<T, S>
166where
167 T: 'static,
168 S: Storage<ArcRwSignal<T>> + Storage<ArcReadSignal<T>>,
169{
170 #[inline(always)]
172 #[track_caller]
173 pub fn read_only(&self) -> ReadSignal<T, S> {
174 ReadSignal {
175 #[cfg(any(debug_assertions, leptos_debuginfo))]
176 defined_at: Location::caller(),
177 inner: ArenaItem::new_with_storage(
178 self.inner
179 .try_get_value()
180 .map(|inner| inner.read_only())
181 .unwrap_or_else(unwrap_signal!(self)),
182 ),
183 }
184 }
185}
186
187impl<T, S> RwSignal<T, S>
188where
189 T: 'static,
190 S: Storage<ArcRwSignal<T>> + Storage<ArcWriteSignal<T>>,
191{
192 #[inline(always)]
194 #[track_caller]
195 pub fn write_only(&self) -> WriteSignal<T, S> {
196 WriteSignal {
197 #[cfg(any(debug_assertions, leptos_debuginfo))]
198 defined_at: Location::caller(),
199 inner: ArenaItem::new_with_storage(
200 self.inner
201 .try_get_value()
202 .map(|inner| inner.write_only())
203 .unwrap_or_else(unwrap_signal!(self)),
204 ),
205 }
206 }
207}
208
209impl<T, S> RwSignal<T, S>
210where
211 T: 'static,
212 S: Storage<ArcRwSignal<T>>
213 + Storage<ArcWriteSignal<T>>
214 + Storage<ArcReadSignal<T>>,
215{
216 #[track_caller]
218 #[inline(always)]
219 pub fn split(&self) -> (ReadSignal<T, S>, WriteSignal<T, S>) {
220 (self.read_only(), self.write_only())
221 }
222
223 #[track_caller]
226 pub fn unite(
227 read: ReadSignal<T, S>,
228 write: WriteSignal<T, S>,
229 ) -> Option<Self> {
230 match (read.inner.try_get_value(), write.inner.try_get_value()) {
231 (Some(read), Some(write)) => {
232 if Arc::ptr_eq(&read.inner, &write.inner) {
233 Some(Self {
234 #[cfg(any(debug_assertions, leptos_debuginfo))]
235 defined_at: Location::caller(),
236 inner: ArenaItem::new_with_storage(ArcRwSignal {
237 #[cfg(any(debug_assertions, leptos_debuginfo))]
238 defined_at: Location::caller(),
239 value: Arc::clone(&read.value),
240 inner: Arc::clone(&read.inner),
241 }),
242 })
243 } else {
244 None
245 }
246 }
247 _ => None,
248 }
249 }
250}
251
252impl<T, S> Copy for RwSignal<T, S> {}
253
254impl<T, S> Clone for RwSignal<T, S> {
255 fn clone(&self) -> Self {
256 *self
257 }
258}
259
260impl<T, S> Debug for RwSignal<T, S>
261where
262 S: Debug,
263{
264 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
265 f.debug_struct("RwSignal")
266 .field("type", &std::any::type_name::<T>())
267 .field("store", &self.inner)
268 .finish()
269 }
270}
271
272impl<T, S> Default for RwSignal<T, S>
273where
274 T: Default + 'static,
275 S: Storage<ArcRwSignal<T>>,
276{
277 #[track_caller]
278 fn default() -> Self {
279 Self::new_with_storage(T::default())
280 }
281}
282
283impl<T, S> PartialEq for RwSignal<T, S> {
284 fn eq(&self, other: &Self) -> bool {
285 self.inner == other.inner
286 }
287}
288
289impl<T, S> Eq for RwSignal<T, S> {}
290
291impl<T, S> Hash for RwSignal<T, S> {
292 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
293 self.inner.hash(state);
294 }
295}
296
297impl<T, S> DefinedAt for RwSignal<T, S> {
298 fn defined_at(&self) -> Option<&'static Location<'static>> {
299 #[cfg(any(debug_assertions, leptos_debuginfo))]
300 {
301 Some(self.defined_at)
302 }
303 #[cfg(not(any(debug_assertions, leptos_debuginfo)))]
304 {
305 None
306 }
307 }
308}
309
310impl<T: 'static, S> IsDisposed for RwSignal<T, S> {
311 fn is_disposed(&self) -> bool {
312 self.inner.is_disposed()
313 }
314}
315
316impl<T, S> IntoInner for RwSignal<T, S>
317where
318 S: Storage<ArcRwSignal<T>>,
319{
320 type Value = T;
321
322 #[inline(always)]
323 fn into_inner(self) -> Option<Self::Value> {
324 self.inner.into_inner()?.into_inner()
325 }
326}
327
328impl<T, S> AsSubscriberSet for RwSignal<T, S>
329where
330 S: Storage<ArcRwSignal<T>>,
331{
332 type Output = Arc<RwLock<SubscriberSet>>;
333
334 fn as_subscriber_set(&self) -> Option<Self::Output> {
335 self.inner
336 .try_with_value(|inner| inner.as_subscriber_set())
337 .flatten()
338 }
339}
340
341impl<T, S> ReadUntracked for RwSignal<T, S>
342where
343 T: 'static,
344 S: Storage<ArcRwSignal<T>>,
345{
346 type Value = ReadGuard<T, Plain<T>>;
347
348 fn try_read_untracked(&self) -> Option<Self::Value> {
349 self.inner
350 .try_get_value()
351 .map(|inner| inner.read_untracked())
352 }
353}
354
355impl<T, S> Notify for RwSignal<T, S>
356where
357 S: Storage<ArcRwSignal<T>>,
358{
359 fn notify(&self) {
360 self.mark_dirty();
361 }
362}
363
364impl<T, S> Write for RwSignal<T, S>
365where
366 T: 'static,
367 S: Storage<ArcRwSignal<T>>,
368{
369 type Value = T;
370
371 fn try_write(&self) -> Option<impl UntrackableGuard<Target = Self::Value>> {
372 let guard = self.inner.try_with_value(|n| {
373 ArcRwLockWriteGuardian::take(Arc::clone(&n.value)).ok()
374 })??;
375 Some(WriteGuard::new(*self, guard))
376 }
377
378 #[allow(refining_impl_trait)]
379 fn try_write_untracked(&self) -> Option<UntrackedWriteGuard<Self::Value>> {
380 self.inner
381 .try_with_value(|n| n.try_write_untracked())
382 .flatten()
383 }
384}
385
386impl<T> From<ArcRwSignal<T>> for RwSignal<T>
387where
388 T: Send + Sync + 'static,
389{
390 #[track_caller]
391 fn from(value: ArcRwSignal<T>) -> Self {
392 RwSignal {
393 #[cfg(any(debug_assertions, leptos_debuginfo))]
394 defined_at: Location::caller(),
395 inner: ArenaItem::new_with_storage(value),
396 }
397 }
398}
399
400impl<'a, T> From<&'a ArcRwSignal<T>> for RwSignal<T>
401where
402 T: Send + Sync + 'static,
403{
404 #[track_caller]
405 fn from(value: &'a ArcRwSignal<T>) -> Self {
406 value.clone().into()
407 }
408}
409
410impl<T> FromLocal<ArcRwSignal<T>> for RwSignal<T, LocalStorage>
411where
412 T: 'static,
413{
414 #[track_caller]
415 fn from_local(value: ArcRwSignal<T>) -> Self {
416 RwSignal {
417 #[cfg(any(debug_assertions, leptos_debuginfo))]
418 defined_at: Location::caller(),
419 inner: ArenaItem::new_with_storage(value),
420 }
421 }
422}
423
424impl<T, S> From<RwSignal<T, S>> for ArcRwSignal<T>
425where
426 T: 'static,
427 S: Storage<ArcRwSignal<T>>,
428{
429 #[track_caller]
430 fn from(value: RwSignal<T, S>) -> Self {
431 value
432 .inner
433 .try_get_value()
434 .unwrap_or_else(unwrap_signal!(value))
435 }
436}