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}