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
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
//! # Runtime dependency injection.
//!
//! By default, services provided by the [`Injector`] use thread-safe pointers.
//! This is because [`Arc<T>`](std::sync::Arc) is used to hold instances of the
//! services. This can be changed to [`Rc<T>`](std::rc::Rc) by disabling
//! default features and enabling the "rc" feature:
//!
//! ```text
//! [dependencies.runtime_injector]
//! version = "*" # Replace with the version you want to use
//! default-features = false
//! features = ["rc"]
//! ```
//!
//! ## Getting started
//!
//! If you are unfamiliar with dependency injection, then you may want to check
//! about how a container can help
//! [simplify your application][ioc]. Otherwise,
//! check out the [getting started guide][getting-started]
//!
//! [ioc]: crate::docs::inversion_of_control
//! [getting-started]: crate::docs::getting_started
//!
//! ## Dependency injection at runtime (rather than compile-time)
//!
//! Runtime dependency injection allows for advanced configuration of services
//! during runtime rather than needing to decide what services your application
//! will use at compile time. This means you can read a config when your
//! application starts, decide what implementations you want to use for your
//! interfaces, and assign those at runtime. This is also slightly slower than
//! compile-time dependency injection, so if pointer indirection, dynamic
//! dispatch, or heap allocations are a concern, then a compile-time dependency
//! injection library might work better instead. However, in asynchronous,
//! I/O-based applications like a web server, the additional overhead is
//! probably insignificant compared to the additional flexibility you get with
//! runtime_injector.
//!
//! ## Interfaces
//!
//! Using interfaces allows you to write your services without worrying about
//! how its dependencies are implemented. You can think of them like generic
//! type parameters for your service, except rather than needing to add a new
//! type parameter, you use a service pointer to the interface for your
//! dependency. This makes your code easier to read and faster to write, and
//! keeps your services decoupled from their dependencies and dependents.
//!
//! Interfaces are implemented as trait objects in runtime_injector. For
//! instance, you may define a trait `UserDatabase` and implement it for
//! several different types. [`Svc<dyn UserDatabase>`](crate::Svc<T>) is a
//! reference-counted service pointer to an implementation of your trait.
//! Similarly, `dyn UserDatabase` is your interface. You can read more about
//! how interfaces work and how they're created in the
//! [type-level docs](crate::Interface).
//!
//! ## Service lifetimes
//!
//! Lifetimes of services created by the [`Injector`] are controlled by the
//! [`Provider`] used to construct those lifetimes. Currently, there are three
//! built-in service provider types:
//!
//! - **[Transient](crate::TransientProvider):** A service is created each time
//!   it is requested. This will never return the same instance of a service
//!   more than once.
//! - **[Singleton](crate::SingletonProvider):** A service is created only the
//!   first time it is requested, then that single instance is reused for each
//!   future request.
//! - **[Constant](crate::ConstantProvider):** Used for services that are not
//!   created using a service factory and instead can have their instance
//!   provided to the container directly. This behaves similar to singleton in
//!   that the same instance is provided each time the service is requested.
//!
//! Custom service providers can also be created by implementing either the
//! [`TypedProvider`] or [`Provider`] trait.
//!
//! ## Fallible service factories
//!
//! Not all types can always be successfully created. Sometimes, creating an
//! instance of a service might fail. Rather than panicking on error, it's
//! possible to instead return a [`Result<T, E>`] from your constructors and
//! inject the result as a [`Svc<T>`]. Read more in the
//! [docs for `IntoFallible`](crate::IntoFallible).
//!
//! ## Owned service pointers
//!
//! In general, providers need to be able to provide their services via
//! reference-counted service pointers, or [`Svc<T>`]. The issue with this is
//! that you cannot get mutable or owned access to the contents of those
//! pointers since they are shared pointers. As a result, you may need to clone
//! some dependencies in your constructors if you want to be able to own them.
//!
//! If your dependency is a transient service, then it might make more sense
//! to inject it as a [`Box<T>`] than clone it from a reference-counted service
//! pointer. In these cases, you can request a [`Box<T>`] directly from the
//! injector and avoid needing to clone your dependency entirely!
//!
//! ## Custom target-specific arguments
//!
//! Sometimes it's useful to be able to pass a specific value into your
//! services. For example, if you're writing a database service and you need a
//! connection string, you could define a new `ConnectionString` struct as a
//! newtype for [`String`], but that would be a bit excessive for passing in a
//! single value. If you had several arguments you needed to pass in this way,
//! then that would mean you would need a new type for each one.
//!
//! Rather than creating a bunch of newtypes, you can use [`Arg<T>`] to pass in
//! pre-defined values directly to your services. For example, you can use
//! `Arg<String>` to pass in your connection string, plus you can use
//! `Arg<usize>` to set the max size of your connection pool, and another
//! `Arg<String>` in your logging service to set your logging format without
//! needing to worry about accidentally using your connection string as your
//! logging format!
//!
//! ## Example
//!
//! ```
//! use runtime_injector::{
//!     define_module, Module, interface, Injector, Svc, IntoSingleton,
//!     TypedProvider, IntoTransient, constant
//! };
//! use std::error::Error;
//!
//! // Some type that represents a user
//! struct User;
//!
//! // This is our interface. In practice, multiple structs can implement this
//! // trait, and we don't care what the concrete type is most of the time in
//! // our other services as long as it implements this trait. Because of this,
//! // we're going to use dynamic dispatch later so that we can determine the
//! // concrete type at runtime (vs. generics, which are determined instead at
//! // compile time).
//! //
//! // The `Send` and `Sync` supertrait requirements are only necessary when
//! // compiling with the "arc" feature to allow for service pointer
//! // downcasting.
//! trait DataService: Send + Sync {
//!     fn get_user(&self, user_id: &str) -> Option<User>;
//! }
//!
//! // We can use a data service which connects to a SQL database.
//! #[derive(Default)]
//! struct SqlDataService;
//! impl DataService for SqlDataService {
//!     fn get_user(&self, _user_id: &str) -> Option<User> { todo!() }
//! }
//!
//! // ... Or we can mock out the data service entirely!
//! #[derive(Default)]
//! struct MockDataService;
//! impl DataService for MockDataService {
//!     fn get_user(&self, _user_id: &str) -> Option<User> { Some(User) }
//! }
//!
//! // Specify which types implement the DataService interface. This does not
//! // determine the actual implementation used. It only registers the types as
//! // possible implementations of the DataService interface.
//! interface!(dyn DataService = [SqlDataService, MockDataService]);
//!
//! // Here's another service our application uses. This service depends on our
//! // data service, however it doesn't care how that service is actually
//! // implemented as long as it works. Because of that, we're using dynamic
//! // dispatch to allow the implementation to be determined at runtime.
//! struct UserService {
//!     data_service: Svc<dyn DataService>,
//! }
//!
//! impl UserService {
//!     // This is just a normal constructor. The only requirement is that each
//!     // parameter is a valid injectable dependency.
//!     pub fn new(data_service: Svc<dyn DataService>) -> Self {
//!         UserService { data_service }
//!     }
//!
//!     pub fn get_user(&self, user_id: &str) -> Option<User> {
//!         // UserService doesn't care how the user is actually retrieved
//!         self.data_service.get_user(user_id)
//!     }
//! }
//!
//! fn main() -> Result<(), Box<dyn Error>> {
//!     // This is where we register our services. Each call to `.provide` adds
//!     // a new service provider to our container, however nothing is actually
//!     // created until it is requested. This means we can add providers for
//!     // types we aren't actually going to use without worrying about
//!     // constructing instances of those types that we aren't actually using.
//!     let mut builder = Injector::builder();
//!
//!     // We can manually add providers to our builder
//!     builder.provide(UserService::new.singleton());
//!
//!     struct Foo(Svc<dyn DataService>);
//!     
//!     // Alternatively, modules can be used to group providers and
//!     // configurations together, and can be defined via the
//!     // define_module! macro
//!     let module = define_module! {
//!         services = [
//!             // Simple tuple structs can be registered as services directly without
//!             // defining any additional constructors
//!             Foo.singleton(),
//!             
//!             // Note that we can register closures as providers as well
//!             (|_: Svc<dyn DataService>| "Hello, world!").singleton(),
//!             (|_: Option<Svc<i32>>| 120.9).transient(),
//!
//!             // Since we know our dependency is transient, we can request an
//!             // owned pointer to it rather than a reference-counted pointer
//!             (|value: Box<f32>| format!("{}", value)).transient(),
//!
//!             // We can also provide constant values directly to our services
//!             constant(8usize),
//!         ],
//!         interfaces = {
//!             // Let's choose to use the MockDataService as our data service
//!             dyn DataService = [MockDataService::default.singleton()],
//!         },
//!     };
//!
//!     // You can easily add a module to your builder
//!     builder.add_module(module);
//!
//!     // Now that we've registered all our providers and implementations, we
//!     // can start relying on our container to create our services for us!
//!     let injector = builder.build();
//!     let user_service: Svc<UserService> = injector.get()?;
//!     let _user = user_service.get_user("john");
//!     
//!     Ok(())
//! }
//! ```

#![forbid(unsafe_code)]
#![deny(clippy::all, clippy::pedantic)]
#![warn(missing_docs)]
#![allow(
    clippy::module_name_repetitions,
    clippy::missing_errors_doc,
    clippy::doc_markdown,
    clippy::needless_doctest_main,
    clippy::needless_pass_by_value
)]

#[cfg(not(any(feature = "arc", feature = "rc")))]
compile_error!(
    "Either the 'arc' or 'rc' feature must be enabled (but not both)."
);

#[cfg(all(feature = "arc", feature = "rc"))]
compile_error!(
    "The 'arc' and 'rc' features are mutually exclusive and cannot be enabled together."
);

mod any;
mod builder;
mod injector;
mod iter;
mod module;
mod requests;
mod services;

pub use any::*;
pub use builder::*;
pub use injector::*;
pub use iter::*;
pub use module::*;
pub use requests::*;
pub use services::*;

pub mod docs;

#[cfg(test)]
mod tests;