type_set/
entry.rs

1use crate::{unwrap, Key, Value};
2use std::{
3    any::{type_name, Any, TypeId},
4    collections::btree_map,
5    fmt::{self, Debug, Formatter},
6    marker::PhantomData,
7    ops::{Deref, DerefMut},
8};
9
10/// A view into a single type in the `TypeSet`, which may be either vacant or occupied.
11///
12/// This type is constructed by [`TypeSet::entry`][crate::TypeSet::entry]
13///
14/// ## Examples
15///
16/// This is a somewhat contrived example that demonstrates matching on the [`Entry`]. Often,
17/// [`Entry::or_insert`], [`Entry::or_insert_with`], and [`Entry::and_modify`] can achieve
18/// comparable results. See those functions for further usage examples.
19///
20/// ```rust
21/// use type_set::{TypeSet, entry::Entry};
22/// let mut set = TypeSet::new().with("hello");
23/// let (previous, current) = match set.entry::<&'static str>() {
24///     Entry::Vacant(vacant_entry) => {
25///         let current = vacant_entry.insert("entry was vacant");
26///         (None, current)
27///     }
28///
29///     Entry::Occupied(mut occupied_entry) => {
30///         let previous = occupied_entry.insert("entry was occupied");
31///         (Some(previous), occupied_entry.into_mut())
32///     }
33/// };
34/// assert_eq!(previous, Some("hello"));
35/// assert_eq!(*current, "entry was occupied");
36/// ```
37pub enum Entry<'a, T> {
38    /// A view into the location a T would be stored in the `TypeSet`. See [`VacantEntry`]
39    Vacant(VacantEntry<'a, T>),
40
41    /// A view into the location a T is currently stored in the `TypeSet`. See [`OccupiedEntry`]
42    Occupied(OccupiedEntry<'a, T>),
43}
44
45impl<'a, T: Debug + Any + Send + Sync + 'static> Debug for Entry<'a, T> {
46    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
47        match self {
48            Self::Vacant(vacant_entry) => f.debug_tuple("Vacant").field(vacant_entry).finish(),
49            Self::Occupied(occupied_entry) => {
50                f.debug_tuple("Occupied").field(occupied_entry).finish()
51            }
52        }
53    }
54}
55
56/// A view into a vacant entry in a `TypeSet`.
57///
58/// It is part of the [`Entry`] enum.
59pub struct VacantEntry<'a, T>(
60    pub(super) btree_map::VacantEntry<'a, Key, Value>,
61    PhantomData<T>,
62);
63
64impl<'a, T: Debug + Any + Send + Sync + 'static> Debug for VacantEntry<'a, T> {
65    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
66        write!(f, "VacantEntry<{}>", type_name::<T>())
67    }
68}
69/// A view into the location a T is stored
70pub struct OccupiedEntry<'a, T>(
71    pub(super) btree_map::OccupiedEntry<'a, Key, Value>,
72    PhantomData<T>,
73);
74
75impl<'a, T: Debug + Any + Send + Sync + 'static> Debug for OccupiedEntry<'a, T> {
76    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
77        f.debug_tuple(&format!("OccupiedEntry<{}>", type_name::<T>()))
78            .field(unwrap!(self.0.get().downcast_ref::<T>()))
79            .finish()
80    }
81}
82
83impl<'a, T: Send + Sync + 'static> Entry<'a, T> {
84    /// Ensures a value is in the `Entry` by inserting the provided `default` value if the Entry was
85    /// previously vacant. Returns a mutable reference to the value.
86    ///
87    /// Prefer [`Entry::or_insert_with`] if constructing a T is expensive.
88    ///
89    /// ## Example
90    ///
91    /// ```rust
92    /// let mut set = type_set::TypeSet::new();
93    /// assert_eq!(*set.entry().or_insert("hello"), "hello");
94    /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
95    /// assert_eq!(*set.entry().or_insert("world"), "hello");
96    /// assert_eq!(set.get::<&'static str>(), Some(&"hello"));
97    /// ```
98    pub fn or_insert(self, default: T) -> &'a mut T {
99        match self {
100            Entry::Vacant(vacant) => vacant.insert(default),
101            Entry::Occupied(occupied) => occupied.into_mut(),
102        }
103    }
104
105    /// Ensures a value is in the `Entry` by inserting the provided value returned by the `default`
106    /// function if the `Entry` was previously vacant. Returns a mutable reference to the value.
107    ///
108    /// Prefer this to [`Entry::or_insert`] if constructing a T is expensive.
109    ///
110    /// ## Example
111    ///
112    /// ```rust
113    /// let mut set = type_set::TypeSet::new();
114    /// assert_eq!(*set.entry().or_insert_with(|| String::from("hello")), "hello");
115    /// assert_eq!(set.get::<String>(), Some(&String::from("hello")));
116    /// assert_eq!(*set.entry::<String>().or_insert_with(|| panic!("never called")), "hello");
117    /// assert_eq!(set.get::<String>(), Some(&String::from("hello")));
118    /// ```
119    pub fn or_insert_with(self, default: impl FnOnce() -> T) -> &'a mut T {
120        match self {
121            Entry::Vacant(vacant) => vacant.insert(default()),
122            Entry::Occupied(occupied) => occupied.into_mut(),
123        }
124    }
125
126    /// Provides in-place mutable access to an occupied entry before any potential inserts into the
127    /// set using [`Entry::or_insert`] or [`Entry::or_insert_with`].
128    ///
129    /// ## Example
130    ///
131    /// ```rust
132    /// let mut set = type_set::TypeSet::new().with(String::from("hello"));
133    /// let value = set.entry::<String>()
134    ///     .and_modify(|s| s.push_str(" world"))
135    ///     .or_insert_with(|| String::from("greetings"));
136    /// assert_eq!(value, "hello world");
137    ///
138    /// set.take::<String>();
139    /// let value = set.entry::<String>()
140    ///     .and_modify(|s| s.push_str(" world"))
141    ///     .or_insert_with(|| String::from("greetings"));
142    /// assert_eq!(value, "greetings");
143    /// ```
144    #[must_use]
145    pub fn and_modify(self, f: impl FnOnce(&mut T)) -> Self {
146        match self {
147            Entry::Vacant(vacant) => Entry::Vacant(vacant),
148            Entry::Occupied(mut occupied) => {
149                f(occupied.get_mut());
150                Entry::Occupied(occupied)
151            }
152        }
153    }
154
155    /// Remove and return a value from this entry, if occupied.
156    #[must_use]
157    pub fn take(self) -> Option<T> {
158        self.into_occupied().map(OccupiedEntry::remove)
159    }
160
161    /// Returns an `OccupiedEntry` or panic
162    ///
163    /// # Panics
164    ///
165    /// This function will panic if the entry is vacant
166    #[must_use]
167    pub fn unwrap_occupied(self) -> OccupiedEntry<'a, T> {
168        self.into_occupied().unwrap_or_else(|| {
169            panic!(
170                "expected an occupied type-set entry for {}, but was vacant",
171                type_name::<T>()
172            )
173        })
174    }
175
176    /// Returns a `VacantEntry` or panic
177    ///
178    /// # Panics
179    ///
180    /// This function will panic if the entry is occupied
181    #[must_use]
182    pub fn unwrap_vacant(self) -> VacantEntry<'a, T> {
183        self.into_vacant().unwrap_or_else(|| {
184            panic!(
185                "expected a vacant type-set entry for {}, but was occupied",
186                type_name::<T>()
187            )
188        })
189    }
190
191    /// Returns a mutable reference to the contained type, if this entry is occupied
192    #[must_use]
193    pub fn into_mut(self) -> Option<&'a mut T> {
194        self.into_occupied().map(OccupiedEntry::into_mut)
195    }
196
197    /// Returns an [`OccupiedEntry`] or `None` if this entry is vacant.
198    #[must_use]
199    pub fn into_occupied(self) -> Option<OccupiedEntry<'a, T>> {
200        match self {
201            Entry::Occupied(occupied_entry) => Some(occupied_entry),
202            Entry::Vacant(_) => None,
203        }
204    }
205
206    /// Returns a [`VacantEntry`] or `None` if this entry is occupied.
207    #[must_use]
208    pub fn into_vacant(self) -> Option<VacantEntry<'a, T>> {
209        match self {
210            Entry::Occupied(_) => None,
211            Entry::Vacant(vacant_entry) => Some(vacant_entry),
212        }
213    }
214
215    /// Returns whether this `Entry` is a [`VacantEntry`]
216    #[must_use]
217    pub fn is_empty(&self) -> bool {
218        matches!(self, Entry::Vacant(_))
219    }
220
221    /// Insert a value into this [`Entry`].
222    ///
223    /// If the Entry is already an [`OccupiedEntry`], the previously contained value will be
224    /// returned
225    pub fn insert(self, value: T) -> Option<T> {
226        match self {
227            Entry::Vacant(v) => {
228                #[cfg(feature = "log")]
229                log::trace!("inserting {}", type_name::<T>());
230                v.insert(value);
231                None
232            }
233
234            Entry::Occupied(mut o) => {
235                #[cfg(feature = "log")]
236                log::trace!("replacing {}", type_name::<T>());
237                Some(o.insert(value))
238            }
239        }
240    }
241
242    pub(super) fn new(entry: btree_map::Entry<'a, TypeId, Value>) -> Self {
243        match entry {
244            btree_map::Entry::Vacant(vacant) => Self::Vacant(VacantEntry(vacant, PhantomData)),
245            btree_map::Entry::Occupied(occupied) => {
246                Self::Occupied(OccupiedEntry(occupied, PhantomData))
247            }
248        }
249    }
250}
251
252impl<'a, T: Default + Send + Sync + 'static> Entry<'a, T> {
253    /// Ensures a value is in the Entry by inserting the default value if vacant, and returns a
254    /// mutable reference to the value.
255    ///
256    /// Equivalent to `.or_insert_with(Default::default)`
257    ///
258    /// ## Example
259    ///
260    /// ```rust
261    /// let mut set = type_set::TypeSet::new();
262    /// assert_eq!(*set.entry::<&'static str>().or_default(), "");
263    /// set.insert("hello");
264    /// assert_eq!(*set.entry::<&'static str>().or_default(), "hello");
265    /// ```
266    pub fn or_default(self) -> &'a mut T {
267        #[allow(clippy::unwrap_or_default)]
268        // this is the implementation of or_default so it can't call or_default
269        self.or_insert_with(T::default)
270    }
271}
272
273impl<'a, T: Send + Sync + 'static> VacantEntry<'a, T> {
274    /// Sets the value of this entry to the provided `value`
275    pub fn insert(self, value: T) -> &'a mut T {
276        unwrap!(self.0.insert(Value::new(value)).downcast_mut())
277    }
278}
279
280impl<'a, T: Send + Sync + 'static> OccupiedEntry<'a, T> {
281    /// Gets a reference to the value in this entry
282    #[must_use]
283    pub fn get(&self) -> &T {
284        unwrap!(self.0.get().downcast_ref())
285    }
286
287    /// Gets a mutable reference to the value in the entry
288    ///
289    /// If you need a reference to the `OccupiedEntry` that may outlive the
290    /// destruction of the `Entry` value, see [`OccupiedEntry::into_mut`].
291    #[must_use]
292    pub fn get_mut(&mut self) -> &mut T {
293        unwrap!(self.0.get_mut().downcast_mut())
294    }
295
296    /// Sets the value of the entry to `value`, returning the entry's previous value.
297    pub fn insert(&mut self, value: T) -> T {
298        unwrap!(self.0.insert(Value::new(value)).downcast())
299    }
300
301    /// Take ownership of the value from this Entry
302    #[allow(clippy::must_use_candidate)] // sometimes we just want to take the value out and drop it
303    pub fn remove(self) -> T {
304        unwrap!(self.0.remove().downcast())
305    }
306
307    /// Converts the entry into a mutable reference to its value.
308    ///
309    /// If you need multiple references to the `OccupiedEntry`, see [`OccupiedEntry::get_mut`].
310    #[must_use]
311    pub fn into_mut(self) -> &'a mut T {
312        unwrap!(self.0.into_mut().downcast_mut())
313    }
314}
315
316impl<'a, T: Send + Sync + 'static> Deref for OccupiedEntry<'a, T> {
317    type Target = T;
318
319    fn deref(&self) -> &Self::Target {
320        self.get()
321    }
322}
323
324impl<'a, T: Send + Sync + 'static> DerefMut for OccupiedEntry<'a, T> {
325    fn deref_mut(&mut self) -> &mut Self::Target {
326        self.get_mut()
327    }
328}
329
330impl<'a, T: Send + Sync + 'static> From<OccupiedEntry<'a, T>> for Entry<'a, T> {
331    fn from(occupied_entry: OccupiedEntry<'a, T>) -> Self {
332        Self::Occupied(occupied_entry)
333    }
334}
335
336impl<'a, T: Send + Sync + 'static> From<VacantEntry<'a, T>> for Entry<'a, T> {
337    fn from(vacant_entry: VacantEntry<'a, T>) -> Self {
338        Self::Vacant(vacant_entry)
339    }
340}