shared_container/
lib.rs

1//! # Shared Container Module
2//!
3//! This module provides a unified abstraction over different container types
4//! used for shared data access with interior mutability in different contexts.
5//!
6//! It abstracts over the differences between:
7//! - `Arc<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
8//! - `Arc<tokio::sync::RwLock<T>>` (used for async/await support)
9//! - `Rc<RefCell<T>>` (used in single-threaded environments like WebAssembly)
10//!
11//! This allows code using these containers to be written once but work efficiently
12//! in different contexts.
13//!
14//! ## Feature Flags
15//!
16//! This library provides several feature flags to customize its behavior:
17//!
18//! - **std-sync** (default): Uses `Arc<std::sync::RwLock<T>>` for thread-safe access
19//! - **tokio-sync**: Uses `Arc<tokio::sync::RwLock<T>>` for async/await support
20//! - **wasm-sync**: Uses `Rc<RefCell<T>>` for single-threaded environments
21//! - **force-wasm-impl**: Legacy feature, equivalent to wasm-sync
22//!
23//! ## Async Support
24//!
25//! When the `tokio-sync` feature is enabled, the library provides async versions of the read and write methods:
26//!
27//! ```rust
28//! # #[cfg(feature = "tokio-sync")]
29//! # async fn example() {
30//! # use shared_container::SharedContainer;
31//! let container = SharedContainer::new(42);
32//!
33//! // Read access
34//! let guard = container.read_async().await;
35//! println!("Value: {}", *guard);
36//!
37//! // Write access
38//! let mut guard = container.write_async().await;
39//! *guard = 100;
40//! # }
41//! ```
42//!
43//! Note that when using the `tokio-sync` feature, the synchronous `read()` and `write()` methods
44//! will always return `None`. You should use the async methods instead.
45
46#![cfg_attr(docsrs, feature(doc_cfg))]
47
48use std::ops::{Deref, DerefMut};
49
50/// Custom attribute to warn when using synchronous methods with tokio-sync
51///
52/// This attribute will generate a warning during compilation when the `tokio-sync` feature
53/// is enabled, but won't mark the function as deprecated in the documentation.
54#[cfg(feature = "tokio-sync")]
55#[doc(hidden)]
56macro_rules! tokio_sync_warning {
57    ($msg:expr) => {
58        #[doc(hidden)]
59        #[allow(deprecated)]
60        struct __TokioSyncWarning;
61
62        impl __TokioSyncWarning {
63            #[deprecated(note = $msg)]
64            #[doc(hidden)]
65            fn __warn() {}
66        }
67
68        // Call the deprecated function to trigger the warning
69        let _ = __TokioSyncWarning::__warn();
70    };
71}
72
73// #[cfg(not(feature = "tokio-sync"))]
74// #[doc(hidden)]
75// #[allow(dead_code)]
76// macro_rules! tokio_sync_warning {
77//     ($msg:expr) => {};
78// }
79
80// Standard library synchronization primitives (default)
81#[cfg(all(
82    feature = "std-sync",
83    not(feature = "tokio-sync"),
84    not(feature = "wasm-sync")
85))]
86use std::sync::{Arc, RwLock, Weak};
87
88// Tokio async synchronization primitives
89#[cfg(feature = "tokio-sync")]
90use std::sync::{Arc, Weak};
91#[cfg(feature = "tokio-sync")]
92use tokio::sync::RwLock;
93
94// WebAssembly/single-threaded synchronization primitives
95#[cfg(any(
96    feature = "wasm-sync",
97    all(
98        target_arch = "wasm32",
99        not(feature = "std-sync"),
100        not(feature = "tokio-sync")
101    )
102))]
103use std::cell::{Ref, RefCell, RefMut};
104#[cfg(any(
105    feature = "wasm-sync",
106    all(
107        target_arch = "wasm32",
108        not(feature = "std-sync"),
109        not(feature = "tokio-sync")
110    )
111))]
112use std::rc::{Rc, Weak as RcWeak};
113
114/// A unified container for shared data that works in both multi-threaded and single-threaded environments.
115///
116/// This struct provides an abstraction over different container types:
117/// - `Arc<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
118/// - `Arc<tokio::sync::RwLock<T>>` (used for async/await support)
119/// - `Rc<RefCell<T>>` (used in single-threaded environments like WebAssembly)
120///
121/// It allows code to be written once but compile to the most efficient implementation
122/// based on the environment where it will run and the features enabled.
123#[derive(Debug)]
124pub struct SharedContainer<T> {
125    // Standard library thread-safe implementation
126    #[cfg(all(
127        feature = "std-sync",
128        not(feature = "tokio-sync"),
129        not(feature = "wasm-sync")
130    ))]
131    std_inner: Arc<RwLock<T>>,
132
133    // Tokio async implementation
134    #[cfg(feature = "tokio-sync")]
135    tokio_inner: Arc<RwLock<T>>,
136
137    // Single-threaded implementation for WebAssembly
138    #[cfg(any(
139        feature = "wasm-sync",
140        all(
141            target_arch = "wasm32",
142            not(feature = "std-sync"),
143            not(feature = "tokio-sync")
144        )
145    ))]
146    wasm_inner: Rc<RefCell<T>>,
147}
148
149// Implement Send and Sync for SharedContainer only for thread-safe implementations
150#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
151unsafe impl<T: Send> Send for SharedContainer<T> {}
152
153#[cfg(any(feature = "std-sync", feature = "tokio-sync"))]
154unsafe impl<T: Send + Sync> Sync for SharedContainer<T> {}
155
156/// A weak reference to a `SharedContainer`.
157///
158/// This struct provides an abstraction over different weak reference types:
159/// - `Weak<std::sync::RwLock<T>>` (used in standard multi-threaded environments)
160/// - `Weak<tokio::sync::RwLock<T>>` (used for async/await support)
161/// - `Weak<RefCell<T>>` (used in single-threaded environments like WebAssembly)
162///
163/// Weak references don't prevent the value from being dropped when no strong references
164/// remain. This helps break reference cycles that could cause memory leaks.
165#[derive(Debug)]
166pub struct WeakSharedContainer<T> {
167    // Standard library thread-safe implementation
168    #[cfg(all(
169        feature = "std-sync",
170        not(feature = "tokio-sync"),
171        not(feature = "wasm-sync")
172    ))]
173    std_inner: Weak<RwLock<T>>,
174
175    // Tokio async implementation
176    #[cfg(feature = "tokio-sync")]
177    tokio_inner: Weak<RwLock<T>>,
178
179    // Single-threaded implementation for WebAssembly
180    #[cfg(any(
181        feature = "wasm-sync",
182        all(
183            target_arch = "wasm32",
184            not(feature = "std-sync"),
185            not(feature = "tokio-sync")
186        )
187    ))]
188    wasm_inner: RcWeak<RefCell<T>>,
189}
190
191impl<T> Clone for WeakSharedContainer<T> {
192    fn clone(&self) -> Self {
193        // Different implementations for different platforms
194        #[cfg(all(
195            feature = "std-sync",
196            not(feature = "tokio-sync"),
197            not(feature = "wasm-sync")
198        ))]
199        {
200            WeakSharedContainer {
201                std_inner: self.std_inner.clone(),
202            }
203        }
204
205        #[cfg(feature = "tokio-sync")]
206        {
207            WeakSharedContainer {
208                tokio_inner: self.tokio_inner.clone(),
209            }
210        }
211
212        #[cfg(any(
213            feature = "wasm-sync",
214            all(
215                target_arch = "wasm32",
216                not(feature = "std-sync"),
217                not(feature = "tokio-sync")
218            )
219        ))]
220        {
221            WeakSharedContainer {
222                wasm_inner: self.wasm_inner.clone(),
223            }
224        }
225    }
226}
227
228impl<T: PartialEq> PartialEq for SharedContainer<T> {
229    fn eq(&self, other: &Self) -> bool {
230        #[cfg(feature = "tokio-sync")]
231        {
232            // For tokio-sync, we need to block on the async read
233            use std::sync::Arc;
234            let self_inner = Arc::clone(&self.tokio_inner);
235            let other_inner = Arc::clone(&other.tokio_inner);
236
237            tokio::task::block_in_place(|| {
238                // Create a new runtime for this blocking operation
239                let rt = tokio::runtime::Builder::new_current_thread()
240                    .enable_all()
241                    .build()
242                    .unwrap();
243                rt.block_on(async {
244                    let self_val = self_inner.read().await;
245                    let other_val = other_inner.read().await;
246                    *self_val == *other_val
247                })
248            })
249        }
250
251        #[cfg(not(feature = "tokio-sync"))]
252        {
253            match (self.read(), other.read()) {
254                (Some(self_val), Some(other_val)) => *self_val == *other_val,
255                _ => false,
256            }
257        }
258    }
259}
260
261impl<T> Clone for SharedContainer<T> {
262    fn clone(&self) -> Self {
263        #[cfg(all(
264            feature = "std-sync",
265            not(feature = "tokio-sync"),
266            not(feature = "wasm-sync")
267        ))]
268        {
269            SharedContainer {
270                std_inner: Arc::clone(&self.std_inner),
271            }
272        }
273
274        #[cfg(feature = "tokio-sync")]
275        {
276            SharedContainer {
277                tokio_inner: Arc::clone(&self.tokio_inner),
278            }
279        }
280
281        #[cfg(any(
282            feature = "wasm-sync",
283            all(
284                target_arch = "wasm32",
285                not(feature = "std-sync"),
286                not(feature = "tokio-sync")
287            )
288        ))]
289        {
290            SharedContainer {
291                wasm_inner: Rc::clone(&self.wasm_inner),
292            }
293        }
294    }
295}
296
297impl<T: Clone> SharedContainer<T> {
298    /// Gets a clone of the contained value.
299    ///
300    /// This method acquires a read lock, clones the value, and releases the lock.
301    ///
302    /// # Returns
303    /// * `Some(T)`: A clone of the contained value
304    /// * `None`: If the lock couldn't be acquired
305    ///
306    /// # Note
307    /// When using the `tokio-sync` feature, this method will try to acquire the lock
308    /// in a blocking manner, which may not be ideal for async code. Consider using
309    /// `get_cloned_async()` instead.
310    #[cfg_attr(
311        feature = "tokio-sync",
312        doc = "WARNING: This method uses blocking operations when using tokio-sync feature, which is not ideal for async code. Consider using get_cloned_async() instead."
313    )]
314    pub fn get_cloned(&self) -> Option<T> {
315        #[cfg(feature = "tokio-sync")]
316        {
317            tokio_sync_warning!(
318                "This method uses blocking operations when using tokio-sync feature, which is not ideal for async code. Consider using get_cloned_async() instead."
319            );
320            // For tokio-sync, we need to block on the async read
321            // This is not ideal for async code, but it allows the method to work
322            // in both sync and async contexts
323            use std::sync::Arc;
324            let inner = Arc::clone(&self.tokio_inner);
325            let value = tokio::task::block_in_place(|| {
326                // Create a new runtime for this blocking operation
327                let rt = tokio::runtime::Builder::new_current_thread()
328                    .enable_all()
329                    .build()
330                    .unwrap();
331                rt.block_on(async {
332                    let guard = inner.read().await;
333                    (*guard).clone()
334                })
335            });
336            Some(value)
337        }
338
339        #[cfg(not(feature = "tokio-sync"))]
340        {
341            let guard = self.read()?;
342            Some((*guard).clone())
343        }
344    }
345
346    /// Gets a clone of the contained value asynchronously.
347    ///
348    /// This method is only available when the `tokio-sync` feature is enabled.
349    ///
350    /// # Returns
351    /// A clone of the contained value
352    ///
353    /// # Examples
354    ///
355    /// ```
356    /// # #[cfg(feature = "tokio-sync")]
357    /// # async fn example() {
358    /// # use shared_container::SharedContainer;
359    /// let container = SharedContainer::new(42);
360    /// let value = container.get_cloned_async().await;
361    /// assert_eq!(value, 42);
362    /// # }
363    /// ```
364    #[cfg(feature = "tokio-sync")]
365    #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
366    pub async fn get_cloned_async(&self) -> T
367    where
368        T: Clone,
369    {
370        let guard = self.tokio_inner.read().await;
371        (*guard).clone()
372    }
373}
374
375impl<T> SharedContainer<T> {
376    /// Creates a new `SharedContainer` containing the given value.
377    ///
378    /// # Parameters
379    /// * `value`: The value to store in the container
380    ///
381    /// # Returns
382    /// A new `SharedContainer` instance containing the value
383    pub fn new(value: T) -> Self {
384        #[cfg(all(
385            feature = "std-sync",
386            not(feature = "tokio-sync"),
387            not(feature = "wasm-sync")
388        ))]
389        {
390            SharedContainer {
391                std_inner: Arc::new(RwLock::new(value)),
392            }
393        }
394
395        #[cfg(feature = "tokio-sync")]
396        {
397            SharedContainer {
398                tokio_inner: Arc::new(RwLock::new(value)),
399            }
400        }
401
402        #[cfg(any(
403            feature = "wasm-sync",
404            all(
405                target_arch = "wasm32",
406                not(feature = "std-sync"),
407                not(feature = "tokio-sync")
408            )
409        ))]
410        {
411            SharedContainer {
412                wasm_inner: Rc::new(RefCell::new(value)),
413            }
414        }
415    }
416
417    /// Gets a read-only access guard to the contained value.
418    ///
419    /// # Returns
420    /// * `Some(SharedReadGuard<T>)`: A guard allowing read-only access to the value
421    /// * `None`: If the lock couldn't be acquired
422    ///
423    /// # Note
424    /// When using the `tokio-sync` feature, this method will always return `None`.
425    /// Use `read_async()` instead for async access.
426    #[cfg_attr(
427        feature = "tokio-sync",
428        doc = "WARNING: This method always returns None when using tokio-sync feature. Use read_async() instead."
429    )]
430    pub fn read(&self) -> Option<SharedReadGuard<T>> {
431        #[cfg(feature = "tokio-sync")]
432        {
433            tokio_sync_warning!(
434                "This method always returns None when using tokio-sync feature. Use read_async() instead."
435            );
436            // Tokio's RwLock doesn't have a non-async read method, so we can't use it here
437            // Users should use read_async instead
438            return None;
439        }
440
441        #[cfg(all(
442            feature = "std-sync",
443            not(feature = "tokio-sync"),
444            not(feature = "wasm-sync")
445        ))]
446        {
447            match self.std_inner.read() {
448                Ok(guard) => Some(SharedReadGuard::StdSync(guard)),
449                Err(_) => None,
450            }
451        }
452
453        #[cfg(any(
454            feature = "wasm-sync",
455            all(
456                target_arch = "wasm32",
457                not(feature = "std-sync"),
458                not(feature = "tokio-sync")
459            )
460        ))]
461        {
462            match self.wasm_inner.try_borrow() {
463                Ok(borrow) => Some(SharedReadGuard::Single(borrow)),
464                Err(_) => None,
465            }
466        }
467    }
468
469    /// Gets a writable access guard to the contained value.
470    ///
471    /// # Returns
472    /// * `Some(SharedWriteGuard<T>)`: A guard allowing read-write access to the value
473    /// * `None`: If the lock couldn't be acquired
474    ///
475    /// # Note
476    /// When using the `tokio-sync` feature, this method will always return `None`.
477    /// Use `write_async()` instead for async access.
478    #[cfg_attr(
479        feature = "tokio-sync",
480        doc = "WARNING: This method always returns None when using tokio-sync feature. Use write_async() instead."
481    )]
482    pub fn write(&self) -> Option<SharedWriteGuard<T>> {
483        #[cfg(feature = "tokio-sync")]
484        {
485            tokio_sync_warning!(
486                "This method always returns None when using tokio-sync feature. Use write_async() instead."
487            );
488            // Tokio's RwLock doesn't have a non-async write method, so we can't use it here
489            // Users should use write_async instead
490            return None;
491        }
492
493        #[cfg(all(
494            feature = "std-sync",
495            not(feature = "tokio-sync"),
496            not(feature = "wasm-sync")
497        ))]
498        {
499            match self.std_inner.write() {
500                Ok(guard) => Some(SharedWriteGuard::StdSync(guard)),
501                Err(_) => None,
502            }
503        }
504
505        #[cfg(any(
506            feature = "wasm-sync",
507            all(
508                target_arch = "wasm32",
509                not(feature = "std-sync"),
510                not(feature = "tokio-sync")
511            )
512        ))]
513        {
514            match self.wasm_inner.try_borrow_mut() {
515                Ok(borrow) => Some(SharedWriteGuard::Single(borrow)),
516                Err(_) => None,
517            }
518        }
519    }
520
521    /// Creates a weak reference to this container.
522    ///
523    /// A weak reference doesn't prevent the value from being dropped when no strong
524    /// references remain, which helps break reference cycles that could cause memory leaks.
525    ///
526    /// # Returns
527    /// A `WeakSharedContainer` that points to the same data
528    pub fn downgrade(&self) -> WeakSharedContainer<T> {
529        #[cfg(all(
530            feature = "std-sync",
531            not(feature = "tokio-sync"),
532            not(feature = "wasm-sync")
533        ))]
534        {
535            WeakSharedContainer {
536                std_inner: Arc::downgrade(&self.std_inner),
537            }
538        }
539
540        #[cfg(feature = "tokio-sync")]
541        {
542            WeakSharedContainer {
543                tokio_inner: Arc::downgrade(&self.tokio_inner),
544            }
545        }
546
547        #[cfg(any(
548            feature = "wasm-sync",
549            all(
550                target_arch = "wasm32",
551                not(feature = "std-sync"),
552                not(feature = "tokio-sync")
553            )
554        ))]
555        {
556            WeakSharedContainer {
557                wasm_inner: Rc::downgrade(&self.wasm_inner),
558            }
559        }
560    }
561
562    /// Asynchronously gets a read-only access guard to the contained value.
563    ///
564    /// This method is only available when the `tokio-sync` feature is enabled.
565    ///
566    /// # Returns
567    /// A guard allowing read-only access to the value
568    ///
569    /// # Examples
570    ///
571    /// ```
572    /// # #[cfg(feature = "tokio-sync")]
573    /// # async fn example() {
574    /// # use shared_container::SharedContainer;
575    /// let container = SharedContainer::new(42);
576    /// let guard = container.read_async().await;
577    /// assert_eq!(*guard, 42);
578    /// # }
579    /// ```
580    #[cfg(feature = "tokio-sync")]
581    #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
582    pub async fn read_async(&self) -> SharedReadGuard<'_, T> {
583        let guard = self.tokio_inner.read().await;
584        SharedReadGuard::TokioSync(guard)
585    }
586
587    /// Asynchronously gets a writable access guard to the contained value.
588    ///
589    /// This method is only available when the `tokio-sync` feature is enabled.
590    ///
591    /// # Returns
592    /// A guard allowing read-write access to the value
593    ///
594    /// # Examples
595    ///
596    /// ```
597    /// # #[cfg(feature = "tokio-sync")]
598    /// # async fn example() {
599    /// # use shared_container::SharedContainer;
600    /// let container = SharedContainer::new(42);
601    /// let mut guard = container.write_async().await;
602    /// *guard = 100;
603    /// # }
604    /// ```
605    #[cfg(feature = "tokio-sync")]
606    #[cfg_attr(docsrs, doc(cfg(feature = "tokio-sync")))]
607    pub async fn write_async(&self) -> SharedWriteGuard<'_, T> {
608        let guard = self.tokio_inner.write().await;
609        SharedWriteGuard::TokioSync(guard)
610    }
611}
612
613impl<T> WeakSharedContainer<T> {
614    /// Attempts to create a strong `SharedContainer` from this weak reference.
615    ///
616    /// This will succeed if the value has not yet been dropped, i.e., if there are
617    /// still other strong references to it.
618    ///
619    /// # Returns
620    /// * `Some(SharedContainer<T>)`: If the value still exists
621    /// * `None`: If the value has been dropped
622    pub fn upgrade(&self) -> Option<SharedContainer<T>> {
623        // Different implementations for different platforms
624        #[cfg(all(
625            feature = "std-sync",
626            not(feature = "tokio-sync"),
627            not(feature = "wasm-sync")
628        ))]
629        {
630            self.std_inner
631                .upgrade()
632                .map(|inner| SharedContainer { std_inner: inner })
633        }
634
635        #[cfg(feature = "tokio-sync")]
636        {
637            self.tokio_inner
638                .upgrade()
639                .map(|inner| SharedContainer { tokio_inner: inner })
640        }
641
642        #[cfg(any(
643            feature = "wasm-sync",
644            all(
645                target_arch = "wasm32",
646                not(feature = "std-sync"),
647                not(feature = "tokio-sync")
648            )
649        ))]
650        {
651            self.wasm_inner
652                .upgrade()
653                .map(|inner| SharedContainer { wasm_inner: inner })
654        }
655    }
656}
657/// A read-only guard for accessing data in a `SharedContainer`.
658///
659/// This type abstracts over the differences between different read guards:
660/// - `std::sync::RwLockReadGuard` (used in standard multi-threaded environments)
661/// - `tokio::sync::RwLockReadGuard` (used for async/await support)
662/// - `std::cell::Ref` (used in single-threaded environments like WebAssembly)
663///
664/// It implements `Deref` to allow transparent access to the underlying data.
665pub enum SharedReadGuard<'a, T> {
666    #[cfg(all(
667        feature = "std-sync",
668        not(feature = "tokio-sync"),
669        not(feature = "wasm-sync")
670    ))]
671    StdSync(std::sync::RwLockReadGuard<'a, T>),
672
673    #[cfg(feature = "tokio-sync")]
674    TokioSync(tokio::sync::RwLockReadGuard<'a, T>),
675
676    #[cfg(any(
677        feature = "wasm-sync",
678        all(
679            target_arch = "wasm32",
680            not(feature = "std-sync"),
681            not(feature = "tokio-sync")
682        )
683    ))]
684    Single(Ref<'a, T>),
685}
686
687impl<'a, T> Deref for SharedReadGuard<'a, T> {
688    type Target = T;
689
690    fn deref(&self) -> &Self::Target {
691        #[cfg(all(
692            feature = "std-sync",
693            not(feature = "tokio-sync"),
694            not(feature = "wasm-sync")
695        ))]
696        {
697            match self {
698                SharedReadGuard::StdSync(guard) => guard.deref(),
699                #[allow(unreachable_patterns)]
700                _ => unreachable!(),
701            }
702        }
703
704        #[cfg(feature = "tokio-sync")]
705        {
706            match self {
707                SharedReadGuard::TokioSync(guard) => guard.deref(),
708                #[allow(unreachable_patterns)]
709                _ => unreachable!(),
710            }
711        }
712
713        #[cfg(any(
714            feature = "wasm-sync",
715            all(
716                target_arch = "wasm32",
717                not(feature = "std-sync"),
718                not(feature = "tokio-sync")
719            )
720        ))]
721        {
722            match self {
723                SharedReadGuard::Single(borrow) => borrow.deref(),
724                #[allow(unreachable_patterns)]
725                _ => unreachable!(),
726            }
727        }
728    }
729}
730
731/// A writable guard for accessing and modifying data in a `SharedContainer`.
732///
733/// This type abstracts over the differences between different write guards:
734/// - `std::sync::RwLockWriteGuard` (used in standard multi-threaded environments)
735/// - `tokio::sync::RwLockWriteGuard` (used for async/await support)
736/// - `std::cell::RefMut` (used in single-threaded environments like WebAssembly)
737///
738/// It implements both `Deref` and `DerefMut` to allow transparent access to the underlying data.
739pub enum SharedWriteGuard<'a, T> {
740    #[cfg(all(
741        feature = "std-sync",
742        not(feature = "tokio-sync"),
743        not(feature = "wasm-sync")
744    ))]
745    StdSync(std::sync::RwLockWriteGuard<'a, T>),
746
747    #[cfg(feature = "tokio-sync")]
748    TokioSync(tokio::sync::RwLockWriteGuard<'a, T>),
749
750    #[cfg(any(
751        feature = "wasm-sync",
752        all(
753            target_arch = "wasm32",
754            not(feature = "std-sync"),
755            not(feature = "tokio-sync")
756        )
757    ))]
758    Single(RefMut<'a, T>),
759}
760
761impl<'a, T> Deref for SharedWriteGuard<'a, T> {
762    type Target = T;
763
764    fn deref(&self) -> &Self::Target {
765        #[cfg(all(
766            feature = "std-sync",
767            not(feature = "tokio-sync"),
768            not(feature = "wasm-sync")
769        ))]
770        {
771            match self {
772                SharedWriteGuard::StdSync(guard) => guard.deref(),
773                #[allow(unreachable_patterns)]
774                _ => unreachable!(),
775            }
776        }
777
778        #[cfg(feature = "tokio-sync")]
779        {
780            match self {
781                SharedWriteGuard::TokioSync(guard) => guard.deref(),
782                #[allow(unreachable_patterns)]
783                _ => unreachable!(),
784            }
785        }
786
787        #[cfg(any(
788            feature = "wasm-sync",
789            all(
790                target_arch = "wasm32",
791                not(feature = "std-sync"),
792                not(feature = "tokio-sync")
793            )
794        ))]
795        {
796            match self {
797                SharedWriteGuard::Single(borrow) => borrow.deref(),
798                #[allow(unreachable_patterns)]
799                _ => unreachable!(),
800            }
801        }
802    }
803}
804
805impl<'a, T> DerefMut for SharedWriteGuard<'a, T> {
806    fn deref_mut(&mut self) -> &mut Self::Target {
807        #[cfg(all(
808            feature = "std-sync",
809            not(feature = "tokio-sync"),
810            not(feature = "wasm-sync")
811        ))]
812        {
813            match self {
814                SharedWriteGuard::StdSync(guard) => guard.deref_mut(),
815                #[allow(unreachable_patterns)]
816                _ => unreachable!(),
817            }
818        }
819
820        #[cfg(feature = "tokio-sync")]
821        {
822            match self {
823                SharedWriteGuard::TokioSync(guard) => guard.deref_mut(),
824                #[allow(unreachable_patterns)]
825                _ => unreachable!(),
826            }
827        }
828
829        #[cfg(any(
830            feature = "wasm-sync",
831            all(
832                target_arch = "wasm32",
833                not(feature = "std-sync"),
834                not(feature = "tokio-sync")
835            )
836        ))]
837        {
838            match self {
839                SharedWriteGuard::Single(borrow) => borrow.deref_mut(),
840                #[allow(unreachable_patterns)]
841                _ => unreachable!(),
842            }
843        }
844    }
845}
846
847#[cfg(test)]
848mod tests {
849
850    #[derive(Debug, Clone, PartialEq)]
851    #[allow(dead_code)]
852    struct TestStruct {
853        value: i32,
854    }
855
856    // Skip synchronous tests when tokio-sync is enabled
857    #[cfg(not(feature = "tokio-sync"))]
858    mod sync_tests {
859        use super::*;
860        use crate::SharedContainer;
861
862        #[test]
863        fn test_read_access() {
864            let container = SharedContainer::new(TestStruct { value: 42 });
865
866            // Read access
867            let guard = container.read().unwrap();
868            assert_eq!(guard.value, 42);
869        }
870
871        #[test]
872        fn test_write_access() {
873            let container = SharedContainer::new(TestStruct { value: 42 });
874
875            // Write access
876            {
877                let mut guard = container.write().unwrap();
878                guard.value = 100;
879            }
880
881            // Verify change
882            let guard = container.read().unwrap();
883            assert_eq!(guard.value, 100);
884        }
885
886        #[test]
887        fn test_clone_container() {
888            let container1 = SharedContainer::new(TestStruct { value: 42 });
889            let container2 = container1.clone();
890
891            // Modify through container2
892            {
893                let mut guard = container2.write().unwrap();
894                guard.value = 100;
895            }
896
897            // Verify change visible through container1
898            let guard = container1.read().unwrap();
899            assert_eq!(guard.value, 100);
900        }
901
902        #[test]
903        fn test_get_cloned() {
904            let container = SharedContainer::new(TestStruct { value: 42 });
905            let cloned = container.get_cloned().unwrap();
906            assert_eq!(cloned, TestStruct { value: 42 });
907
908            // Modify original
909            {
910                let mut guard = container.write().unwrap();
911                guard.value = 100;
912            }
913
914            // Cloned value should not change
915            assert_eq!(cloned, TestStruct { value: 42 });
916
917            // And we can get a new clone with updated value
918            let new_clone = container.get_cloned().unwrap();
919            assert_eq!(new_clone, TestStruct { value: 100 });
920        }
921
922        #[test]
923        fn test_weak_ref() {
924            let container = SharedContainer::new(TestStruct { value: 42 });
925
926            // Create a weak reference
927            let weak = container.downgrade();
928
929            // Weak reference can be upgraded to a strong reference
930            let container2 = weak.upgrade().unwrap();
931
932            // Both containers point to the same data
933            {
934                let mut guard = container2.write().unwrap();
935                guard.value = 100;
936            }
937
938            // Change visible through first container
939            {
940                let guard = container.read().unwrap();
941                assert_eq!(guard.value, 100);
942            }
943            // Drop all strong references
944            drop(container);
945            drop(container2);
946
947            // Weak reference can no longer be upgraded
948            assert!(weak.upgrade().is_none());
949        }
950
951        #[test]
952        fn test_weak_clone() {
953            let container = SharedContainer::new(TestStruct { value: 42 });
954
955            // Create a weak reference and clone it
956            let weak1 = container.downgrade();
957            let weak2 = weak1.clone();
958
959            // Both weak references can be upgraded
960            let container1 = weak1.upgrade().unwrap();
961            let container2 = weak2.upgrade().unwrap();
962
963            // Modify through container2
964            {
965                let mut guard = container2.write().unwrap();
966                guard.value = 100;
967            }
968
969            // Change visible through container1
970            {
971                let guard = container1.read().unwrap();
972                assert_eq!(guard.value, 100);
973            }
974
975            // Drop all strong references
976            drop(container);
977            drop(container1);
978            drop(container2);
979
980            // Neither weak reference can be upgraded
981            assert!(weak1.upgrade().is_none());
982            assert!(weak2.upgrade().is_none());
983        }
984    }
985}
986
987// Tests specifically for the tokio async implementation
988#[cfg(test)]
989#[cfg(feature = "tokio-sync")]
990mod tokio_tests {
991    use super::*;
992    use tokio::runtime::Runtime;
993
994    #[derive(Debug, Clone, PartialEq)]
995    struct TestStruct {
996        value: i32,
997    }
998
999    #[test]
1000    fn test_tokio_read_access() {
1001        let rt = Runtime::new().unwrap();
1002        rt.block_on(async {
1003            let container = SharedContainer::new(TestStruct { value: 42 });
1004
1005            // Synchronous read should return None with tokio-sync
1006            assert!(container.read().is_none());
1007
1008            // Async read access
1009            let guard = container.read_async().await;
1010            assert_eq!(guard.value, 42);
1011        });
1012    }
1013
1014    #[test]
1015    fn test_tokio_write_access() {
1016        let rt = Runtime::new().unwrap();
1017        rt.block_on(async {
1018            let container = SharedContainer::new(TestStruct { value: 42 });
1019
1020            // Synchronous write should return None with tokio-sync
1021            assert!(container.write().is_none());
1022
1023            // Async write access
1024            {
1025                let mut guard = container.write_async().await;
1026                guard.value = 100;
1027            }
1028
1029            // Verify change
1030            let guard = container.read_async().await;
1031            assert_eq!(guard.value, 100);
1032        });
1033    }
1034
1035    #[test]
1036    fn test_tokio_clone_container() {
1037        let rt = Runtime::new().unwrap();
1038        rt.block_on(async {
1039            let container1 = SharedContainer::new(TestStruct { value: 42 });
1040            let container2 = container1.clone();
1041
1042            // Modify through container2
1043            {
1044                let mut guard = container2.write_async().await;
1045                guard.value = 100;
1046            }
1047
1048            // Verify change visible through container1
1049            let guard = container1.read_async().await;
1050            assert_eq!(guard.value, 100);
1051        });
1052    }
1053
1054    #[test]
1055    fn test_tokio_weak_ref() {
1056        let rt = Runtime::new().unwrap();
1057        rt.block_on(async {
1058            let container = SharedContainer::new(TestStruct { value: 42 });
1059
1060            // Create a weak reference
1061            let weak = container.downgrade();
1062
1063            // Weak reference can be upgraded to a strong reference
1064            let container2 = weak.upgrade().unwrap();
1065
1066            // Both containers point to the same data
1067            {
1068                let mut guard = container2.write_async().await;
1069                guard.value = 100;
1070            }
1071
1072            // Change visible through first container
1073            {
1074                let guard = container.read_async().await;
1075                assert_eq!(guard.value, 100);
1076            }
1077
1078            // Drop all strong references
1079            drop(container);
1080            drop(container2);
1081
1082            // Weak reference can no longer be upgraded
1083            assert!(weak.upgrade().is_none());
1084        });
1085    }
1086}
1087
1088// Tests specifically for the WebAssembly implementation
1089// These tests can be run on any platform by enabling the force-wasm-impl feature
1090#[cfg(test)]
1091#[cfg(any(target_arch = "wasm32", feature = "force-wasm-impl"))]
1092mod wasm_tests {
1093    use super::*;
1094
1095    #[derive(Debug, Clone, PartialEq)]
1096    struct TestStruct {
1097        value: i32,
1098    }
1099
1100    #[test]
1101    fn test_wasm_read_access() {
1102        let container = SharedContainer::new(TestStruct { value: 42 });
1103
1104        // Read access
1105        let guard = container.read().unwrap();
1106        assert_eq!(guard.value, 42);
1107    }
1108
1109    #[test]
1110    fn test_wasm_write_access() {
1111        let container = SharedContainer::new(TestStruct { value: 42 });
1112
1113        // Write access
1114        {
1115            let mut guard = container.write().unwrap();
1116            guard.value = 100;
1117        }
1118
1119        // Verify change
1120        let guard = container.read().unwrap();
1121        assert_eq!(guard.value, 100);
1122    }
1123
1124    #[test]
1125    fn test_wasm_borrow_conflict() {
1126        let container = SharedContainer::new(TestStruct { value: 42 });
1127
1128        // Get a read borrow
1129        let _guard = container.read().unwrap();
1130
1131        // Trying to get a write borrow while a read borrow exists should fail
1132        assert!(container.write().is_none());
1133    }
1134
1135    #[test]
1136    fn test_wasm_multiple_reads() {
1137        let container = SharedContainer::new(TestStruct { value: 42 });
1138
1139        // Multiple read borrows should work
1140        let _guard1 = container.read().unwrap();
1141        let guard2 = container.read().unwrap();
1142
1143        assert_eq!(guard2.value, 42);
1144    }
1145
1146    #[test]
1147    fn test_wasm_weak_ref() {
1148        let container = SharedContainer::new(TestStruct { value: 42 });
1149        let weak = container.downgrade();
1150
1151        // Upgrade should work
1152        let container2 = weak.upgrade().unwrap();
1153        assert_eq!(container2.read().unwrap().value, 42);
1154
1155        // After dropping all strong references, upgrade should fail
1156        drop(container);
1157        drop(container2);
1158        assert!(weak.upgrade().is_none());
1159    }
1160}