reactive_graph/owner/
arena_item.rs

1use super::{
2    arena::{Arena, NodeId},
3    LocalStorage, Storage, SyncStorage, OWNER,
4};
5use crate::traits::{Dispose, IntoInner, IsDisposed};
6use send_wrapper::SendWrapper;
7use std::{any::Any, hash::Hash, marker::PhantomData};
8
9/// A copyable, stable reference for any value, stored on the arena whose ownership is managed by the
10/// reactive ownership tree.
11#[derive(Debug)]
12pub struct ArenaItem<T, S = SyncStorage> {
13    node: NodeId,
14    #[allow(clippy::type_complexity)]
15    ty: PhantomData<fn() -> (SendWrapper<T>, S)>,
16}
17
18impl<T, S> Copy for ArenaItem<T, S> {}
19
20impl<T, S> Clone for ArenaItem<T, S> {
21    fn clone(&self) -> Self {
22        *self
23    }
24}
25
26impl<T, S> PartialEq for ArenaItem<T, S> {
27    fn eq(&self, other: &Self) -> bool {
28        self.node == other.node
29    }
30}
31
32impl<T, S> Eq for ArenaItem<T, S> {}
33
34impl<T, S> Hash for ArenaItem<T, S> {
35    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
36        self.node.hash(state);
37    }
38}
39
40impl<T, S> ArenaItem<T, S>
41where
42    T: 'static,
43    S: Storage<T>,
44{
45    /// Stores the given value in the arena allocator.
46    #[track_caller]
47    pub fn new_with_storage(value: T) -> Self {
48        let node = {
49            Arena::with_mut(|arena| {
50                arena.insert(
51                    Box::new(S::wrap(value)) as Box<dyn Any + Send + Sync>
52                )
53            })
54        };
55        OWNER.with(|o| {
56            if let Some(owner) = o.borrow().as_ref().and_then(|o| o.upgrade()) {
57                owner.register(node);
58            }
59        });
60
61        Self {
62            node,
63            ty: PhantomData,
64        }
65    }
66}
67
68impl<T, S> Default for ArenaItem<T, S>
69where
70    T: Default + 'static,
71    S: Storage<T>,
72{
73    #[track_caller] // Default trait is not annotated with #[track_caller]
74    fn default() -> Self {
75        Self::new_with_storage(Default::default())
76    }
77}
78
79impl<T> ArenaItem<T>
80where
81    T: Send + Sync + 'static,
82{
83    /// Stores the given value in the arena allocator.
84    #[track_caller]
85    pub fn new(value: T) -> Self {
86        ArenaItem::new_with_storage(value)
87    }
88}
89
90impl<T> ArenaItem<T, LocalStorage>
91where
92    T: 'static,
93{
94    /// Stores the given value in the arena allocator.
95    #[track_caller]
96    pub fn new_local(value: T) -> Self {
97        ArenaItem::new_with_storage(value)
98    }
99}
100
101impl<T, S: Storage<T>> ArenaItem<T, S> {
102    /// Applies a function to a reference to the stored value and returns the result, or `None` if it has already been disposed.
103    #[track_caller]
104    pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> {
105        S::try_with(self.node, fun)
106    }
107
108    /// Applies a function to a mutable reference to the stored value and returns the result, or `None` if it has already been disposed.
109    #[track_caller]
110    pub fn try_update_value<U>(
111        &self,
112        fun: impl FnOnce(&mut T) -> U,
113    ) -> Option<U> {
114        S::try_with_mut(self.node, fun)
115    }
116}
117
118impl<T: Clone, S: Storage<T>> ArenaItem<T, S> {
119    /// Returns a clone of the stored value, or `None` if it has already been disposed.
120    #[track_caller]
121    pub fn try_get_value(&self) -> Option<T> {
122        S::try_with(self.node, Clone::clone)
123    }
124}
125
126impl<T, S> IsDisposed for ArenaItem<T, S> {
127    fn is_disposed(&self) -> bool {
128        Arena::with(|arena| !arena.contains_key(self.node))
129    }
130}
131
132impl<T, S> Dispose for ArenaItem<T, S> {
133    fn dispose(self) {
134        Arena::with_mut(|arena| arena.remove(self.node));
135    }
136}
137
138impl<T, S: Storage<T>> IntoInner for ArenaItem<T, S> {
139    type Value = T;
140
141    #[inline(always)]
142    fn into_inner(self) -> Option<Self::Value> {
143        S::take(self.node)
144    }
145}