async_dropper/
lib.rs

1//! `async_dropper` provides two ad-hoc implementations of asynchronous `drop` behavior (`AsyncDrop`).
2//!
3//! - `async_dropper::AsyncDropper` is a wrapper struct (suggested on [StackOverflow by paholg](https://stackoverflow.com/a/75584109))
4//! - `async_dropper::AsyncDrop` is a `derive` macro which generates code that enables async drop functionality to run during a regular `drop`. That code requires `T: Default + PartialEq + Eq`.
5//!
6//! Here is a quick example of the shorter `async_dropper::AsyncDropper`:
7//!
8//! ```ignore
9//! // NOTE: you must have the 'simple' feature enabled to use this code!
10//!
11//! /// This object will be async-dropped (which must be wrapped in AsyncDropper)
12//! #[derive(Default)]
13//! struct AsyncThing(String);
14//!
15//! #[async_trait]
16//! impl AsyncDrop for AsyncThing {
17//!     async fn async_drop(&mut self) {
18//!         tokio::time::sleep(Duration::from_secs(2)).await; // fake work
19//!         Ok(())
20//!     }
21//! }
22//!
23//! #[your_async_runtime_of_choice::main] // i.e. tokio::main or async_std::main
24//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
25//!     drop(AsyncDropper::new(AsyncThing(String::from("test"))));
26//!     Ok(())
27//! }
28//! ```
29//!
30//! Note that `AsyncThing` must be wrapped in `async_dropper::AsyncDropper`.
31//!
32//! For `async_dropper::AsyncDrop`, the simplest example looks like this:
33//!
34//! ```ignore
35//! // NOTE: you must have the 'derive' feature enabled to use this code!
36//!
37//! use async_dropper::AsyncDrop;
38//!
39//! // Your struct (named field structs and tuple structs both work)
40//! #[derive(Debug, Default, PartialEq, Eq, AsyncDrop)]
41//! struct AsyncThing(String);
42//!
43//! /// How it drops, asynchrounously
44//! #[async_trait]
45//! impl AsyncDrop for AsyncThing {
46//!     async fn async_drop(&mut self) -> Result<(), AsyncDropError> {
47//!         tokio::time::sleep(Duration::from_secs(2)).await; // fake work
48//!         Ok(())
49//!     }
50//! }
51//!
52//! #[your_async_runtime_of_choice::main] // i.e. tokio::main or async_std::main
53//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
54//!     drop(AsyncThing(String::from("test")));
55//!     Ok(())
56//! }
57//! ```
58//!
59//! The `async_dropper::AsyncDrop` is interesting because it attempts to automatically (if somewhat painfully) determine
60//! whether an object should have the defined asynchronous drop behavior performed by checking whether it is equal
61//! to `Self::default()`.
62//!
63//! **Said differently, async drop behavior is skipped if an instance of `T` is exactly equal to a `T::default()`**.
64//!
65//! For convenience, a `reset(&mut self)` function that sets any T to T::default() is automatically derived, but it can be overriden via `AsyncDrop#reset()`.
66//!
67//! If `T` is *not* exactly equal to `T::default()`, the assumption is that `T` must still be holding things that require asynchronous dropping behavior, and as such that behavior will be performed.
68//!
69//! **If `reset(&mut self)` does not return `T` to a state where it is equal to `T::default()`, `drop` will panic**
70
71/// Re-export #[derive(AsyncDrop)]
72#[cfg(feature = "derive")]
73extern crate async_dropper_derive;
74#[cfg(feature = "derive")]
75pub use async_dropper_derive::AsyncDrop;
76
77#[cfg(feature = "simple")]
78pub use async_dropper_simple::{AsyncDrop, AsyncDropper};
79
80#[cfg(all(feature = "simple", feature = "derive"))]
81compile_error!("both 'derive' and 'simple' features cannot be enabled at the same time");
82
83#[cfg(all(not(feature = "simple"), not(feature = "derive")))]
84compile_error!("either the 'derive' feature or the 'simple' feature must be enabled");
85
86#[derive(Debug)]
87pub enum AsyncDropError {
88    UnexpectedError(Box<dyn std::error::Error>),
89    Timeout,
90}
91
92/// What to do when a drop fails
93#[derive(Debug, PartialEq, Eq)]
94pub enum DropFailAction {
95    // Ignore the failed drop
96    Continue,
97    // Elevate the drop failure to a full on panic
98    Panic,
99}
100
101/// Types that can reset themselves to T::default()
102pub trait ResetDefault {
103    fn reset_to_default(&mut self);
104}
105
106/// The operative trait that enables AsyncDrop functionality.
107/// Normally, implementing only async_drop(&mut self) and reset(&mut self) is necessary.
108#[async_trait::async_trait]
109#[cfg(feature = "derive")]
110pub trait AsyncDrop: Default + PartialEq + Eq + ResetDefault {
111    /// Operative drop that does async operations, returning
112    async fn async_drop(&mut self) -> Result<(), AsyncDropError> {
113        Ok(())
114    }
115
116    /// A way to reset the object (set all it's internal members to their default).
117    /// This method is used after a successful AsyncDrop, to ensure that future drops do not
118    /// perform async_drop behavior twice.
119    fn reset(&mut self) {
120        self.reset_to_default();
121    }
122
123    /// Timeout for drop operation, meant to be overriden if needed
124    fn drop_timeout(&self) -> std::time::Duration {
125        std::time::Duration::from_secs(3)
126    }
127
128    /// What to do what a drop fails
129    fn drop_fail_action(&self) -> DropFailAction {
130        DropFailAction::Continue
131    }
132}