1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
//! # aerosol
//! Simple but powerful dependency injection for Rust.
//!
//! This crate provides the `Aero` type, which stores dependencies (called resources) keyed by their
//! type. Resources can be constructed eagerly at application startup, or on-demand when they are
//! first needed. Resources can access and/or initialize other resources on creation.
//!
//! The crate will detect dependency cycles (if constructing resource A requires resource B which
//! itself requires resource A) and will panic rather than stack overflow in that case.
//!
//! The `Aero` type has an optional type parameter to make certain resources *required*. When
//! a resource is required it can be accessed infallibly. The `Aero![...]` macro exists to
//! easily name an `Aero` with a specific set of required resources.
//!
//! Cloning or type casting an `Aero` type is cheap (equivalent to cloning an `Arc`).
//!
//! ## Optional features
//!
//! ### `async`
//!
//! Allows resources to be constructed asynchrously, and provides a corresponding
//! `AsyncConstructibleResource` trait.
//!
//! ### `axum`
//!
//! Provides integrations with the `axum` web framework. See the `axum` module
//! for more information.
//!
//! ### `aide`
//!
//! Provides integrations with the `aide` library. Implements the `OperationInput` trait
//! for `Dep<T>` and `Obtain<T>` types, allowing them to be used in `aide`-generated OpenAPI
//! documentation.
//!
//! ## Example usage
//!
//! ```rust
//! use std::{sync::Arc, any::Any};
//!
//! # struct PostmarkClient;
//! # #[derive(Clone)]
//! # struct ConnectionPool;
//! # #[derive(Clone)]
//! # struct MessageQueue;
//! # #[derive(Clone)]
//! # struct MagicNumber(i32);
//! # trait EmailSender: Send + Sync { fn send(&self) {} }
//! # impl EmailSender for PostmarkClient {}
//! # impl PostmarkClient { fn new() -> anyhow::Result<Self> { Ok(Self) }}
//! use aerosol::{Aero, Constructible};
//!
//! // Here, we can list all the things we want to guarantee are in
//! // our app state. This is entirely optional, we could also just
//! // use the `Aero` type with default arguments and check that
//! // resources are present at runtime.
//! type AppState = Aero![
//! Arc<PostmarkClient>,
//! Arc<dyn EmailSender>,
//! ConnectionPool,
//! MessageQueue,
//! MagicNumber,
//! ];
//!
//! fn main() {
//! let app_state: AppState = Aero::new()
//! // Directly add a resource which doesn't implement `Constructible`.
//! .with(MagicNumber(42))
//! // Construct an `Arc<PostmarkClient>` resource in the AppState
//! .with_constructed::<Arc<PostmarkClient>>()
//! // Check that an implementation of `EmailSender` was added as a result
//! .assert::<Arc<dyn EmailSender>>()
//! // Automatically construct anything else necessary for our AppState
//! // (in this case, `ConnectionPool` and `MessageQueue`)
//! .construct_remaining();
//!
//! // Add an extra resource
//! app_state.insert("Hello, world");
//!
//! run(app_state);
//! }
//!
//! fn run(app_state: AppState) {
//! // The `get()` method is infallible because the `Arc<dyn EmailSender>` was
//! // explicitly listed when defining our `AppState`.
//! let email_sender: Arc<dyn EmailSender> = app_state.get();
//! email_sender.send(/* email */);
//!
//! // We have to use `try_get()` here because a `&str` is not guaranteed to
//! // exist on our `AppState`.
//! let hello_message: &str = app_state.try_get().unwrap();
//! println!("{hello_message}");
//!
//! // ... more application logic
//! }
//!
//! // The `Constructible` trait can be implemented to allow resources to be automatically
//! // constructed.
//! impl Constructible for PostmarkClient {
//! type Error = anyhow::Error;
//!
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
//! PostmarkClient::new(/* initialize using environment variables */)
//! }
//!
//! fn after_construction(this: &(dyn Any + Send + Sync), aero: &Aero) -> Result<(), Self::Error> {
//! // We can use this to automatically populate extra resources on the context.
//! // For example, in this case we can make it so that if an `Arc<PostmarkClient>` gets
//! // constructed, we also provide `Arc<dyn EmailSender>`.
//! if let Some(arc) = this.downcast_ref::<Arc<Self>>() {
//! aero.insert(arc.clone() as Arc<dyn EmailSender>)
//! }
//! Ok(())
//! }
//! }
//!
//! impl Constructible for ConnectionPool {
//! type Error = anyhow::Error;
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
//! // ...
//! # Ok(ConnectionPool)
//! }
//! }
//!
//! impl Constructible for MessageQueue {
//! type Error = anyhow::Error;
//! fn construct(aero: &Aero) -> Result<Self, Self::Error> {
//! // ...
//! # Ok(MessageQueue)
//! }
//! }
//! ```
//!
//! ## Implementation details
//!
//! The `Aero` type manages shared ownership of a map from resource types to "slots".
//! For a given resource type, the corresponding "slot" can be in one of three state:
//! 1) Absent.
//! No instance of this resource is present in the map.
//! 2) Present.
//! An instance of this resource exists in the map and can be accessed immediately.
//! 3) Under construction.
//! An instance of this resource is currently under construction, and may be accessed
//! once construction has finished.
//! The slot maintains a list of threads or tasks waiting for this resource to be
//! constructed, and will wake them when the resource becomes available.
//!
//! Resources can be constructed synchronously, or (when the feature is enabled) asynchronously.
//!
//! If a resource is accessed whilst under construction, the caller will wait for construction
//! to complete. The caller determines whether the wait occurs synchronously or asynchronously,
//! depending on whether `obtain()` or `obtain_async()` is used.
//!
//! It is possible (and allowed) for a thread to synchronously wait on a resource being constructed
//! asynchronously in a task, or for a task to asynchronously wait on a resource being synchronously
//! constructed on a thread.
//!
pub use frunk;
pub use ;
pub use Aero;
pub use ;
pub use ;