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