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
//! Runtime dependency injection.
//!
//! By default, services provided by the `Injector` are not thread-safe. This
//! is because `Rc<T>` is used to hold instances of the services, which is not
//! a thread-safe pointer type. This can be changed by disabling default
//! features and enabling the "arc" feature:
//!
//! ```text
//! runtime_injector = {
//!     version = "*",
//!     default_features = false,
//!     features = ["arc"]
//! }
//! ```
//!
//! # Runtime dependency injection (rather than compile-time)
//!
//! Runtime dependency injection allows for custom configuration of services
//! during runtime rather than needing to determine what services are used at
//! compile time. This means you can read a config when your application
//! starts, determine what implementations you want to use for your interfaces,
//! and assign those at runtime. This is also 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 be preferred instead.
//!
//! # Interfaces
//!
//! Proper inversion of control requires that each service requests its
//! dependencies without actually caring how those dependencies are
//! implemented. For instance, suppose you are working with a database. A
//! service which depends on interacting with that database may request a
//! dependency that can interact with that database without needing to know the
//! concrete type being used. This is done using dynamic dispatch to allow the
//! concrete type to be determined at runtime (rather than using generics to
//! determine the implementations at compile time).
//!
//! # 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:
//!
//! - Singleton: A service is created only the first time it is requested and
//!   that single instance is reused for each future request.
//! - Transient: A service is created each time it is requested.
//! - Constant: Used for services that are not created using a factory function
//!   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 the
//! `TypedProvider` trait.
//!
//! # Example
//!
//! ```
//! use runtime_injector::{interface, Injector, Svc, IntoSingleton};
//! 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!(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();
//!     builder.provide(UserService::new.singleton());
//!     builder.provide(SqlDataService::default.singleton());
//!     builder.provide(MockDataService::default.singleton());
//!
//!     // Note that we can register closures as providers as well
//!     builder.provide((|_: Svc<dyn DataService>| "Hello, world!").singleton());
//!     builder.provide((|_: Option<Svc<i32>>| 120.9).singleton());
//!     
//!     // Let's choose to use the MockDataService as our data service
//!     builder.implement::<dyn DataService, MockDataService>();
//!     
//!     // Now that we've registered all our providers and implementations, we
//!     // can start relying on our container to create our services for us!
//!     let mut injector = builder.build();
//!     let user_service: Svc<UserService> = injector.get()?;
//!     let _user = user_service.get_user("john");
//!     
//!     Ok(())
//! }
//! ```

#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![allow(
    clippy::module_name_repetitions,
    clippy::missing_errors_doc,
    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 builder;
mod injector;
mod request;
mod services;

pub use builder::*;
pub use injector::*;
pub use request::*;
pub use services::*;

#[cfg(test)]
mod tests;