Skip to main content

drop_tracker/
lib.rs

1//! Crate to check if a variable got correctly [dropped]. This crate is mostly useful in unit
2//! tests for code involving [`ManuallyDrop`], [`MaybeUninit`], unsafe memory management,
3//! custom containers, and more.
4//!
5//! [dropped]: https://doc.rust-lang.org/reference/destructors.html
6//! [`ManuallyDrop`]: std::mem::ManuallyDrop
7//! [`MaybeUninit`]: std::mem::MaybeUninit
8//!
9//! # Concepts
10//!
11//! The main struct of this crate is [`DropTracker`]. Once you initialize a tracker, you call
12//! [`DropTracker::track`] on it to get a [`DropItem`]. Each drop item is identified by a key;
13//! the key can be used at any time to check the state of the item and see if it's alive or if
14//! it has been dropped.
15//!
16//! # Examples
17//!
18//! This is how you would test that a container like [`Vec`] drops all its items when the container
19//! is dropped:
20//!
21//! ```
22//! use drop_tracker::DropTracker;
23//!
24//! let mut tracker = DropTracker::new();
25//!
26//! // Create a new vector and add a bunch of elements to it. The elements in this case are
27//! // identified by integer keys (1, 2, 3), but any hashable type would work.
28//! let v = vec![tracker.track(1),
29//!              tracker.track(2),
30//!              tracker.track(3)];
31//!
32//! // Assert that all elements in the vector are alive
33//! tracker.all_alive(1..=3)
34//!        .expect("expected all elements to be alive");
35//!
36//! // Once the vector is dropped, all items should be dropped with it
37//! drop(v);
38//! tracker.all_dropped(1..=3)
39//!        .expect("expected all elements to be dropped");
40//! ```
41//!
42//! This is how you would test a struct that involves [`MaybeUninit`]:
43//!
44//! ```should_panic
45//! # #![allow(dead_code)]
46//! use std::mem::MaybeUninit;
47//!
48//! struct MyOption<T> {
49//!     set: bool,
50//!     data: MaybeUninit<T>,
51//! }
52//!
53//! impl<T> MyOption<T> {
54//!     fn none() -> Self {
55//!         Self { set: false, data: MaybeUninit::uninit() }
56//!     }
57//!
58//!     fn some(x: T) -> Self {
59//!         Self { set: true, data: MaybeUninit::new(x) }
60//!     }
61//! }
62//!
63//! // BUG: MyOption<T> does not implement Drop!
64//! // BUG: The instance inside `data` may be initialized but not be properly destructed!
65//!
66//! // BUG: The following code will silently leak memory:
67//! let opt = MyOption::some(String::from("hello"));
68//! drop(opt); // the String does not get deallocated
69//!
70//! // DropTracker is able to catch this sort of bugs:
71//! use drop_tracker::DropTracker;
72//!
73//! let mut tracker = DropTracker::new();
74//! let opt = MyOption::some(tracker.track("item"));
75//!
76//! tracker.state(&"item")
77//!        .alive()
78//!        .expect("item is expected to be alive"); // works
79//!
80//! drop(opt);
81//!
82//! tracker.state(&"item")
83//!        .dropped()
84//!        .expect("item is expected to be dropped"); // panics, meaning that the bug was detected
85//! ```
86//!
87//! If you want to write more succint code and don't care about the error message, you can also use
88//! the following `assert` methods:
89//!
90//! * [`assert_alive(...)`](DropTracker::assert_alive) equivalent to
91//!   `state(...).alive().expect("error message")`
92//! * [`assert_dropped(...)`](DropTracker::assert_dropped) equivalent to
93//!   `state(...).dropped().expect("error message")`
94//! * [`assert_all_alive(...)`](DropTracker::assert_all_alive) equivalent to
95//!   `all_alive(...).expect("error message")`
96//! * [`assert_all_dropped(...)`](DropTracker::assert_all_dropped) equivalent to
97//!   `all_dropped(...).expect("error message")`
98//!
99//! Here is how the first example above could be rewritten more consisely using `assert` methods:
100//!
101//! ```
102//! use drop_tracker::DropTracker;
103//!
104//! let mut tracker = DropTracker::new();
105//! let v = vec![tracker.track(1),
106//!              tracker.track(2),
107//!              tracker.track(3)];
108//!
109//! tracker.assert_all_alive(1..=3);
110//! drop(v);
111//! tracker.assert_all_dropped(1..=3);
112//! ```
113//!
114//! # Double drop
115//!
116//! [`DropItem`] will panic if it gets dropped twice or more, as this is generally a bug and may
117//! cause undefined behavior. This feature can be used to identify bugs with code using
118//! [`ManuallyDrop`](std::mem::ManuallyDrop), [`MaybeUninit`](std::mem::MaybeUninit) or
119//! [`std::ptr::drop_in_place`], like in the following example:
120//!
121//! ```should_panic
122//! use std::ptr;
123//! use drop_tracker::DropTracker;
124//!
125//! let mut tracker = DropTracker::new();
126//! let mut item = tracker.track("something");
127//!
128//! unsafe { ptr::drop_in_place(&mut item); } // ok
129//! unsafe { ptr::drop_in_place(&mut item); } // panic!
130//! ```
131//!
132//! # Use in collections
133//!
134//! The [`DropItem`] instances returned by [`DropTracker::track`] hold a clone of the key passed
135//! to `track`. The `DropItem`s are [comparable](std::cmp) and [hashable](std::hash) if the
136//! underlying key is. This makes `DropItem` instances usable directly in collections like
137//! [`HashMap`](std::collections::HashMap), [`BTreeMap`](std::collections::BTreeMap),
138//! [`HashSet`](std::collections::HashSet) and many more.
139//!
140//! Here is an example involving [`HashSet`](std::collections::HashSet):
141//!
142//! ```
143//! use drop_tracker::DropTracker;
144//! use std::collections::HashSet;
145//!
146//! let mut tracker = DropTracker::new();
147//!
148//! let mut set = HashSet::from([
149//!     tracker.track(1),
150//!     tracker.track(2),
151//!     tracker.track(3),
152//! ]);
153//!
154//! set.remove(&3);
155//!
156//! tracker.state(&1).alive().expect("first item should be alive");
157//! tracker.state(&2).alive().expect("second item should be alive");
158//! tracker.state(&3).dropped().expect("third item should be dropped");
159//! ```
160//!
161//! Keys are required to be hashable and unique. If you need [`DropItem`] to hold a non-hashable
162//! value, or a repeated value, you can construct a [`DropItem`] with an arbitrary value using
163//! [`DropTracker::track_with_value`]:
164//!
165//! ```
166//! use drop_tracker::DropTracker;
167//!
168//! let mut tracker = DropTracker::new();
169//!
170//! // Construct items identified by integers and holding floats (which are not hashable)
171//! let item1 = tracker.track_with_value(1, 7.52);
172//! let item2 = tracker.track_with_value(2, 3.89);
173//!
174//! // Items compare according to their value
175//! assert!(item1 > item2); // 7.52 > 3.89
176//!
177//! // Items that support comparison can be put in a vector and sorted
178//! let mut v = vec![item1, item2];
179//! v.sort_by(|x, y| x.partial_cmp(y).unwrap());
180//! ```
181
182#![warn(missing_debug_implementations)]
183#![warn(missing_docs)]
184#![warn(unreachable_pub)]
185#![warn(unused_crate_dependencies)]
186#![warn(unused_qualifications)]
187#![doc(test(attr(deny(warnings))))]
188
189#[cfg(test)]
190mod tests;
191
192mod itemtraits;
193
194use std::borrow::Borrow;
195use std::collections::HashMap;
196use std::collections::hash_map::Entry;
197use std::error::Error;
198use std::fmt;
199use std::hash::Hash;
200use std::iter::FusedIterator;
201use std::mem::MaybeUninit;
202use std::sync::Arc;
203use std::sync::atomic::AtomicBool;
204use std::sync::atomic::Ordering;
205
206/// A type that represents the state of a [`DropItem`]: either alive or dropped.
207///
208/// See the [module documentation](self) for details.
209#[must_use = "you should check whether the status is alive or dropped"]
210#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
211pub enum State {
212    /// The item is alive.
213    Alive,
214    /// The item has been dropped, and its destructor has been called.
215    Dropped,
216}
217
218impl State {
219    /// Returns `true` if the state is [`Alive`](State::Alive).
220    ///
221    /// # Examples
222    ///
223    /// ```
224    /// use drop_tracker::State;
225    ///
226    /// assert!(State::Alive.is_alive());
227    /// assert!(!State::Dropped.is_alive());
228    /// ```
229    #[inline]
230    #[must_use = "if you intended to assert that this is alive, consider `.alive().expect()`"]
231    pub const fn is_alive(&self) -> bool {
232        match self {
233            Self::Alive => true,
234            Self::Dropped => false,
235        }
236    }
237
238    /// Returns `true` if the state is [`Dropped`](State::Dropped).
239    ///
240    /// # Examples
241    ///
242    /// ```
243    /// use drop_tracker::State;
244    ///
245    /// assert!(State::Dropped.is_dropped());
246    /// assert!(!State::Alive.is_dropped());
247    /// ```
248    #[inline]
249    #[must_use = "if you intended to assert that this is dropped, consider `.dropped().expect()`"]
250    pub const fn is_dropped(&self) -> bool {
251        match self {
252            Self::Alive => false,
253            Self::Dropped => true,
254        }
255    }
256
257    /// Returns [`Ok`] if the state is [`Alive`](State::Alive), [`Err`] otherwise.
258    ///
259    /// # Examples
260    ///
261    /// ```
262    /// use drop_tracker::DroppedError;
263    /// use drop_tracker::State;
264    ///
265    /// assert_eq!(State::Alive.alive(), Ok(()));
266    /// assert_eq!(State::Dropped.alive(), Err(DroppedError));
267    /// ```
268    #[inline]
269    #[must_use = "if you intended to assert that this is alive, consider `.alive().expect()`"]
270    pub const fn alive(&self) -> Result<(), DroppedError> {
271        match self {
272            Self::Alive => Ok(()),
273            Self::Dropped => Err(DroppedError),
274        }
275    }
276
277    /// Returns [`Ok`] if the state is [`Dropped`](State::Dropped), [`Err`] otherwise.
278    ///
279    /// # Examples
280    ///
281    /// ```
282    /// use drop_tracker::AliveError;
283    /// use drop_tracker::State;
284    ///
285    /// assert_eq!(State::Dropped.dropped(), Ok(()));
286    /// assert_eq!(State::Alive.dropped(), Err(AliveError));
287    /// ```
288    #[inline]
289    #[must_use = "if you intended to assert that this is dropped, consider `.dropped().expect()`"]
290    pub const fn dropped(&self) -> Result<(), AliveError> {
291        match self {
292            Self::Alive => Err(AliveError),
293            Self::Dropped => Ok(()),
294        }
295    }
296}
297
298// Uses an `AtomicBool` (as opposed to e.g. a `RefCell`) to ensure that `DropTracker` and
299// `DropItem` are `Send`, `Sync` and `UnwindSafe`.
300#[derive(Clone, Debug)]
301struct StateCell(Arc<AtomicBool>);
302
303impl StateCell {
304    #[inline]
305    #[must_use]
306    fn new(state: State) -> Self {
307        Self(Arc::new(AtomicBool::new(state.is_dropped())))
308    }
309
310    #[inline]
311    fn get(&self) -> State {
312        match self.0.load(Ordering::Relaxed) {
313            false => State::Alive,
314            true => State::Dropped,
315        }
316    }
317
318    #[inline]
319    fn replace(&mut self, state: State) -> State {
320        match self.0.swap(state.is_dropped(), Ordering::Relaxed) {
321            false => State::Alive,
322            true => State::Dropped,
323        }
324    }
325
326    #[inline]
327    #[must_use]
328    fn is_alive(&self) -> bool {
329        self.get().is_alive()
330    }
331
332    #[inline]
333    #[must_use]
334    fn is_dropped(&self) -> bool {
335        self.get().is_dropped()
336    }
337}
338
339/// Creates [`DropItem`]s and tracks their state.
340///
341/// [`DropItem`]s can be created using [`track`](DropTracker::track) or
342/// [`try_track`](DropTracker::try_track) and their state can be later checked using
343/// [`state`](DropTracker::state).
344///
345/// [`DropItem`]s are identified by keys. A key can be of any type that implement the [`Hash`]
346/// and [`Eq`] traits, which include, for example: [`u32`], [`char`], [`str`], ...
347///
348/// See the [module documentation](self) for details.
349#[derive(Default, Debug)]
350pub struct DropTracker<K> {
351    tracked: HashMap<K, StateCell>,
352}
353
354impl<K> DropTracker<K> {
355    /// Creates a new empty `DropTracker`.
356    ///
357    /// # Examples
358    ///
359    /// ```
360    /// use drop_tracker::DropTracker;
361    ///
362    /// let tracker = DropTracker::<u32>::new();
363    /// assert_eq!(tracker.tracked().count(), 0);
364    /// ```
365    #[must_use]
366    pub fn new() -> Self {
367        Self {
368            tracked: HashMap::new(),
369        }
370    }
371
372    /// Returns an iterator over the keys tracked by this `DropTracker`.
373    ///
374    /// The order of keys returned by this iterator is non deterministic.
375    ///
376    /// # Examples
377    ///
378    /// ```
379    /// # #![allow(unused_variables)]
380    /// use drop_tracker::DropTracker;
381    ///
382    /// let mut tracker = DropTracker::new();
383    /// let item_a = tracker.track("a");
384    /// let item_b = tracker.track("b");
385    /// let item_c = tracker.track("c");
386    ///
387    /// let mut keys = tracker.tracked()
388    ///                       .collect::<Vec<&&str>>();
389    /// keys.sort();
390    /// assert_eq!(keys, [&"a", &"b", &"c"]);
391    /// ```
392    pub fn tracked(&self) -> impl Clone + ExactSizeIterator<Item = &K> + FusedIterator {
393        self.tracked.keys()
394    }
395
396    /// Returns an iterator over the keys tracked by this `DropTracker` that are alive.
397    ///
398    /// The order of keys returned by this iterator is non deterministic.
399    ///
400    /// # Examples
401    ///
402    /// ```
403    /// use drop_tracker::DropTracker;
404    ///
405    /// let mut tracker = DropTracker::new();
406    /// let item_a = tracker.track("a");
407    /// let item_b = tracker.track("b");
408    /// let item_c = tracker.track("c");
409    ///
410    /// drop(item_c);
411    ///
412    /// let mut alive_keys = tracker.alive()
413    ///                             .collect::<Vec<&&str>>();
414    /// alive_keys.sort();
415    /// assert_eq!(alive_keys, [&"a", &"b"]);
416    ///
417    /// drop(item_a);
418    /// drop(item_b);
419    ///
420    /// assert_eq!(tracker.alive().count(), 0);
421    /// ```
422    pub fn alive(&self) -> impl Clone + FusedIterator<Item = &K> {
423        self.tracked
424            .iter()
425            .filter(|(_, state)| state.is_alive())
426            .map(|(key, _)| key)
427    }
428
429    /// Returns an iterator over the keys tracked by this `DropTracker` that have been dropped.
430    ///
431    /// The order of keys returned by this iterator is non deterministic.
432    ///
433    /// # Examples
434    ///
435    /// ```
436    /// # #![allow(unused_variables)]
437    /// use drop_tracker::DropTracker;
438    ///
439    /// let mut tracker = DropTracker::new();
440    /// let item_a = tracker.track("a");
441    /// let item_b = tracker.track("b");
442    /// let item_c = tracker.track("c");
443    ///
444    /// assert_eq!(tracker.dropped().count(), 0);
445    ///
446    /// drop(item_a);
447    /// drop(item_b);
448    ///
449    /// let mut alive_keys = tracker.dropped()
450    ///                             .collect::<Vec<&&str>>();
451    /// alive_keys.sort();
452    /// assert_eq!(alive_keys, [&"a", &"b"]);
453    /// ```
454    pub fn dropped(&self) -> impl Clone + FusedIterator<Item = &K> {
455        self.tracked
456            .iter()
457            .filter(|(_, state)| state.is_dropped())
458            .map(|(key, _)| key)
459    }
460
461    /// Forgets all the items tracked by this `DropTracker`.
462    ///
463    /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
464    /// longer be possible to query their status after forgetting them.
465    ///
466    /// # Examples
467    ///
468    /// ```
469    /// # #![allow(unused_variables)]
470    /// use drop_tracker::DropTracker;
471    ///
472    /// let mut tracker = DropTracker::new();
473    /// assert_eq!(tracker.tracked().count(), 0);
474    ///
475    /// let item_a = tracker.track("a");
476    /// let item_b = tracker.track("b");
477    /// let item_c = tracker.track("c");
478    /// assert_eq!(tracker.tracked().count(), 3);
479    ///
480    /// tracker.forget_all();
481    /// assert_eq!(tracker.tracked().count(), 0);
482    /// ```
483    pub fn forget_all(&mut self) {
484        self.tracked.clear();
485    }
486
487    /// Forgets all the items tracked by this `DropTracker` that have been dropped.
488    ///
489    /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
490    /// longer be possible to query their status after forgetting them.
491    ///
492    /// # Examples
493    ///
494    /// ```
495    /// # #![allow(unused_variables)]
496    /// use drop_tracker::DropTracker;
497    ///
498    /// let mut tracker = DropTracker::new();
499    /// assert_eq!(tracker.tracked().count(), 0);
500    ///
501    /// let item_a = tracker.track("a");
502    /// let item_b = tracker.track("b");
503    /// let item_c = tracker.track("c");
504    /// assert_eq!(tracker.tracked().count(), 3);
505    ///
506    /// // After dropping an item, the item is still tracked
507    /// drop(item_a);
508    /// drop(item_b);
509    /// assert_eq!(tracker.tracked().count(), 3);
510    ///
511    /// // Use `forget_dropped` to lose track of items that have been dropped
512    /// tracker.forget_dropped();
513    /// assert_eq!(tracker.tracked().count(), 1);
514    ///
515    /// let mut keys = tracker.tracked()
516    ///                       .collect::<Vec<&&str>>();
517    /// keys.sort();
518    /// assert_eq!(keys, [&"c"]);
519    /// ```
520    pub fn forget_dropped(&mut self) {
521        self.tracked.retain(|_, state| state.is_alive())
522    }
523}
524
525impl<K: Hash + Eq> DropTracker<K> {
526    /// Creates a new [`DropItem`] identified by the given key.
527    ///
528    /// The value held by the `DropItem` is a clone of the key. Use
529    /// [`DropTracker::track_with_value`] if you wish to specify a custom value.
530    ///
531    /// # Panics
532    ///
533    /// Panics if the key is already used by another tracked item.
534    ///
535    /// Call [`forget`](DropTracker::forget),
536    /// [`forget_dropped`](DropTracker::forget_dropped) or
537    /// [`forget_all`](DropTracker::forget_all) if you wish to reuse a key from an item you no
538    /// longer need to track.
539    ///
540    /// See [`try_track`](DropTracker::try_track) for a variant of this method that does not panic.
541    ///
542    /// # Examples
543    ///
544    /// ```
545    /// use drop_tracker::DropTracker;
546    /// use drop_tracker::State;
547    ///
548    /// let mut tracker = DropTracker::new();
549    ///
550    /// let item = tracker.track("abc");
551    /// assert_eq!(tracker.state("abc"), State::Alive);
552    ///
553    /// drop(item);
554    /// assert_eq!(tracker.state("abc"), State::Dropped);
555    /// ```
556    ///
557    /// Using the same key twice causes a panic:
558    ///
559    /// ```should_panic
560    /// # #![allow(unused_variables)]
561    /// use drop_tracker::DropTracker;
562    ///
563    /// let mut tracker = DropTracker::new();
564    ///
565    /// let item1 = tracker.track("abc");
566    /// let item2 = tracker.track("abc"); // panics!
567    /// ```
568    ///
569    /// Use [`forget`](DropTracker::forget) to reuse the same key:
570    ///
571    /// ```
572    /// # #![allow(unused_variables)]
573    /// use drop_tracker::DropTracker;
574    ///
575    /// let mut tracker = DropTracker::new();
576    ///
577    /// let item1 = tracker.track("abc");
578    /// let _ = tracker.forget("abc");
579    /// let item2 = tracker.track("abc"); // works
580    /// ```
581    pub fn track(&mut self, key: K) -> DropItem<K>
582    where
583        K: Clone,
584    {
585        self.try_track(key).expect("cannot track key")
586    }
587
588    /// Creates a new [`DropItem`] identified by the given key, or [`Err`] if the key is
589    /// already in use.
590    ///
591    /// The value held by the `DropItem` is a clone of the key. Use
592    /// [`DropTracker::try_track_with_value`] if you wish to specify a custom value.
593    ///
594    /// See also [`track`](DropTracker::track).
595    ///
596    /// # Examples
597    ///
598    /// ```
599    /// # #![allow(unused_variables)]
600    /// use drop_tracker::DropTracker;
601    ///
602    /// let mut tracker = DropTracker::new();
603    ///
604    /// let item = tracker.try_track("abc");
605    /// assert!(item.is_ok());
606    ///
607    /// let item = tracker.try_track("abc");
608    /// assert!(item.is_err()); // key is already used
609    /// ```
610    pub fn try_track(&mut self, key: K) -> Result<DropItem<K>, CollisionError>
611    where
612        K: Clone,
613    {
614        let value = key.clone();
615        self.try_track_with_value(key, value)
616    }
617
618    /// Creates a new [`DropItem`] identified by the given key and holding the given value.
619    ///
620    /// # Panics
621    ///
622    /// Panics if the key is already used by another tracked item.
623    ///
624    /// Call [`forget`](DropTracker::forget),
625    /// [`forget_dropped`](DropTracker::forget_dropped) or
626    /// [`forget_all`](DropTracker::forget_all) if you wish to reuse a key from an item you no
627    /// longer need to track.
628    ///
629    /// See [`try_track_with_value`](DropTracker::try_track_with_value) for a variant of this
630    /// method that does not panic.
631    ///
632    /// # Examples
633    ///
634    /// ```
635    /// use drop_tracker::DropTracker;
636    /// use drop_tracker::State;
637    ///
638    /// let mut tracker = DropTracker::new();
639    ///
640    /// let item = tracker.track_with_value("abc", vec![1, 2, 3]);
641    /// assert_eq!(tracker.state("abc"), State::Alive);
642    ///
643    /// drop(item);
644    /// assert_eq!(tracker.state("abc"), State::Dropped);
645    /// ```
646    ///
647    /// Using the same key twice causes a panic:
648    ///
649    /// ```should_panic
650    /// # #![allow(unused_variables)]
651    /// use drop_tracker::DropTracker;
652    ///
653    /// let mut tracker = DropTracker::new();
654    ///
655    /// let item1 = tracker.track_with_value("abc", vec![1, 2, 3]);
656    /// let item2 = tracker.track_with_value("abc", vec![4, 5, 6]); // panics!
657    /// ```
658    ///
659    /// Use [`forget`](DropTracker::forget) to reuse the same key:
660    ///
661    /// ```
662    /// # #![allow(unused_variables)]
663    /// use drop_tracker::DropTracker;
664    ///
665    /// let mut tracker = DropTracker::new();
666    ///
667    /// let item1 = tracker.track_with_value("abc", vec![1, 2, 3]);
668    /// let _ = tracker.forget("abc");
669    /// let item2 = tracker.track_with_value("abc", vec![4, 5, 6]); // works
670    /// ```
671    pub fn track_with_value<V>(&mut self, key: K, value: V) -> DropItem<V> {
672        self.try_track_with_value(key, value)
673            .expect("cannot track key")
674    }
675
676    /// Creates a new [`DropItem`] identified by the given key and holding the given value, or
677    /// [`Err`] if the key is already in use.
678    ///
679    /// See also [`track_with_value`](DropTracker::track_with_value).
680    ///
681    /// # Examples
682    ///
683    /// ```
684    /// # #![allow(unused_variables)]
685    /// use drop_tracker::DropTracker;
686    ///
687    /// let mut tracker = DropTracker::new();
688    ///
689    /// let item = tracker.try_track_with_value("abc", vec![1, 2, 3]);
690    /// assert!(item.is_ok());
691    ///
692    /// let item = tracker.try_track_with_value("abc", vec![4, 5, 6]);
693    /// assert!(item.is_err()); // key is already used
694    /// ```
695    pub fn try_track_with_value<V>(
696        &mut self,
697        key: K,
698        value: V,
699    ) -> Result<DropItem<V>, CollisionError> {
700        let state = StateCell::new(State::Alive);
701        match self.tracked.entry(key) {
702            Entry::Occupied(_) => Err(CollisionError),
703            Entry::Vacant(entry) => {
704                entry.insert(state.clone());
705                Ok(DropItem::new(value, state))
706            }
707        }
708    }
709
710    /// Creates multiple new [`DropItem`] structs, each identified by a key from the given
711    /// iterable.
712    ///
713    /// Calling `track_many` is equivalent to calling [`track`](DropTracker::track) multiple times.
714    ///
715    /// Note: this method returns an iterator that must be fully consumed in order for the items to
716    /// be tracked. Discarding the return value would cause items to be ignored and not be tracked.
717    ///
718    /// # Examples
719    ///
720    /// ```
721    /// use drop_tracker::DropTracker;
722    /// use drop_tracker::State;
723    ///
724    /// let mut tracker = DropTracker::new();
725    ///
726    /// let mut items = tracker.track_many(["abc", "def", "ghi"]);
727    ///
728    /// let abc = items.next().unwrap();
729    /// let def = items.next().unwrap();
730    /// let ghi = items.next().unwrap();
731    /// assert_eq!(items.next(), None);
732    /// drop(items);
733    ///
734    /// assert_eq!(abc, "abc");
735    /// assert_eq!(def, "def");
736    /// assert_eq!(ghi, "ghi");
737    ///
738    /// assert_eq!(tracker.state("abc"), State::Alive);
739    /// assert_eq!(tracker.state("def"), State::Alive);
740    /// assert_eq!(tracker.state("ghi"), State::Alive);
741    ///
742    /// drop(def);
743    ///
744    /// assert_eq!(tracker.state("abc"), State::Alive);
745    /// assert_eq!(tracker.state("def"), State::Dropped);
746    /// assert_eq!(tracker.state("ghi"), State::Alive);
747    ///
748    /// # drop(abc);
749    /// # drop(ghi);
750    /// ```
751    pub fn track_many<'a, Iter>(&'a mut self, keys: Iter) -> impl Iterator<Item = DropItem<K>> + 'a
752    where
753        Iter: IntoIterator<Item = K> + 'a,
754        K: Clone,
755    {
756        keys.into_iter().map(|key| self.track(key))
757    }
758}
759
760impl<K: Hash + Eq> DropTracker<K> {
761    /// Checks the state of a [`DropItem`] tracked by this `DropTracker`: [alive](State::Alive) or
762    /// [dropped](State::Dropped).
763    ///
764    /// # Panics
765    ///
766    /// Panics if the given key is not tracked.
767    ///
768    /// See [`try_state`](DropTracker::try_state) for a variant of this method that does not panic.
769    ///
770    /// # Examples
771    ///
772    /// ```
773    /// use drop_tracker::DropTracker;
774    /// use drop_tracker::State;
775    ///
776    /// let mut tracker = DropTracker::new();
777    ///
778    /// let item = tracker.track("abc");
779    /// assert_eq!(tracker.state("abc"), State::Alive);
780    ///
781    /// drop(item);
782    /// assert_eq!(tracker.state("abc"), State::Dropped);
783    /// ```
784    ///
785    /// Querying a key that is not tracked causes a panic:
786    ///
787    /// ```should_panic
788    /// # #![allow(unused_variables)]
789    /// use drop_tracker::DropTracker;
790    ///
791    /// let mut tracker = DropTracker::new();
792    ///
793    /// let item = tracker.track("abc");
794    /// let state = tracker.state("def"); // panics!
795    /// ```
796    pub fn state<Q>(&self, key: &Q) -> State
797    where
798        K: Borrow<Q>,
799        Q: Hash + Eq + ?Sized,
800    {
801        self.try_state(key).expect("cannot get state")
802    }
803
804    /// Checks the state of a [`DropItem`] tracked by this `DropTracker`: [alive](State::Alive) or
805    /// [dropped](State::Dropped). Returns [`Err`] it the given key is not tracked.
806    ///
807    /// See also [`state`](DropTracker::state).
808    ///
809    /// # Examples
810    ///
811    /// ```
812    /// use drop_tracker::DropTracker;
813    /// use drop_tracker::NotTrackedError;
814    /// use drop_tracker::State;
815    ///
816    /// let mut tracker = DropTracker::new();
817    ///
818    /// let item = tracker.track("abc");
819    /// assert_eq!(tracker.try_state("abc"), Ok(State::Alive));
820    /// assert_eq!(tracker.try_state("def"), Err(NotTrackedError));
821    ///
822    /// drop(item);
823    /// assert_eq!(tracker.try_state("abc"), Ok(State::Dropped));
824    /// assert_eq!(tracker.try_state("def"), Err(NotTrackedError));
825    /// ```
826    pub fn try_state<Q>(&self, key: &Q) -> Result<State, NotTrackedError>
827    where
828        K: Borrow<Q>,
829        Q: Hash + Eq + ?Sized,
830    {
831        self.tracked
832            .get(key)
833            .ok_or(NotTrackedError)
834            .map(|state| state.get())
835    }
836
837    /// Forgets an item tracked by this `DropTracker`, and returns its current state
838    /// ([alive](State::Alive) or [dropped](State::Dropped)).
839    ///
840    /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
841    /// longer be possible to query their status after forgetting them.
842    ///
843    /// # Panics
844    ///
845    /// Panics if the given key is not tracked.
846    ///
847    /// See [`try_forget`](DropTracker::try_forget) for a variant of this method that does not panic.
848    ///
849    /// # Examples
850    ///
851    /// ```
852    /// # #![allow(unused_variables)]
853    /// use drop_tracker::DropTracker;
854    /// use drop_tracker::State;
855    ///
856    /// let mut tracker = DropTracker::new();
857    ///
858    /// let item = tracker.track("a");
859    /// assert!(tracker.is_tracked("a"));
860    ///
861    /// assert_eq!(tracker.forget("a"), State::Alive);
862    /// assert!(!tracker.is_tracked("a"));
863    /// ```
864    ///
865    /// Forgetting a key that is not tracked causes a panic:
866    ///
867    /// ```should_panic
868    /// # #![allow(unused_variables)]
869    /// use drop_tracker::DropTracker;
870    ///
871    /// let mut tracker = DropTracker::new();
872    ///
873    /// let item = tracker.track("abc");
874    /// let state = tracker.forget("def"); // panics!
875    /// ```
876    pub fn forget<Q>(&mut self, key: &Q) -> State
877    where
878        K: Borrow<Q>,
879        Q: Hash + Eq + ?Sized,
880    {
881        self.try_forget(key).expect("cannot forget item")
882    }
883
884    /// Forgets an item tracked by this `DropTracker`, and returns its current state
885    /// ([alive](State::Alive) or [dropped](State::Dropped)), or [`Err`] if the item is not
886    /// tracked.
887    ///
888    /// The `DropItem`s previously returned by the tracker will still work normally, but it will no
889    /// longer be possible to query their status after forgetting them.
890    ///
891    /// See also [`forget`](DropTracker::forget).
892    ///
893    /// # Examples
894    ///
895    /// ```
896    /// # #![allow(unused_variables)]
897    /// use drop_tracker::DropTracker;
898    /// use drop_tracker::NotTrackedError;
899    /// use drop_tracker::State;
900    ///
901    /// let mut tracker = DropTracker::new();
902    ///
903    /// let item = tracker.track("a");
904    /// assert!(tracker.is_tracked("a"));
905    ///
906    /// assert_eq!(tracker.try_forget("a"), Ok(State::Alive));
907    /// assert_eq!(tracker.try_forget("b"), Err(NotTrackedError));
908    /// ```
909    pub fn try_forget<Q>(&mut self, key: &Q) -> Result<State, NotTrackedError>
910    where
911        K: Borrow<Q>,
912        Q: Hash + Eq + ?Sized,
913    {
914        self.tracked
915            .remove(key)
916            .ok_or(NotTrackedError)
917            .map(|state| state.get())
918    }
919
920    /// Returns [`true`] if an item identified by the given key is tracked by this `DropTracker`,
921    /// [`false`] otherwise.
922    ///
923    /// # Examples
924    ///
925    /// ```
926    /// # #![allow(unused_variables)]
927    /// use drop_tracker::DropTracker;
928    ///
929    /// let mut tracker = DropTracker::new();
930    /// assert!(!tracker.is_tracked("abc"));
931    ///
932    /// let item = tracker.track("abc");
933    /// assert!(tracker.is_tracked("abc"));
934    /// ```
935    #[must_use]
936    pub fn is_tracked<Q>(&self, key: &Q) -> bool
937    where
938        K: Borrow<Q>,
939        Q: Hash + Eq + ?Sized,
940    {
941        self.try_state(key).is_ok()
942    }
943
944    /// Returns [`Ok`] if all the given keys point to items that are [alive](State::Alive),
945    /// [`Err`] otherwise.
946    ///
947    /// An error may be returned in two cases: either a key is not tracked, or it has been dropped.
948    ///
949    /// This method returns `Ok` if the sequence of keys passed is empty.
950    ///
951    /// # Examples
952    ///
953    /// ```
954    /// # #![allow(unused_variables)]
955    /// use drop_tracker::DropTracker;
956    /// use drop_tracker::NotAllAliveError;
957    ///
958    /// let mut tracker = DropTracker::new();
959    ///
960    /// let item1 = tracker.track(1);
961    /// let item2 = tracker.track(2);
962    /// let item3 = tracker.track(3);
963    /// let item4 = tracker.track(4);
964    ///
965    /// drop(item3);
966    /// drop(item4);
967    ///
968    /// assert_eq!(tracker.all_alive([1, 2]), Ok(()));
969    ///
970    /// assert_eq!(tracker.all_alive([1, 2, 3, 4, 5, 6]),
971    ///            Err(NotAllAliveError {
972    ///                dropped: vec![3, 4],
973    ///                untracked: vec![5, 6],
974    ///            }));
975    /// ```
976    ///
977    /// Passing an empty set of keys returns `Ok`:
978    ///
979    /// ```
980    /// use drop_tracker::DropTracker;
981    ///
982    /// let tracker = DropTracker::<()>::new();
983    /// assert_eq!(tracker.all_alive([(); 0]), Ok(()));
984    /// ```
985    pub fn all_alive<Q, Item, Iter>(&self, iter: Iter) -> Result<(), NotAllAliveError<Item>>
986    where
987        K: Borrow<Q>,
988        Q: Hash + Eq + ?Sized,
989        Item: Borrow<Q>,
990        Iter: IntoIterator<Item = Item>,
991    {
992        // Vec won't allocate any memory until items are pushed to it, so if this method does not
993        // fail, no memory will be allocated
994        let mut err = NotAllAliveError {
995            dropped: Vec::new(),
996            untracked: Vec::new(),
997        };
998
999        for key in iter {
1000            match self.try_state(key.borrow()) {
1001                Ok(State::Alive) => (),
1002                Ok(State::Dropped) => err.dropped.push(key),
1003                Err(NotTrackedError) => err.untracked.push(key),
1004            }
1005        }
1006
1007        if err.dropped.is_empty() && err.untracked.is_empty() {
1008            Ok(())
1009        } else {
1010            Err(err)
1011        }
1012    }
1013
1014    /// Returns [`Ok`] if all the given keys point to items that are [dropped](State::Dropped),
1015    /// [`Err`] otherwise.
1016    ///
1017    /// An error may be returned in two cases: either a key is not tracked, or it is alive.
1018    ///
1019    /// This method returns `Ok` if the sequence of keys passed is empty.
1020    ///
1021    /// # Examples
1022    ///
1023    /// ```
1024    /// # #![allow(unused_variables)]
1025    /// use drop_tracker::DropTracker;
1026    /// use drop_tracker::NotAllDroppedError;
1027    ///
1028    /// let mut tracker = DropTracker::new();
1029    ///
1030    /// let item1 = tracker.track(1);
1031    /// let item2 = tracker.track(2);
1032    /// let item3 = tracker.track(3);
1033    /// let item4 = tracker.track(4);
1034    ///
1035    /// drop(item3);
1036    /// drop(item4);
1037    ///
1038    /// assert_eq!(tracker.all_dropped([3, 4]), Ok(()));
1039    ///
1040    /// assert_eq!(tracker.all_dropped([1, 2, 3, 4, 5, 6]),
1041    ///            Err(NotAllDroppedError {
1042    ///                alive: vec![1, 2],
1043    ///                untracked: vec![5, 6],
1044    ///            }));
1045    /// ```
1046    ///
1047    /// Passing an empty set of keys returns `Ok`:
1048    ///
1049    /// ```
1050    /// use drop_tracker::DropTracker;
1051    ///
1052    /// let tracker = DropTracker::<()>::new();
1053    /// assert_eq!(tracker.all_dropped([(); 0]), Ok(()));
1054    /// ```
1055    pub fn all_dropped<Q, Item, Iter>(&self, iter: Iter) -> Result<(), NotAllDroppedError<Item>>
1056    where
1057        K: Borrow<Q>,
1058        Q: Hash + Eq + ?Sized,
1059        Item: Borrow<Q>,
1060        Iter: IntoIterator<Item = Item>,
1061    {
1062        // Vec won't allocate any memory until items are pushed to it, so if this method does not
1063        // fail, no memory will be allocated
1064        let mut err = NotAllDroppedError {
1065            alive: Vec::new(),
1066            untracked: Vec::new(),
1067        };
1068
1069        for key in iter {
1070            match self.try_state(key.borrow()) {
1071                Ok(State::Alive) => err.alive.push(key),
1072                Ok(State::Dropped) => (),
1073                Err(NotTrackedError) => err.untracked.push(key),
1074            }
1075        }
1076
1077        if err.alive.is_empty() && err.untracked.is_empty() {
1078            Ok(())
1079        } else {
1080            Err(err)
1081        }
1082    }
1083
1084    /// Returns [`Ok`] if all the keys tracked are [alive](State::Alive), [`Err`] otherwise.
1085    ///
1086    /// The error returned references an arbitrary keys that was found [dropped](State::Dropped).
1087    ///
1088    /// If the tracker is empty, this method returns `Ok`.
1089    ///
1090    /// # Examples
1091    ///
1092    /// ```
1093    /// # #![allow(unused_variables)]
1094    /// use drop_tracker::DropTracker;
1095    /// use drop_tracker::SomeDroppedError;
1096    ///
1097    /// let mut tracker = DropTracker::new();
1098    ///
1099    /// let item1 = tracker.track(1);
1100    /// let item2 = tracker.track(2);
1101    /// let item3 = tracker.track(3);
1102    ///
1103    /// assert_eq!(tracker.fully_alive(), Ok(()));
1104    ///
1105    /// drop(item1);
1106    ///
1107    /// assert_eq!(tracker.fully_alive(), Err(SomeDroppedError { dropped: &1 }));
1108    /// ```
1109    ///
1110    /// Calling `fully_alive()` on an empty tracker always returns `Ok`:
1111    ///
1112    /// ```
1113    /// use drop_tracker::DropTracker;
1114    ///
1115    /// let tracker = DropTracker::<()>::new();
1116    /// assert_eq!(tracker.fully_alive(), Ok(()));
1117    /// ```
1118    pub fn fully_alive(&self) -> Result<(), SomeDroppedError<'_, K>> {
1119        let dropped = self
1120            .tracked
1121            .iter()
1122            .find(|(_, state)| state.is_dropped())
1123            .map(|(key, _)| key);
1124        match dropped {
1125            None => Ok(()),
1126            Some(dropped) => Err(SomeDroppedError { dropped }),
1127        }
1128    }
1129
1130    /// Returns [`Ok`] if all the keys tracked are [dropped](State::Dropped), [`Err`] otherwise.
1131    ///
1132    /// The error returned references an arbitrary keys that was found [alive](State::Alive).
1133    ///
1134    /// If the tracker is empty, this method returns `Ok`.
1135    ///
1136    /// # Examples
1137    ///
1138    /// ```
1139    /// # #![allow(unused_variables)]
1140    /// use drop_tracker::DropTracker;
1141    /// use drop_tracker::SomeAliveError;
1142    ///
1143    /// let mut tracker = DropTracker::new();
1144    ///
1145    /// let item1 = tracker.track(1);
1146    /// let item2 = tracker.track(2);
1147    /// let item3 = tracker.track(3);
1148    ///
1149    /// drop(item1);
1150    /// drop(item2);
1151    ///
1152    /// assert_eq!(tracker.fully_dropped(), Err(SomeAliveError { alive: &3 }));
1153    ///
1154    /// drop(item3);
1155    ///
1156    /// assert_eq!(tracker.fully_dropped(), Ok(()));
1157    /// ```
1158    ///
1159    /// Calling `fully_dropped()` on an empty tracker always returns `Ok`:
1160    ///
1161    /// ```
1162    /// use drop_tracker::DropTracker;
1163    ///
1164    /// let tracker = DropTracker::<()>::new();
1165    /// assert_eq!(tracker.fully_dropped(), Ok(()));
1166    /// ```
1167    pub fn fully_dropped(&self) -> Result<(), SomeAliveError<'_, K>> {
1168        let alive = self
1169            .tracked
1170            .iter()
1171            .find(|(_, state)| state.is_alive())
1172            .map(|(key, _)| key);
1173        match alive {
1174            None => Ok(()),
1175            Some(alive) => Err(SomeAliveError { alive }),
1176        }
1177    }
1178
1179    /// Checks that all the given key points to an item that is [alive](State::Alive), panics
1180    /// otherwise.
1181    ///
1182    /// `assert_alive(...)` is a shortcut for `state(...).alive().unwrap()`. See
1183    /// [`state()`](DropTracker::state) for more details.
1184    ///
1185    /// # Panics
1186    ///
1187    /// If the key is not tracked, or if it has been dropped.
1188    ///
1189    /// # Examples
1190    ///
1191    /// ```should_panic
1192    /// # #![allow(unused_variables)]
1193    /// use drop_tracker::DropTracker;
1194    ///
1195    /// let mut tracker = DropTracker::new();
1196    ///
1197    /// let item2 = tracker.track(1);
1198    /// let item2 = tracker.track(2);
1199    ///
1200    /// drop(item2);
1201    ///
1202    /// tracker.assert_alive(&1); // succeeds
1203    /// tracker.assert_alive(&2); // panics (item was dropped)
1204    /// tracker.assert_alive(&3); // panics (key is not tracked)
1205    /// ```
1206    pub fn assert_alive<Q>(&self, key: &Q)
1207    where
1208        K: Borrow<Q>,
1209        Q: Hash + Eq + ?Sized,
1210    {
1211        let state = match self.try_state(key) {
1212            Ok(state) => state,
1213            Err(err) => panic!("{err}"),
1214        };
1215        match state.alive() {
1216            Ok(()) => (),
1217            Err(err) => panic!("{err}"),
1218        }
1219    }
1220
1221    /// Checks that all the given key points to an item that is [dropped](State::Dropped), panics
1222    /// otherwise.
1223    ///
1224    /// `assert_dropped(...)` is a shortcut for `state(...).dropped().unwrap()`. See
1225    /// [`state()`](DropTracker::state) for more details.
1226    ///
1227    /// # Panics
1228    ///
1229    /// If the key is not tracked, or if it is alive.
1230    ///
1231    /// # Examples
1232    ///
1233    /// ```should_panic
1234    /// # #![allow(unused_variables)]
1235    /// use drop_tracker::DropTracker;
1236    ///
1237    /// let mut tracker = DropTracker::new();
1238    ///
1239    /// let item1 = tracker.track(1);
1240    /// let item2 = tracker.track(2);
1241    ///
1242    /// drop(item1);
1243    ///
1244    /// tracker.assert_dropped(&1); // succeeds
1245    /// tracker.assert_dropped(&2); // panics (item is alive)
1246    /// tracker.assert_dropped(&3); // panics (key is not tracked)
1247    /// ```
1248    pub fn assert_dropped<Q>(&self, key: &Q)
1249    where
1250        K: Borrow<Q>,
1251        Q: Hash + Eq + ?Sized,
1252    {
1253        let state = match self.try_state(key) {
1254            Ok(state) => state,
1255            Err(err) => panic!("{err}"),
1256        };
1257        match state.dropped() {
1258            Ok(()) => (),
1259            Err(err) => panic!("{err}"),
1260        }
1261    }
1262
1263    /// Checks that all the given keys point to items that are [alive](State::Alive), panics
1264    /// otherwise.
1265    ///
1266    /// `assert_all_alive(...)` is a shortcut for `all_alive(...).unwrap()`. See
1267    /// [`all_alive()`](DropTracker::all_alive) for more details.
1268    ///
1269    /// # Panics
1270    ///
1271    /// If a key is not tracked, or if it has been dropped.
1272    ///
1273    /// # Examples
1274    ///
1275    /// ```should_panic
1276    /// # #![allow(unused_variables)]
1277    /// use drop_tracker::DropTracker;
1278    ///
1279    /// let mut tracker = DropTracker::new();
1280    ///
1281    /// let item1 = tracker.track(1);
1282    /// let item2 = tracker.track(2);
1283    /// let item3 = tracker.track(3);
1284    /// let item4 = tracker.track(4);
1285    ///
1286    /// drop(item3);
1287    /// drop(item4);
1288    ///
1289    /// tracker.assert_all_alive([1, 2]); // succeeds
1290    /// tracker.assert_all_alive([3, 4]); // panics (items were dropped)
1291    /// tracker.assert_all_alive([5, 6]); // panics (keys are not tracked)
1292    /// ```
1293    ///
1294    /// Passing an empty set of keys succeeds:
1295    ///
1296    /// ```
1297    /// use drop_tracker::DropTracker;
1298    ///
1299    /// let tracker = DropTracker::<()>::new();
1300    /// tracker.assert_all_alive([(); 0]);
1301    /// ```
1302    pub fn assert_all_alive<Q, Item, Iter>(&self, iter: Iter)
1303    where
1304        K: Borrow<Q>,
1305        Q: Hash + Eq + ?Sized,
1306        Item: Borrow<Q> + fmt::Debug,
1307        Iter: IntoIterator<Item = Item>,
1308    {
1309        match self.all_alive(iter) {
1310            Ok(()) => (),
1311            Err(err) => panic!("{err}"),
1312        }
1313    }
1314
1315    /// Checks that all the given keys point to items that are [dropped](State::Dropped), panics
1316    /// otherwise.
1317    ///
1318    /// `assert_all_dropped(...)` is a shortcut for `all_dropped(...).unwrap()`. See
1319    /// [`all_dropped()`](DropTracker::all_dropped) for more details.
1320    ///
1321    /// # Panics
1322    ///
1323    /// If a key is not tracked, or if it is alive.
1324    ///
1325    /// # Examples
1326    ///
1327    /// ```should_panic
1328    /// # #![allow(unused_variables)]
1329    /// use drop_tracker::DropTracker;
1330    ///
1331    /// let mut tracker = DropTracker::new();
1332    ///
1333    /// let item1 = tracker.track(1);
1334    /// let item2 = tracker.track(2);
1335    /// let item3 = tracker.track(3);
1336    /// let item4 = tracker.track(4);
1337    ///
1338    /// drop(item1);
1339    /// drop(item2);
1340    ///
1341    /// tracker.assert_all_dropped([1, 2]); // succeeds
1342    /// tracker.assert_all_dropped([3, 4]); // panics (items are alive)
1343    /// tracker.assert_all_dropped([5, 6]); // panics (keys are not tracked)
1344    /// ```
1345    ///
1346    /// Passing an empty set of keys succeeds:
1347    ///
1348    /// ```
1349    /// use drop_tracker::DropTracker;
1350    ///
1351    /// let tracker = DropTracker::<()>::new();
1352    /// tracker.assert_all_dropped([(); 0]);
1353    /// ```
1354    pub fn assert_all_dropped<Q, Item, Iter>(&self, iter: Iter)
1355    where
1356        K: Borrow<Q>,
1357        Q: Hash + Eq + ?Sized,
1358        Item: Borrow<Q> + fmt::Debug,
1359        Iter: IntoIterator<Item = Item>,
1360    {
1361        match self.all_dropped(iter) {
1362            Ok(()) => (),
1363            Err(err) => panic!("{err}"),
1364        }
1365    }
1366
1367    /// Checks that all the keys tracked are [alive](State::Alive), panics otherwise.
1368    ///
1369    /// `assert_fully_alive()` is a shortcut for `fully_alive().unwrap()`. See
1370    /// [`fully_alive()`](DropTracker::fully_alive) for more details.
1371    ///
1372    /// # Panics
1373    ///
1374    /// If one or more items were found to have been [dropped](State::Dropped).
1375    ///
1376    /// # Examples
1377    ///
1378    /// Calling `assert_fully_alive()` when all items are alive succeeds:
1379    ///
1380    /// ```
1381    /// # #![allow(unused_variables)]
1382    /// use drop_tracker::DropTracker;
1383    ///
1384    /// let mut tracker = DropTracker::new();
1385    ///
1386    /// let item1 = tracker.track(1);
1387    /// let item2 = tracker.track(2);
1388    ///
1389    /// tracker.assert_fully_alive(); // succeeds
1390    /// ```
1391    ///
1392    /// Calling `assert_fully_alive()` when one or more items are dropped causes a panic:
1393    ///
1394    /// ```should_panic
1395    /// # #![allow(unused_variables)]
1396    /// use drop_tracker::DropTracker;
1397    ///
1398    /// let mut tracker = DropTracker::new();
1399    ///
1400    /// let item1 = tracker.track(1);
1401    /// let item2 = tracker.track(2);
1402    ///
1403    /// drop(item1);
1404    ///
1405    /// tracker.assert_fully_alive(); // panics
1406    /// ```
1407    ///
1408    /// Calling `assert_fully_alive()` when the tracker is empty succeeds:
1409    ///
1410    /// ```
1411    /// use drop_tracker::DropTracker;
1412    ///
1413    /// let tracker = DropTracker::<()>::new();
1414    /// tracker.assert_fully_alive(); // succeeds
1415    /// ```
1416    pub fn assert_fully_alive(&self)
1417    where
1418        K: fmt::Debug,
1419    {
1420        match self.fully_alive() {
1421            Ok(()) => (),
1422            Err(err) => panic!("{err}"),
1423        }
1424    }
1425
1426    /// Checks that all the keys tracked are [dropped](State::Dropped), panics otherwise.
1427    ///
1428    /// `assert_fully_dropped()` is a shortcut for `fully_dropped().unwrap()`. See
1429    /// [`fully_dropped()`](DropTracker::fully_dropped) for more details.
1430    ///
1431    /// # Panics
1432    ///
1433    /// If one or more items were found [alive](State::Alive).
1434    ///
1435    /// # Examples
1436    ///
1437    /// Calling `assert_fully_dropped()` when items are alive causes a panic:
1438    ///
1439    /// ```should_panic
1440    /// # #![allow(unused_variables)]
1441    /// use drop_tracker::DropTracker;
1442    ///
1443    /// let mut tracker = DropTracker::new();
1444    ///
1445    /// let item1 = tracker.track(1);
1446    /// let item2 = tracker.track(2);
1447    ///
1448    /// tracker.assert_fully_dropped(); // panics
1449    /// ```
1450    ///
1451    /// Calling `assert_fully_dropped()` when all items are dropped succeeds:
1452    ///
1453    /// ```
1454    /// use drop_tracker::DropTracker;
1455    ///
1456    /// let mut tracker = DropTracker::new();
1457    ///
1458    /// let item1 = tracker.track(1);
1459    /// let item2 = tracker.track(2);
1460    ///
1461    /// drop(item1);
1462    /// drop(item2);
1463    ///
1464    /// tracker.assert_fully_dropped(); // succeeds
1465    /// ```
1466    ///
1467    /// Calling `assert_fully_dropped()` when the tracker is empty succeeds:
1468    ///
1469    /// ```
1470    /// use drop_tracker::DropTracker;
1471    ///
1472    /// let tracker = DropTracker::<()>::new();
1473    /// tracker.assert_fully_dropped(); // succeeds
1474    /// ```
1475    pub fn assert_fully_dropped(&self)
1476    where
1477        K: fmt::Debug,
1478    {
1479        match self.fully_dropped() {
1480            Ok(()) => (),
1481            Err(err) => panic!("{err}"),
1482        }
1483    }
1484}
1485
1486/// An item that will notify the parent [`DropTracker`] once it gets dropped.
1487///
1488/// `DropItem` instances are created by [`DropTracker::track`], [`DropTracker::track_with_value`],
1489/// and related functions. `DropItem` instances may contain an "underlying value" that affects the
1490/// item behavior when used with standard traits. The underlying value is either:
1491///
1492/// * a clone of `key` when constructing an item using `track(key)` (implicit); or
1493/// * `value` when constructing an item using `track_with_value(key, value)` (explicit).
1494///
1495/// To check whether an item is alive or has been dropped, use [`DropTracker::state`] or see the
1496/// documentation for [`DropTracker`] for alternatives.
1497///
1498/// # Coercing and borrowing
1499///
1500/// `DropItem` instances may be [_coerced_](std::ops::Deref) and [_borrowed_](std::borrow::Borrow)
1501/// as the the underlying value type. This means that, for example, if you create a `DropItem`
1502/// using `track(String::from("abc"))`, you may call all of the `String` methods on that item.
1503///
1504/// `DropItem` also implements the standard traits [`PartialEq`](std::cmp::PartialEq),
1505/// [`Eq`](std::cmp::Eq), [`PartialOrd`](std::cmp::PartialOrd), [`Ord`](std::cmp::Ord) and
1506/// [`Hash`](std::hash::Hash), [`Display`](std::fmt::Display), [`Debug`](std::fmt::Debug) if the
1507/// type of the underlying value implements them.
1508///
1509/// # Cloning
1510///
1511/// `DropItem` does not implement the [`Clone`] trait as it would introduce ambiguity with respect
1512/// to understanding whether the item has been dropped or is still alive when using
1513/// [`DropTracker::state`].
1514///
1515/// # Double drop
1516///
1517/// `DropItem` instances can be dropped twice or more. Doing so will cause a panic, but will not
1518/// cause undefined behavior (unless you're calling drop on an invalid memory location). The panic
1519/// on double drop is an useful feature to detect logic errors in destructors.
1520///
1521/// # Safety
1522///
1523/// Borrowing or performing operations on the underlying value of a `DropItem` is generally safe
1524/// when using safe Rust code. However, `DropItem`s are often used in unsafe code and are used to
1525/// detect potential bugs. In those circumstances, it is possible to trigger undefined behavior.
1526/// In particular, borrowing or performing operations on a `DropItem` while another thread is
1527/// dropping will result in undefined behavior (although it must be noted that this is a bug in the
1528/// caller code and is not something that should happen in safe Rust code).
1529///
1530/// Only [`Drop`](std::ops::Drop) on a `DropItem` is guaranteed to be safe in all circumstances.
1531///
1532/// # Examples
1533///
1534/// ```
1535/// use drop_tracker::DropTracker;
1536///
1537/// let mut tracker = DropTracker::<u32>::new();
1538///
1539/// // Create an item using `123u32` as the key. Implicitly, this also sets its value to `123u32`
1540/// let item = tracker.track(123);
1541///
1542/// // Check that the item is alive
1543/// tracker.state(&123).alive().expect("item should be alive");
1544///
1545/// // Dereference the value of the item
1546/// assert_eq!(*item, 123);
1547/// assert!(!item.is_power_of_two());
1548///
1549/// // Drop the item and check that it really got dropped
1550/// drop(item);
1551/// tracker.state(&123).dropped().expect("item should be dropped");
1552///
1553/// // Create a new item, this time using an explicit `String` value
1554/// let abc_item = tracker.track_with_value(111, String::from("abc"));
1555///
1556/// // Comparison with other items using `String` work using the underlying `String`
1557/// // operations
1558/// assert_eq!(abc_item, tracker.track_with_value(222, String::from("abc")));
1559/// assert_ne!(abc_item, tracker.track_with_value(333, String::from("def")));
1560/// assert!(abc_item < tracker.track_with_value(444, String::from("def")));
1561///
1562/// // Display, debug and hashing also work using the underlying `String` operations
1563/// assert_eq!(format!("{}", abc_item), "abc");
1564/// assert_eq!(format!("{:?}", abc_item), "DropItem { value: \"abc\", state: Alive }");
1565///
1566/// use std::collections::hash_map::DefaultHasher;
1567/// use std::hash::Hash;
1568/// use std::hash::Hasher;
1569/// fn hash<T: Hash + ?Sized>(x: &T) -> u64 {
1570///     let mut hasher = DefaultHasher::new();
1571///     x.hash(&mut hasher);
1572///     hasher.finish()
1573/// }
1574/// assert_eq!(hash(&abc_item), hash(&"abc"));
1575///
1576/// // Methods on `String` can be called transparently on items
1577/// assert_eq!(abc_item.to_ascii_uppercase(), "ABC");
1578/// ```
1579///
1580/// Using hashable items in a set, with an implicit underlying value:
1581///
1582/// ```
1583/// use drop_tracker::DropTracker;
1584/// use std::collections::HashSet;
1585///
1586/// let mut tracker = DropTracker::new();
1587///
1588/// let mut set = HashSet::from([
1589///     tracker.track(1),
1590///     tracker.track(2),
1591///     tracker.track(3),
1592/// ]);
1593///
1594/// set.remove(&3);
1595///
1596/// tracker.state(&1).alive().expect("first item should be alive");
1597/// tracker.state(&2).alive().expect("second item should be alive");
1598/// tracker.state(&3).dropped().expect("third item should be dropped");
1599/// ```
1600///
1601/// Using hashable items in a set, with an explicit underlying value:
1602///
1603/// ```
1604/// use drop_tracker::DropTracker;
1605/// use std::collections::HashSet;
1606///
1607/// let mut tracker = DropTracker::new();
1608///
1609/// let mut set = HashSet::from([
1610///     tracker.track_with_value(1, String::from("first")),
1611///     tracker.track_with_value(2, String::from("second")),
1612///     tracker.track_with_value(3, String::from("third")),
1613/// ]);
1614///
1615/// set.remove("third");
1616///
1617/// tracker.state(&1).alive().expect("first item should be alive");
1618/// tracker.state(&2).alive().expect("second item should be alive");
1619/// tracker.state(&3).dropped().expect("third item should be dropped");
1620/// ```
1621#[must_use = "if you don't use this item, it will get automatically dropped"]
1622pub struct DropItem<V> {
1623    value: MaybeUninit<V>,
1624    state: Option<StateCell>,
1625}
1626
1627impl<V> DropItem<V> {
1628    const fn new(value: V, state: StateCell) -> Self {
1629        Self {
1630            value: MaybeUninit::new(value),
1631            state: Some(state),
1632        }
1633    }
1634}
1635
1636impl<V> Drop for DropItem<V> {
1637    fn drop(&mut self) {
1638        // The use of an Option might seem redundant, but it's actually needed to safely detect and
1639        // report double drops. Without the Option, we would be touching shared memory behind an Rc
1640        // that probably does not exist anymore, causing memory corruption. The Option makes this a
1641        // bit safer (assuming that the DropItem memory has not been moved or altered), and also
1642        // prevents a double drop on the Rc.
1643        match self.state.take() {
1644            Some(mut state) => {
1645                if state.replace(State::Dropped).is_dropped() {
1646                    panic!("item dropped twice");
1647                }
1648                // SAFETY: `state` was `Some(State::Alive)`, which means that `value` has not been
1649                // dropped yet and that `value` is initialized.
1650                unsafe { self.value.assume_init_drop() };
1651            }
1652            None => {
1653                panic!("item dropped twice");
1654            }
1655        }
1656    }
1657}
1658
1659/// Error signaling that an item was expected to have been dropped, but it's [alive](State::Alive).
1660///
1661/// See [`State::dropped`] for more information and examples.
1662#[derive(PartialEq, Eq, Debug)]
1663pub struct AliveError;
1664
1665impl fmt::Display for AliveError {
1666    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1667        fmt::Display::fmt("item is alive", f)
1668    }
1669}
1670
1671impl Error for AliveError {}
1672
1673/// Error signaling that an item was expected to be alive, but it was [dropped](State::Dropped).
1674///
1675/// See [`State::alive`] for more information and examples.
1676#[derive(PartialEq, Eq, Debug)]
1677pub struct DroppedError;
1678
1679impl fmt::Display for DroppedError {
1680    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1681        fmt::Display::fmt("item is dropped", f)
1682    }
1683}
1684
1685impl Error for DroppedError {}
1686
1687/// Error returned when trying to place multiple items with the same key inside the same [`DropTracker`].
1688///
1689/// See [`DropTracker::try_track`] for more information and examples.
1690#[derive(PartialEq, Eq, Debug)]
1691pub struct CollisionError;
1692
1693impl fmt::Display for CollisionError {
1694    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1695        fmt::Display::fmt("another item with the same key is already tracked", f)
1696    }
1697}
1698
1699impl Error for CollisionError {}
1700
1701/// Error returned when failing to query the status of an item with a key that is not known to [`DropTracker`].
1702///
1703/// See [`DropTracker::try_state`] for more information and examples.
1704#[derive(PartialEq, Eq, Debug)]
1705pub struct NotTrackedError;
1706
1707impl fmt::Display for NotTrackedError {
1708    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1709        fmt::Display::fmt("item is not tracked", f)
1710    }
1711}
1712
1713impl Error for NotTrackedError {}
1714
1715/// Error returned when failing to assert that a set of items is all [alive](State::Alive).
1716///
1717/// See [`DropTracker::all_alive`] for more information and examples.
1718#[derive(PartialEq, Eq, Debug)]
1719pub struct NotAllAliveError<K> {
1720    /// Sequence of keys that were expected to be alive, but were dropped.
1721    pub dropped: Vec<K>,
1722    /// Sequence of keys that were expected to be alive, but are not tracked by the
1723    /// [`DropTracker`].
1724    pub untracked: Vec<K>,
1725}
1726
1727impl<K: fmt::Debug> fmt::Display for NotAllAliveError<K> {
1728    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1729        write!(f, "not all items are alive: ")?;
1730        if !self.dropped.is_empty() {
1731            write!(f, "dropped: {:?}", &self.dropped)?;
1732        }
1733        if !self.untracked.is_empty() {
1734            if !self.dropped.is_empty() {
1735                write!(f, ", ")?;
1736            }
1737            write!(f, "not tracked: {:?}", &self.untracked)?;
1738        }
1739        Ok(())
1740    }
1741}
1742
1743impl<K: fmt::Debug> Error for NotAllAliveError<K> {}
1744
1745/// Error returned when failing to assert that a set of items is all [dropped](State::Dropped).
1746///
1747/// See [`DropTracker::all_dropped`] for more information and examples.
1748#[derive(PartialEq, Eq, Debug)]
1749pub struct NotAllDroppedError<K> {
1750    /// Sequence of keys that were expected to be dropped, but are alive.
1751    pub alive: Vec<K>,
1752    /// Sequence of keys that were expected to be dropped, but are not tracked by the
1753    /// [`DropTracker`].
1754    pub untracked: Vec<K>,
1755}
1756
1757impl<K: fmt::Debug> fmt::Display for NotAllDroppedError<K> {
1758    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1759        write!(f, "not all items are dropped: ")?;
1760        if !self.alive.is_empty() {
1761            write!(f, "alive: {:?}", &self.alive)?;
1762        }
1763        if !self.untracked.is_empty() {
1764            if !self.alive.is_empty() {
1765                write!(f, ", ")?;
1766            }
1767            write!(f, "not tracked: {:?}", &self.untracked)?;
1768        }
1769        Ok(())
1770    }
1771}
1772
1773impl<K: fmt::Debug> Error for NotAllDroppedError<K> {}
1774
1775/// Error returned when failing to assert that all tracked items are [dropped](State::Dropped).
1776///
1777/// See [`DropTracker::fully_dropped`] for more information and examples.
1778#[derive(PartialEq, Eq, Debug)]
1779pub struct SomeAliveError<'a, K> {
1780    /// Reference to the first key that was found [alive](State::Alive).
1781    pub alive: &'a K,
1782}
1783
1784impl<'a, K: fmt::Debug> fmt::Display for SomeAliveError<'a, K> {
1785    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1786        write!(f, "item is alive: {:?}", self.alive)
1787    }
1788}
1789
1790impl<'a, K: fmt::Debug> Error for SomeAliveError<'a, K> {}
1791
1792/// Error returned when failing to assert that all tracked items are [alive](State::Alive).
1793///
1794/// See [`DropTracker::fully_alive`] for more information and examples.
1795#[derive(PartialEq, Eq, Debug)]
1796pub struct SomeDroppedError<'a, K> {
1797    /// Reference to the first key that was found to have been be [dropped](State::Dropped).
1798    pub dropped: &'a K,
1799}
1800
1801impl<'a, K: fmt::Debug> fmt::Display for SomeDroppedError<'a, K> {
1802    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1803        write!(f, "item is dropped: {:?}", self.dropped)
1804    }
1805}
1806
1807impl<'a, K: fmt::Debug> Error for SomeDroppedError<'a, K> {}