inject/
lib.rs

1//! Library for dependency injection
2//!
3//! Inject implements macros for dependency resolution, attempting to promote the
4//! [Inversion of control](https://en.wikipedia.org/wiki/Inversion_of_control) (IoC) principle.
5//!
6//! # Quick Start
7//!
8//! To get you started quickly, there are 3 procedural macros and one attribute macro to keep track of:
9//! [`container!`](macro.container.html), [`get!`](macro.get.html), [`call!`](macro.call.html) and
10//! [`#[inject]`](macro.inject.html).
11//!
12//! [`container!`](macro.container.html) constructs a new container with [providers](provider/index.html).
13//!
14//! [`get!`](macro.get.html) resolves a type using a container.
15//!
16//! [`call!`](macro.call.html) invokes a function using a container.
17//!
18//! [`#[inject]`](attr.inject.html) generates code to enable the above two macros.
19//!
20//! # Example
21//!
22//! ```
23//! use ::inject::*;
24//!
25//! struct Connection(isize);
26//!
27//! impl Connection {
28//!     #[inject]
29//!     fn new(foo: isize) -> Self {
30//!         Self(foo)
31//!     }
32//! }
33//!
34//! struct Instance(isize);
35//!
36//! impl Instance {
37//!     #[inject]
38//!     fn new(conn: &Connection) -> Self {
39//!         Self(conn.0)
40//!     }
41//! }
42//! let conn = Box::new(Connection(2));
43//! let container = container![
44//!     ref conn
45//! ];
46//!
47//! let instance = get!(&container, Instance).unwrap();
48//!
49//! assert_eq!(instance.0, 2)
50//!
51//! ```
52//!
53//! The container resolves the dependencies of the `Instance` struct, using the installed provider to
54//! resolve the `&Connection` dependency.
55
56use std::any::Any;
57use std::any::TypeId;
58use std::collections::HashMap;
59use std::sync::Arc;
60
61/// Call a function with dependency resolution for its arguments
62///
63/// `call!(..) accepts 2-3 arguments.
64/// 1. The first argument can be any expression, and should return a
65/// reference to a [`Container`](struct.Container.html) instance.
66/// 2. The second argument should be the name of a function that has been annotated using the
67/// #[inject] attribute.
68/// 3. Optionally, a sequence of keyword-value-arguments (kwargs) can be supplied on the form
69/// `kwargs = {arg1: expr1, arg2: expr2}`, for a method with arguments
70///
71/// # Examples
72///
73/// ```
74/// use ::inject::*;
75///
76/// // A function that can be called with call!(..)
77/// #[inject]
78/// fn foo(a: isize) -> isize {
79///     a + 1
80/// }
81///
82/// // An empty container.
83/// let container  = container![];
84///
85/// // call "foo", argument(s) resolved using injection.
86/// // This time, it will be resolved to 0 (default).
87/// let result = call!(&container, foo).unwrap();
88///
89/// assert_eq!(result, 1);
90///
91/// // call "foo", this time using the provided kwarg "a = 2"
92/// let result = call!(&container, foo, kwargs = { a: 2 }).unwrap();
93///
94/// assert_eq!(result, 3);
95/// ```
96///
97///
98/// Kwargs are parsed recursively and can be supplied in any order. They take precedence over any
99/// installed provider for the invoking of the function.
100///
101/// ```
102/// use ::inject::*;
103///
104/// // A struct which is allowed to be constructed
105/// // with a provided "isize" type.
106/// struct Injectable(isize);
107///
108/// impl Injectable {
109///     #[inject]
110///     fn new(a: isize) -> Self {
111///         Self(a)
112///     }
113/// }
114///
115/// #[inject]
116/// fn callable(injectable: Injectable) {
117///     println!("{} + {} = {}", injectable.0, 2, injectable.0 + 2);
118/// }
119///
120/// let mut container = container![];
121///
122/// // prints 0 + 2 = 2
123/// call!(&container, callable);
124///
125/// container.install(|container: &Container| Ok(Injectable::new(3)));
126///
127/// // prints 3 + 2 = 5
128/// call!(&container, callable);
129///
130/// // prints 1 + 2 = 3
131/// call!(&container, callable, kwargs = { injectable: Injectable::new(1) });
132///
133/// ```
134///
135/// Under the hood, if an arg is not provided a corresponding kwarg, the
136/// [`get!`](macro.get.html) macro is used to evaluate the argument.
137///
138pub use inject_macro::call;
139
140/// Create a container with providers
141///
142/// `container![..]` accepts any number of arguments, each which is expected to implement one of the
143/// [provider traits](provider.mod.html)
144///
145pub use inject_macro::container;
146
147/// Resolve a dependency from a container
148///
149/// `get!(..)` accepts 2-3 arguments.
150/// 1. The first argument can be any expression, and should return a
151/// reference to a [`Container`](struct.Container.html) instance.
152/// 2. The second argument should be
153/// a type which we want to resolve, optionally prepended by an '`&`' to indicate that we
154/// want a reference.
155/// 3. Lastly, the `create: (true|false)` key-value can be supplied to indicate
156/// that we only want to use a `Provider` for the type, NOT the associated `inject` method.
157///
158/// # Example
159///
160/// ```
161/// use ::inject::*;
162///
163/// // Derive default for brevity, see #[inject] for more intricate usages.
164/// #[derive(Default)]
165/// struct Foo;
166///
167/// let container = Container::new();
168///
169/// // 1. Attempt to resolve a reference
170/// let result = get!(&container, &Foo);
171///
172/// // Fails because no reference-provider is installed into "container".
173/// assert!(result.is_err());
174///
175/// // 2. Attempt to resolve a value
176/// let result = get!(&container, Foo);
177///
178/// // Succeeds because the type could be resolved with injection using Foo::inject(&container).
179/// assert!(result.is_ok());
180///
181/// // 3. Attempt to resolve a value, but ONLY using a Provider
182/// let result = get!(&container, Foo, create: false);
183///
184/// // Fails because no value-provider is installed into "container".
185/// assert!(result.is_err());
186///
187/// ```
188///
189/// The `create: (true|false)` key-value only holds meaning for value types. New references cannot be
190/// created by the macro, as their corresponding instance is dropped on return.
191///
192pub use inject_macro::get;
193
194/// Generate functionality for a function/constructor to be injectable
195///
196/// #[inject] accepts two positions: in a "free" function, or a struct impl method that returns `Self`.
197///
198/// # Examples
199///
200/// When in struct impl position, a new associated method `inject` is generated, in which
201/// the `get!` macro is invoked for each argument.
202/// ```
203/// use ::inject::*;
204///
205/// #[derive(Debug, PartialEq)]
206/// struct A(String);
207///
208/// impl A {
209///     #[inject]
210///     fn new(string: String) -> Self {
211///         Self(string)
212///     }
213/// }
214///
215/// let container = Container::new();
216/// assert_eq!(A::new("".into()), A::inject(&container).unwrap())
217///
218/// ```
219///
220/// When in free function position, a set of macro_rules macros are generated (and hidden), which
221/// enables injection and kwarg-style resolution of the function arguments.
222///
223/// ```
224/// use ::inject::*;
225///
226/// #[inject]
227/// fn injectable(a: usize, b: usize) -> usize { a + b }
228///
229/// // A container with a value provider for "usize"
230/// let container = container![
231///     |container: &Container| Ok(2usize),
232/// ];
233///
234/// // Call the function, letting injection resolve the values
235/// let result = call!(&container, injectable).unwrap();
236/// assert_eq!(result, 4);
237///
238/// // Use kwargs to provide a value for one of the args of the function
239/// // By using macros generated by the #[inject] attribute.
240/// let result = call!(&container, injectable, kwargs = { b: 12 }).unwrap();
241/// assert_eq!(result, 14);
242///
243/// ```
244pub use inject_macro::inject;
245
246pub use error::InjectError;
247pub use provider::{Provider, RefProvider};
248
249pub use crate::inject::{Inject, InjectExt};
250
251pub mod error;
252pub mod inject;
253pub mod module;
254pub mod provider;
255pub mod providers;
256
257/// Contains providers for resolvable types.
258///
259/// The macro `container!` simplifies the construction of `Container`s calling
260/// [`container.install(..)`](struct.Container.html#method.install) and
261/// [`container.install_ref(..)`](struct.Container.html#method.install_ref) (if `ref` keyword is supplied)
262/// on the macro arguments provided.
263///
264/// To resolve a dependecy by using the installed providers, methods
265/// [`container.get()`](struct.Container.html#method.get) and
266/// [`container.get_ref()`](struct.Container.html#method.get_ref) are called for values and references,
267/// respectively.
268///
269///
270/// # Example
271///
272/// ```
273/// use ::inject::*;
274///
275/// let reference_provider = Box::new(5usize);
276/// let container = container![
277///     |container: &Container| Ok(2i32),
278///     ref reference_provider,
279/// ];
280///
281/// assert_eq!(5usize, *container.get_ref().unwrap());
282/// assert_eq!(2i32, container.get().unwrap());
283///
284/// ```
285#[derive(Debug, Default)]
286pub struct Container {
287    providers: HashMap<TypeId, Arc<dyn Any>>,
288}
289
290impl Container {
291    /// Create a new `Container`, used to store implementors of
292    /// [`Provider`](provider/trait.Provider.html)s and [`RefProvider`](provider/trait.RefProvider.html)s.
293    pub fn new() -> Container {
294        Self::default()
295    }
296
297    /// Install a [`Provider`](provider/trait.Provider.html) into this `Container`
298    pub fn install<T: Inject, P: 'static + Provider<ProvidedType = T>>(&mut self, provider: P) {
299        self.providers
300            .insert(provider.id(), Arc::new(Self::box_provider(provider)));
301    }
302
303    /// Install a [`RefProvider`](provider/trait.RefProvider.html) into this `Container`
304    pub fn install_ref<T: Inject, P: 'static + RefProvider<ProvidedRef = T>>(
305        &mut self,
306        provider: P,
307    ) {
308        self.providers
309            .insert(provider.id(), Arc::new(Self::box_ref_provider(provider)));
310    }
311
312    /// Resolve a value-type from the installed [`Provider`](provider/trait.Provider.html)s.
313    pub fn get<T: Inject>(&self) -> Result<T, InjectError> {
314        let provider = self
315            .providers
316            .get(&inject::id::<T>())
317            .ok_or_else(|| InjectError::MissingProvider)?
318            .downcast_ref::<Box<dyn Provider<ProvidedType = T>>>()
319            .ok_or_else(|| InjectError::FailedCast)?;
320        provider.provide(self)
321    }
322
323    /// Resolve a reference-type from the installed [`RefProvider`](provider/trait.RefProvider.html)s.
324    pub fn get_ref<T: 'static>(&self) -> Result<&T, InjectError> {
325        let provider = self
326            .providers
327            .get(&inject::id::<&T>())
328            .ok_or_else(|| InjectError::MissingProvider)?
329            .downcast_ref::<Box<dyn RefProvider<ProvidedRef = T>>>()
330            .ok_or_else(|| InjectError::FailedCast)?;
331        provider.provide(self)
332    }
333
334    fn box_provider<T: 'static, P: 'static + Provider<ProvidedType = T>>(
335        provider: P,
336    ) -> Box<dyn Provider<ProvidedType = T>> {
337        Box::new(provider)
338    }
339
340    fn box_ref_provider<T: 'static, P: 'static + RefProvider<ProvidedRef = T>>(
341        provider: P,
342    ) -> Box<dyn RefProvider<ProvidedRef = T>> {
343        Box::new(provider)
344    }
345}