├── .github └── workflows │ └── ci.yml ├── .gitignore ├── .vscode └── settings.json ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── crates ├── runtime_injector │ ├── Cargo.toml │ ├── README.md │ └── src │ │ ├── any.rs │ │ ├── builder.rs │ │ ├── docs.rs │ │ ├── docs │ │ ├── getting_started.rs │ │ └── inversion_of_control.rs │ │ ├── injector.rs │ │ ├── iter.rs │ │ ├── lib.rs │ │ ├── module.rs │ │ ├── requests.rs │ │ ├── requests │ │ ├── arg.rs │ │ ├── factory.rs │ │ ├── info.rs │ │ ├── parameter.rs │ │ └── request.rs │ │ ├── services.rs │ │ ├── services │ │ ├── conditional.rs │ │ ├── constant.rs │ │ ├── fallible.rs │ │ ├── func.rs │ │ ├── interface.rs │ │ ├── providers.rs │ │ ├── service.rs │ │ ├── singleton.rs │ │ └── transient.rs │ │ └── tests.rs └── runtime_injector_actix │ ├── Cargo.toml │ ├── README.md │ ├── examples │ └── query_string_auth.rs │ └── src │ ├── lib.rs │ └── service.rs └── rustfmt.toml /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | fmt: 14 | name: Check formatting 15 | runs-on: ubuntu-latest 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v2 19 | - name: Setup toolchain 20 | uses: dtolnay/rust-toolchain@stable 21 | with: 22 | toolchain: nightly 23 | components: rustfmt 24 | - name: Check formatting 25 | run: >- 26 | cargo fmt 27 | -- 28 | --check 29 | 30 | test: 31 | name: Rust ${{ matrix.rust_version }} (${{ matrix.package }} - ${{ matrix.features }}) 32 | runs-on: ubuntu-latest 33 | strategy: 34 | fail-fast: true 35 | matrix: 36 | rust_version: [stable] 37 | package: [runtime_injector, runtime_injector_actix] 38 | features: [arc, rc] 39 | exclude: 40 | - package: runtime_injector_actix 41 | features: rc 42 | steps: 43 | - name: Checkout 44 | uses: actions/checkout@v2 45 | - name: Setup toolchain 46 | uses: dtolnay/rust-toolchain@stable 47 | with: 48 | toolchain: ${{ matrix.rust_version }} 49 | - name: Run tests 50 | run: >- 51 | cargo test 52 | -p ${{ matrix.package }} 53 | --verbose 54 | --no-default-features 55 | --features ${{ matrix.features }} 56 | 57 | clippy: 58 | name: Clippy (${{ matrix.package }} - ${{ matrix.features }}) 59 | runs-on: ubuntu-latest 60 | strategy: 61 | fail-fast: true 62 | matrix: 63 | package: [runtime_injector, runtime_injector_actix] 64 | features: [arc, rc] 65 | exclude: 66 | - package: runtime_injector_actix 67 | features: rc 68 | steps: 69 | - uses: actions/checkout@v2 70 | - uses: dtolnay/rust-toolchain@clippy 71 | - run: >- 72 | cargo clippy 73 | -p ${{ matrix.package }} 74 | --tests 75 | --no-default-features 76 | --features ${{ matrix.features }} 77 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "rust-analyzer.checkOnSave.extraArgs": [ 3 | "--target-dir", 4 | "target/rust-analyzer" 5 | ] 6 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["crates/runtime_injector", "crates/runtime_injector_actix"] 3 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 TehPers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # runtime_injector 2 | 3 | 4 | This library provides a powerful, easy to use inversion-of-control (IoC) container with a focus on ergonomics and configurability. 5 | 6 | ## Status 7 | 8 | | Crate | crates.io | docs | 9 | |---|---|---| 10 | | runtime_injector | [![Current version][base-crate-badge]][base-crates-io] | [![Current documentation][base-doc-badge]][base-docs] | 11 | | runtime_injector_actix | [![Current version][actix-crate-badge]][actix-crates-io] | [![Current documentation][actix-doc-badge]][actix-docs] | 12 | 13 | ## Getting started 14 | 15 | For local development of runtime_injector, clone the repository, then build the project with cargo: 16 | 17 | ```bash 18 | git clone https://github.com/TehPers/runtime_injector 19 | cd runtime_injector 20 | cargo build 21 | ``` 22 | 23 | If you want to build the project using the "rc" feature instead, disable default features, and enable the "rc" feature: 24 | 25 | ```bash 26 | cargo build -p runtime_injector --no-default-features --features rc 27 | ``` 28 | 29 | Note that not all crates support the "rc" feature, so you will need to specify which crate you want to build. 30 | 31 | ## License 32 | 33 | These libraries are licensed under your choice of either [MIT](./LICENSE-MIT) or [Apache 2.0](./LICENSE-APACHE). 34 | 35 | [base-crate-badge]: https://img.shields.io/crates/v/runtime_injector?style=flat-square 36 | [base-doc-badge]: https://img.shields.io/docsrs/runtime_injector?style=flat-square 37 | [base-crates-io]: https://crates.io/crates/runtime_injector 38 | [base-docs]: https://docs.rs/runtime_injector 39 | 40 | [actix-crate-badge]: https://img.shields.io/crates/v/runtime_injector_actix?style=flat-square 41 | [actix-doc-badge]: https://img.shields.io/docsrs/runtime_injector_actix?style=flat-square 42 | [actix-crates-io]: https://crates.io/crates/runtime_injector_actix 43 | [actix-docs]: https://docs.rs/runtime_injector_actix 44 | -------------------------------------------------------------------------------- /crates/runtime_injector/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime_injector" 3 | version = "0.4.0" 4 | authors = ["TehPers "] 5 | edition = "2018" 6 | license = "MIT OR Apache-2.0" 7 | description = "Runtime dependency injection container" 8 | repository = "https://github.com/TehPers/runtime_injector" 9 | documentation = "https://docs.rs/runtime_injector" 10 | keywords = ["dependency-injection", "di", "ioc"] 11 | readme = "README.md" 12 | exclude = [] 13 | 14 | [features] 15 | default = ["arc"] 16 | arc = [] # Svc = Arc 17 | rc = [] # Svc = Rc 18 | -------------------------------------------------------------------------------- /crates/runtime_injector/README.md: -------------------------------------------------------------------------------- 1 | # runtime_injector 2 | 3 | [![Current version][crate-badge]][crates-io] 4 | [![Current documentation][doc-badge]][docs] 5 | 6 | This library provides a powerful, easy to use inversion-of-control (IoC) container with a focus on ergonomics and configurability. 7 | 8 | ## Getting started 9 | 10 | First, configure your injector: 11 | 12 | ```rust 13 | let module = define_module! { 14 | services = [MyService::new.transient()], 15 | interfaces = { 16 | dyn MyInterface = [MyInterfaceImpl::new.singleton()], 17 | }, 18 | }; 19 | 20 | let mut builder = Injector::builder(); 21 | builder.add_module(module); 22 | builder.provide(constant(MyConfig)); 23 | ``` 24 | 25 | Next, create your injector and request your services from it: 26 | 27 | ```rust 28 | let injector = builder.build(); 29 | let my_service: Svc = injector.get().unwrap(); 30 | let my_interface_impl: Svc = injector.get().unwrap(); 31 | 32 | // Since `MyService` is transient, we can also request an owned instance of it 33 | let my_service: Box = injector.get().unwrap(); 34 | ``` 35 | 36 | ## Minimum supported Rust version 37 | 38 | As the library is still in development, the only supported Rust version is the most recent version of stable Rust. The library may work on older versions, but there is no guarantee. 39 | 40 | ## License 41 | 42 | This library is licensed under your choice of either [MIT](./LICENSE-MIT) or [Apache 2.0](./LICENSE-APACHE). 43 | 44 | [crate-badge]: https://img.shields.io/crates/v/runtime_injector?style=flat-square 45 | [doc-badge]: https://img.shields.io/docsrs/runtime_injector?style=flat-square 46 | [crates-io]: https://crates.io/crates/runtime_injector 47 | [docs]: https://docs.rs/runtime_injector 48 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/any.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | /// Defines a conversion for a type into an [`dyn Any`](Any) trait object. 4 | pub trait AsAny: Any { 5 | /// Converts `self` into a trait object. 6 | fn as_any(&self) -> &dyn Any; 7 | 8 | /// Converts `self` into a mutable trait object. 9 | fn as_any_mut(&mut self) -> &mut dyn Any; 10 | } 11 | 12 | impl AsAny for T { 13 | fn as_any(&self) -> &dyn Any { 14 | self 15 | } 16 | 17 | fn as_any_mut(&mut self) -> &mut dyn Any { 18 | self 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/builder.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | Injector, Module, Provider, ProviderMap, RequestInfo, ServiceInfo, 3 | }; 4 | 5 | /// A builder for an [`Injector`]. 6 | #[derive(Default)] 7 | pub struct InjectorBuilder { 8 | providers: ProviderMap, 9 | root_info: RequestInfo, 10 | } 11 | 12 | impl InjectorBuilder { 13 | /// Assigns the provider for a service type. Multiple providers can be 14 | /// registered for a service. 15 | pub fn provide(&mut self, provider: P) { 16 | self.add_provider(Box::new(provider)) 17 | } 18 | 19 | /// Adds a provider to the injector. 20 | #[allow(clippy::missing_panics_doc)] 21 | pub fn add_provider(&mut self, provider: Box) { 22 | // Should never panic 23 | self.providers 24 | .entry(provider.result()) 25 | .or_insert_with(|| Some(Vec::new())) 26 | .as_mut() 27 | .unwrap() 28 | .push(provider) 29 | } 30 | 31 | /// Removes all providers for a service type. 32 | pub fn remove_providers( 33 | &mut self, 34 | service_info: ServiceInfo, 35 | ) -> Option>> { 36 | self.providers.remove(&service_info).flatten() 37 | } 38 | 39 | /// Borrows the root [`RequestInfo`] that will be used by calls to 40 | /// [`Injector::get()`]. 41 | #[must_use] 42 | pub fn root_info(&self) -> &RequestInfo { 43 | &self.root_info 44 | } 45 | 46 | /// Mutably borrows the root [`RequestInfo`] that will be used by calls to 47 | /// [`Injector::get()`]. 48 | #[must_use] 49 | pub fn root_info_mut(&mut self) -> &mut RequestInfo { 50 | &mut self.root_info 51 | } 52 | 53 | /// Adds all the providers registered in a module. This may cause multiple 54 | /// providers to be registered for the same service. 55 | /// 56 | /// If any conflicting request parameters have been set before adding this 57 | /// module, they are overridden. 58 | #[allow(clippy::missing_panics_doc)] 59 | pub fn add_module(&mut self, module: Module) { 60 | for (result, module_providers) in module.providers { 61 | // Should never panic 62 | let mut module_providers = module_providers.unwrap(); 63 | self.providers 64 | .entry(result) 65 | .and_modify(|providers| { 66 | // Should never panic 67 | providers.as_mut().unwrap().append(&mut module_providers) 68 | }) 69 | .or_insert_with(|| Some(module_providers)); 70 | } 71 | 72 | for (key, value) in module.parameters { 73 | drop(self.root_info_mut().insert_parameter_boxed(&key, value)); 74 | } 75 | } 76 | 77 | /// Builds the injector. 78 | #[must_use] 79 | pub fn build(self) -> Injector { 80 | Injector::new_from_parts(self.providers, self.root_info) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/docs.rs: -------------------------------------------------------------------------------- 1 | //! Additional documentation modules. 2 | 3 | pub mod getting_started; 4 | pub mod inversion_of_control; 5 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/docs/getting_started.rs: -------------------------------------------------------------------------------- 1 | //! # Getting started 2 | //! 3 | //! Let's start with a simple application. Let's write an application for 4 | //! greeting our users with a nice message. 5 | //! 6 | //! ``` 7 | //! use std::io::stdin; 8 | //! 9 | //! fn main() { 10 | //! println!("What is your name?"); 11 | //! let mut name = String::new(); 12 | //! stdin().read_line(&mut name).unwrap(); 13 | //! println!("Hello, {}! I hope you're having a wonderful day.", name); 14 | //! } 15 | //! ``` 16 | //! 17 | //! Cool! We can now ask our user for their name and greet them! But what if we 18 | //! want the user to be able to specify the name directly via command-line? 19 | //! 20 | //! ``` 21 | //! use std::{env::args, io::stdin}; 22 | //! 23 | //! fn get_name() -> String { 24 | //! let name_parts: Vec<_> = args().skip(1).collect(); 25 | //! if name_parts.is_empty() { 26 | //! println!("What is your name?"); 27 | //! let mut name = String::new(); 28 | //! stdin().read_line(&mut name).unwrap(); 29 | //! name 30 | //! } else { 31 | //! name_parts.join(" ") 32 | //! } 33 | //! } 34 | //! 35 | //! fn main() { 36 | //! let name = get_name(); 37 | //! println!("Hello, {}! I hope you're having a wonderful day.", name); 38 | //! } 39 | //! ``` 40 | //! 41 | //! Now our application lets users pass in their names directly, or if they 42 | //! don't, we can prompt them to provide it to us! This is cool, but we can 43 | //! still go further. Let's write some unit tests for our application so we can 44 | //! make sure our code does what it's supposed to do, even if we change it in 45 | //! the future. But wait, how do we verify that the output is correct? Well, 46 | //! we need a way to check the output of our program somehow! Let's write an 47 | //! abstraction for our output so we can check it it in our unit tests. 48 | //! 49 | //! ``` 50 | //! #[cfg(test)] 51 | //! # mod _ignored0 {} 52 | //! use std::fmt::Write; 53 | //! use std::{env::args, io::stdin}; 54 | //! 55 | //! trait OutputWriter { 56 | //! fn write_output(&mut self, message: &str); 57 | //! } 58 | //! 59 | //! struct ConsoleWriter; 60 | //! impl OutputWriter for ConsoleWriter { 61 | //! fn write_output(&mut self, message: &str) { 62 | //! println!("{}", message); 63 | //! } 64 | //! } 65 | //! 66 | //! // Our mock writer so we can observe the output in tests 67 | //! #[cfg(test)] 68 | //! # mod _ignored1 {} 69 | //! struct MockWriter(pub String); 70 | //! #[cfg(test)] 71 | //! # mod _ignored2 {} 72 | //! impl OutputWriter for MockWriter { 73 | //! fn write_output(&mut self, message: &str) { 74 | //! writeln!(self.0, "{}", message).unwrap(); 75 | //! } 76 | //! } 77 | //! 78 | //! trait InputReader { 79 | //! fn read_line(&mut self) -> String; 80 | //! } 81 | //! 82 | //! struct ConsoleReader; 83 | //! impl InputReader for ConsoleReader { 84 | //! fn read_line(&mut self) -> String { 85 | //! let mut input = String::new(); 86 | //! stdin().read_line(&mut input).unwrap(); 87 | //! input 88 | //! } 89 | //! } 90 | //! 91 | //! // Our mock reader for testing our application! 92 | //! #[cfg(test)] 93 | //! # mod _ignored3 {} 94 | //! struct MockReader(pub Option); 95 | //! #[cfg(test)] 96 | //! # mod _ignored4 {} 97 | //! impl InputReader for MockReader { 98 | //! fn read_line(&mut self) -> String { 99 | //! self.0.take().unwrap() 100 | //! } 101 | //! } 102 | //! 103 | //! fn get_name( 104 | //! args: &[String], 105 | //! reader: &mut R, 106 | //! writer: &mut W, 107 | //! ) -> String { 108 | //! let name_parts: Vec<&str> = 109 | //! args.iter().skip(1).map(|s| s.as_str()).collect(); 110 | //! if name_parts.is_empty() { 111 | //! writer.write_output("What is your name?"); 112 | //! reader.read_line() 113 | //! } else { 114 | //! name_parts.join(" ") 115 | //! } 116 | //! } 117 | //! 118 | //! fn main() { 119 | //! # // Let's actually verify the test passes 120 | //! # tests::name_is_correct(); 121 | //! # 122 | //! let args: Vec<_> = args().collect(); 123 | //! let mut reader = ConsoleReader; 124 | //! let mut writer = ConsoleWriter; 125 | //! let name = get_name(&args, &mut reader, &mut writer); 126 | //! writer.write_output(&format!( 127 | //! "Hello, {}! I hope you're having a wonderful day.", 128 | //! name 129 | //! )); 130 | //! } 131 | //! 132 | //! // Verify our program works like we want it to 133 | //! #[cfg(test)] 134 | //! # mod _ignored5 {} 135 | //! mod tests { 136 | //! use super::*; 137 | //! 138 | //! // Let's make sure we're getting the correct name from the user 139 | //! #[test] 140 | //! # fn _ignored() {} 141 | //! # pub 142 | //! fn name_is_correct() { 143 | //! // Setup our mocked reader and writer 144 | //! let args = vec!["ignored".to_string()]; 145 | //! let mut reader = MockReader(Some("John Smith".to_string())); 146 | //! let mut writer = MockWriter(String::new()); 147 | //! 148 | //! // Run the function we're testing 149 | //! let name = get_name(&args, &mut reader, &mut writer); 150 | //! 151 | //! // Verify that we got the right name 152 | //! assert_eq!("John Smith", name); 153 | //! assert!(!writer.0.is_empty()); 154 | //! } 155 | //! } 156 | //! ``` 157 | //! 158 | //! Cool, now we have a simple unit test for our program. We can now verify 159 | //! that it works automatically in our build pipelines, and we can be sure that 160 | //! future improvements to our program won't break it! 161 | //! 162 | //! Speaking of future improvements, our users love the program! They keep 163 | //! asking for more and more features to be added to it, though. There's a huge 164 | //! group of users requesting that we give them control over the output 165 | //! format, plus some users asking to be able to send these messages to people 166 | //! on the internet! Not only that, but some people want the network requests 167 | //! to be done via HTTPS and others want to use TCP. Woah, how in the world do 168 | //! we configure our application to be able to do all this, yet still have the 169 | //! ability to write unit tests for everything and understand what's going on? 170 | //! 171 | //! This is where dependency injection comes in. There is no way we could 172 | //! possibly manage all the different ways of writing outputs, reading inputs, 173 | //! configuring the greetings, and so on entirely on our own without our code 174 | //! becoming huge and complex. We would end up with a tangled web of 175 | //! dependencies between all the parts of our application, and it would quickly 176 | //! become unmaintainable. Instead, let's rely on a container to manage our 177 | //! dependencies for us so we don't need to think about that at all anymore. 178 | //! 179 | //! ``` 180 | //! use runtime_injector::{ 181 | //! interface, Arg, Injector, InjectorBuilder, IntoSingleton, Service, Svc, 182 | //! TypedProvider, WithArg, 183 | //! }; 184 | //! 185 | //! // Let's leave out the functions from our traits, we don't really need them 186 | //! // for this example. We need our trait to be a subtrait of `Service` so 187 | //! // that we can use type erasure in our container later on. Also, if our 188 | //! // services need to be thread-safe and we're using the "arc" feature for 189 | //! // runtime_injector, then `Service` will automatically require `Send` + 190 | //! // `Sync` for us 191 | //! trait OutputWriter: Service {} 192 | //! 193 | //! // We still want to be able to write to the console, but we need our output 194 | //! // formatter to make sure we correctly format our output 195 | //! struct ConsoleWriter(Svc); 196 | //! impl OutputWriter for ConsoleWriter {} 197 | //! 198 | //! // We also want to be able to send greetings across HTTPS 199 | //! struct HttpsWriter(Svc); 200 | //! impl OutputWriter for HttpsWriter {} 201 | //! 202 | //! // Finally, we need to support TCP as well 203 | //! struct TcpWriter(Svc); 204 | //! impl OutputWriter for TcpWriter {} 205 | //! 206 | //! // Also, we need to be able to mock this for testing 207 | //! #[cfg(test)] 208 | //! struct MockWriter(pub Arg, Svc); 209 | //! #[cfg(test)] 210 | //! impl OutputWriter for MockWriter {} 211 | //! 212 | //! // We also need a way to format the messages 213 | //! trait OutputFormatter: Service {} 214 | //! 215 | //! // Our users want to be able to format them with custom formats! 216 | //! struct UserFormatter(pub Arg); 217 | //! impl OutputFormatter for UserFormatter {} 218 | //! 219 | //! // Not all users want to use a custom format though, so we need a default 220 | //! #[derive(Default)] 221 | //! struct DefaultFormatter; 222 | //! impl OutputFormatter for DefaultFormatter {} 223 | //! 224 | //! // Now let's bring over our reader implementations 225 | //! trait InputReader: Service {} 226 | //! 227 | //! #[derive(Default)] 228 | //! struct ConsoleReader; 229 | //! impl InputReader for ConsoleReader {} 230 | //! 231 | //! #[cfg(test)] 232 | //! struct MockReader(pub Arg>); 233 | //! #[cfg(test)] 234 | //! impl InputReader for MockReader {} 235 | //! 236 | //! // Let's create an enum to help us configure our output writer too 237 | //! #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 238 | //! enum OutputType { 239 | //! Console, 240 | //! Https, 241 | //! Tcp, 242 | //! } 243 | //! 244 | //! // Since we'll be relying on dependency injection, we need to provide our 245 | //! // container a little more information by declaring our interfaces. First, 246 | //! // we have three implementations of `OutputWriter` 247 | //! interface! { 248 | //! dyn OutputWriter = [ 249 | //! ConsoleWriter, 250 | //! HttpsWriter, 251 | //! TcpWriter, 252 | //! #[cfg(test)] 253 | //! MockWriter 254 | //! ] 255 | //! } 256 | //! 257 | //! // We also have two implementations of `OutputFormatter` 258 | //! interface! { 259 | //! dyn OutputFormatter = [ 260 | //! UserFormatter, 261 | //! DefaultFormatter 262 | //! ] 263 | //! } 264 | //! 265 | //! // Finally, we have two implementations of `InputReader` as well 266 | //! interface! { 267 | //! dyn InputReader = [ 268 | //! ConsoleReader, 269 | //! #[cfg(test)] 270 | //! MockReader 271 | //! ] 272 | //! } 273 | //! 274 | //! // We'll make a function to help us configure everything based on the 275 | //! // configuration settings our user gave us 276 | //! fn configure_services( 277 | //! user_format: Option, 278 | //! output_type: OutputType, 279 | //! ) -> InjectorBuilder { 280 | //! // Now how do we manage all these possible implementations? Let's rely 281 | //! // on a container to manage them for us! 282 | //! let mut builder = Injector::builder(); 283 | //! 284 | //! // Let's configure everything without worrying about the testing 285 | //! // environment. We want to configure our code to use the console reader 286 | //! // for reading input since we don't support any other input readers 287 | //! builder.provide( 288 | //! ConsoleReader::default 289 | //! .singleton() 290 | //! .with_interface::(), 291 | //! ); 292 | //! 293 | //! // We want to determine the formatter based on whether the user 294 | //! // provided a format to us 295 | //! if let Some(user_format) = user_format { 296 | //! // Let's register the user formatter as our output formatter since 297 | //! // the user gave us a custom format to use 298 | //! builder.provide( 299 | //! UserFormatter 300 | //! .singleton() 301 | //! .with_interface::(), 302 | //! ); 303 | //! 304 | //! // We want to also pass the user's custom format to our service 305 | //! builder.with_arg::(user_format); 306 | //! } else { 307 | //! // The user didn't give us a custom format, so we'll use the 308 | //! // default output formatter instead 309 | //! builder.provide( 310 | //! DefaultFormatter::default 311 | //! .singleton() 312 | //! .with_interface::(), 313 | //! ); 314 | //! } 315 | //! 316 | //! // Finally, we need to decide how we're going to send our greetings to 317 | //! // people. We can use our helper enum for that here 318 | //! match output_type { 319 | //! OutputType::Console => { 320 | //! builder.provide( 321 | //! ConsoleWriter 322 | //! .singleton() 323 | //! .with_interface::(), 324 | //! ); 325 | //! } 326 | //! OutputType::Https => { 327 | //! builder.provide( 328 | //! HttpsWriter 329 | //! .singleton() 330 | //! .with_interface::(), 331 | //! ); 332 | //! } 333 | //! OutputType::Tcp => { 334 | //! builder.provide( 335 | //! TcpWriter.singleton().with_interface::(), 336 | //! ); 337 | //! } 338 | //! } 339 | //! 340 | //! // Let's return our injector builder now 341 | //! builder 342 | //! } 343 | //! 344 | //! fn main() { 345 | //! // We want the user to be able to configure the application here. 346 | //! // Normally, we'd use something like clap for this, but for the sake of 347 | //! // the example, we'll just hardcode the config 348 | //! let user_format = Some("Hello! Have a great day.".to_string()); 349 | //! let output_type = OutputType::Console; 350 | //! 351 | //! // With this, we have enough information to configure everything 352 | //! let builder = configure_services(user_format, output_type); 353 | //! 354 | //! // Finally, we just need to construct our services so we can use them 355 | //! let injector = builder.build(); 356 | //! let reader: Svc = injector.get().unwrap(); 357 | //! let writer: Svc = injector.get().unwrap(); 358 | //! 359 | //! // Now we can write our application logic! 360 | //! // ... 361 | //! } 362 | //! 363 | //! // Let's not forget about unit tests! 364 | //! #[cfg(test)] 365 | //! mod tests { 366 | //! use super::*; 367 | //! use runtime_injector::{define_module, Injector, IntoSingleton, Svc}; 368 | //! 369 | //! #[test] 370 | //! fn console_output_is_formatted_before_being_written() { 371 | //! // Let's make a custom module for testing just the console writer 372 | //! let module = define_module! { 373 | //! interfaces = { 374 | //! dyn OutputFormatter = [ 375 | //! DefaultFormatter::default.singleton(), 376 | //! ], 377 | //! }, 378 | //! services = [ 379 | //! // We won't need to put this behind an interface this time 380 | //! ConsoleWriter.singleton(), 381 | //! ], 382 | //! }; 383 | //! 384 | //! // We'll configure our injector now using the module we created 385 | //! let mut builder = Injector::builder(); 386 | //! builder.add_module(module); 387 | //! 388 | //! // Now we can test our console writer 389 | //! let injector = builder.build(); 390 | //! let writer: Svc = injector.get().unwrap(); 391 | //! 392 | //! // ... 393 | //! } 394 | //! } 395 | //! ``` 396 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/docs/inversion_of_control.rs: -------------------------------------------------------------------------------- 1 | //! # Inversion of control 2 | //! 3 | //! As an application grows, different components within the application need 4 | //! to be tested in isolation. Additionally, different deployment 5 | //! configurations may require different implementations of a service. What 6 | //! happens when you have code that relies on all these complex configurations? 7 | //! If you aren't careful, different components of your application will start 8 | //! becoming tightly coupled, and your code will become hard to maintain. 9 | //! 10 | //! For example, suppose you want to authenticate users by looking them up in 11 | //! a database, however you need to support a different database for unit 12 | //! testing, integration testing, and production: 13 | //! 14 | //! ```no_run 15 | //! use std::sync::Arc; 16 | //! 17 | //! #[derive(Clone, Debug)] 18 | //! struct User { 19 | //! scopes: Vec, 20 | //! } 21 | //! 22 | //! // Suppose we need to abstract out our database so we can use different 23 | //! // implementations for different environments. We need our database to be 24 | //! // thread-safe since we're planning to multi-thread our application 25 | //! trait UserDatabase: Send + Sync { 26 | //! fn get_user(&self, id: u32) -> User { 27 | //! todo!() 28 | //! } 29 | //! } 30 | //! 31 | //! // We need to be able to unit test code that depends on a user database 32 | //! #[derive(Default)] 33 | //! struct MockUserDatabase; 34 | //! impl UserDatabase for MockUserDatabase {} 35 | //! 36 | //! // We also need a real implementation that connects to a SQL database. 37 | //! // We'll need a connection string to establish the connection, so we'll 38 | //! // include a field for that. 39 | //! struct SqlUserDatabase(String); 40 | //! impl UserDatabase for SqlUserDatabase {} 41 | //! 42 | //! // We're using a special configuration for integration testing, so we'll 43 | //! // need an implementation for that environment as well. We still need a 44 | //! // connection string for the database, but now we also want to configure it 45 | //! // based on a set of testing parameters. For example, should we force this 46 | //! // database to fail so we can test that? 47 | //! # struct IntegrationTestParameters; 48 | //! struct IntegrationUserDatabase(String, IntegrationTestParameters); 49 | //! impl UserDatabase for IntegrationUserDatabase {} 50 | //! 51 | //! // Now suppose we want to authenticate users. How might we accomplish this 52 | //! // task? Well, we'd look them up in the database and check what permissions 53 | //! // they have! 54 | //! trait UserAuthenticator: Send + Sync { 55 | //! fn has_access(&self, user_id: u32, scope: &str) -> bool; 56 | //! } 57 | //! 58 | //! // We don't need a database for our mock authenticator since we aren't 59 | //! // looking them up at all. 60 | //! #[derive(Default)] 61 | //! struct MockUserAuthenticator; 62 | //! impl UserAuthenticator for MockUserAuthenticator { 63 | //! fn has_access(&self, _user_id: u32, _scope: &str) -> bool { 64 | //! true 65 | //! } 66 | //! } 67 | //! 68 | //! // We *do* need a database for production though, since we want to look 69 | //! // them up in our database to check their permissions. 70 | //! struct DatabaseUserAuthenticator(Arc); 71 | //! impl UserAuthenticator for DatabaseUserAuthenticator { 72 | //! fn has_access(&self, user_id: u32, scope: &str) -> bool { 73 | //! let user = self.0.get_user(user_id); 74 | //! user.scopes.iter().any(|s| s.as_str() == scope) 75 | //! } 76 | //! } 77 | //! 78 | //! fn main() { 79 | //! // Now we need to configure which implementations to use for our 80 | //! // environment! We can use feature flags for this, but it's ugly and 81 | //! // gets unmaintainable fast as our number of services grow. 82 | //! // Additionally, if we want to construct any services later during our 83 | //! // program's execution, we need a whole mess of cfg attributes again. 84 | //! // This turns into a mess of helper functions for constructing our 85 | //! // dependencies. 86 | //! 87 | //! // Let's make a helper for creating our user database. The actual 88 | //! // implementation depends on if we're in our integration testing 89 | //! // environment, so we'll return `impl UserDatabase` so we don't need to 90 | //! // worry about specifying the concrete type 91 | //! fn make_database(connection_string: String) -> impl UserDatabase { 92 | //! #[cfg(feature = "integration")] 93 | //! { 94 | //! IntegrationUserDatabase( 95 | //! connection_string, 96 | //! // How do we get the integration test parameters? For now, 97 | //! // let's globally configure it somewhere and retrieve them 98 | //! // like that. This way, we can make sure our helper is easy 99 | //! // to use. 100 | //! get_integration_test_parameters(), 101 | //! ) 102 | //! } 103 | //! #[cfg(not(feature = "integration"))] 104 | //! { 105 | //! SqlUserDatabase(connection_string) 106 | //! } 107 | //! } 108 | //! 109 | //! // Now we need a way to get our integration test parameters. Since we 110 | //! // want to configure this at runtime, this can quickly become 111 | //! // complicated, possibly involving global state with static variables 112 | //! // so that we can call this whenever we need to. Since it is unsafe to 113 | //! // use mutable static variables, we'll probably want to store it as 114 | //! // `Arc>>` and have a way of 115 | //! // setting the value before our test. Let's leave that out for now... 116 | //! fn get_integration_test_parameters() -> IntegrationTestParameters { 117 | //! todo!() 118 | //! } 119 | //! 120 | //! // Since we sometimes want to mock our database, let's make an 121 | //! // additional helper for this. Note that we can't pass a bool into our 122 | //! // make_database function to configure this because we'd end up having 123 | //! // two possible return types from our function, causing a compile error 124 | //! fn make_database_mock() -> impl UserDatabase { 125 | //! MockUserDatabase 126 | //! } 127 | //! 128 | //! // Let's create a helper for creating our user authenticator. Since we 129 | //! // don't have a special implementation for integration testing, this 130 | //! // is much simpler than our user database helper 131 | //! fn make_authenticator( 132 | //! database: Arc, 133 | //! ) -> impl UserAuthenticator { 134 | //! DatabaseUserAuthenticator(database) 135 | //! } 136 | //! 137 | //! // Our mock authenticator has a different set of dependencies! Let's 138 | //! // make another helper for creating our mock authenticator since we 139 | //! // don't need a database to construct it. 140 | //! fn make_authenticator_mock() -> impl UserAuthenticator { 141 | //! MockUserAuthenticator 142 | //! } 143 | //! } 144 | //! ``` 145 | //! 146 | //! That's quite a bit of code to setup even just a simple application with 147 | //! multiple target environments! This doesn't even include our business logic. 148 | //! 149 | //! ## Simplifying our code with runtime_injector 150 | //! 151 | //! As our application grows, so does the number of helper functions we need to 152 | //! create to handle all the different implementations of our services. This 153 | //! quickly becomes ugly and unmaintainable. What happens if we let something 154 | //! else create our services for us instead? Let's let an 155 | //! [`Injector`](crate::Injector) manage our services for us to help us 156 | //! simplify our code and make it more maintainable. 157 | //! 158 | //! ``` 159 | //! use runtime_injector::{ 160 | //! constant, define_module, interface, Injector, IntoSingleton, 161 | //! IntoTransient, Service, Svc, 162 | //! }; 163 | //! 164 | //! #[derive(Clone, Debug)] 165 | //! struct User { 166 | //! scopes: Vec, 167 | //! } 168 | //! 169 | //! // We still want our services to be thread-safe. `Service` is a trait that 170 | //! // is automatically implemented for all `Send + Sync + 'static` types, so 171 | //! // we can use it here instead. Additionally, if we decide that we no longer 172 | //! // need to multi-thread this later, we can switch to the "rc" feature to 173 | //! // use `Rc` for our service pointers, and this trait will automatically be 174 | //! // implemented for all `'static` types instead, regardless of thread safety 175 | //! trait UserDatabase: Service { 176 | //! fn get_user(&self, id: u32) -> User { 177 | //! todo!() 178 | //! } 179 | //! } 180 | //! 181 | //! #[derive(Default)] 182 | //! struct MockUserDatabase; 183 | //! impl UserDatabase for MockUserDatabase {} 184 | //! 185 | //! // Since we're having our connection string injected, we need to put it 186 | //! // behind some type that our container can inject 187 | //! struct SqlUserDatabase(Svc); 188 | //! impl UserDatabase for SqlUserDatabase {} 189 | //! 190 | //! # #[derive(Default)] 191 | //! # struct IntegrationTestParameters; 192 | //! // We'll also inject our connection string and test parameters here 193 | //! struct IntegrationUserDatabase(Svc, Svc); 194 | //! impl UserDatabase for IntegrationUserDatabase {} 195 | //! 196 | //! trait UserAuthenticator: Service { 197 | //! fn has_access(&self, user_id: u32, scope: &str) -> bool; 198 | //! } 199 | //! 200 | //! #[derive(Default)] 201 | //! struct MockUserAuthenticator; 202 | //! impl UserAuthenticator for MockUserAuthenticator { 203 | //! fn has_access(&self, _user_id: u32, _scope: &str) -> bool { 204 | //! true 205 | //! } 206 | //! } 207 | //! 208 | //! // We're switching to dynamic dispatch here which is marginally slower than 209 | //! // static dispatch, but we're going to lose most of our performance to I/O 210 | //! // anyway so the additional v-table lookup is hardly relevant here 211 | //! struct DatabaseUserAuthenticator(Svc); 212 | //! impl UserAuthenticator for DatabaseUserAuthenticator { 213 | //! fn has_access(&self, user_id: u32, scope: &str) -> bool { 214 | //! let user = self.0.get_user(user_id); 215 | //! user.scopes.iter().any(|s| s.as_str() == scope) 216 | //! } 217 | //! } 218 | //! 219 | //! // Now we need to declare what services we can use. 220 | //! interface! { 221 | //! // Since we have three implementations of a user database, we'll 222 | //! // declare `UserDatabase` as being an interface that supports those 223 | //! // three types 224 | //! dyn UserDatabase = [ 225 | //! MockUserDatabase, 226 | //! SqlUserDatabase, 227 | //! IntegrationUserDatabase, 228 | //! ], 229 | //! 230 | //! // Similarly, we'll declare `UserAuthenticator` as being an interface 231 | //! // that supports both our database-backed authenticator and mock one 232 | //! dyn UserAuthenticator = [ 233 | //! MockUserAuthenticator, 234 | //! DatabaseUserAuthenticator, 235 | //! ], 236 | //! } 237 | //! 238 | //! fn main() { 239 | //! // We can easily determine which implementations we will use in one 240 | //! // place by creating a module. If we add more implementations later, we 241 | //! // only need to change a few lines of code in one place rather than 242 | //! // adding #[cfg] attributes all over our code 243 | //! let module = define_module! { 244 | //! interfaces = { 245 | //! dyn UserAuthenticator = [DatabaseUserAuthenticator.singleton()] 246 | //! }, 247 | //! #[cfg(feature = "integration")] 248 | //! interfaces = { 249 | //! dyn UserDatabase = [IntegrationUserDatabase.singleton()] 250 | //! }, 251 | //! #[cfg(not(feature = "integration"))] 252 | //! interfaces = { 253 | //! dyn UserDatabase = [SqlUserDatabase.singleton()] 254 | //! }, 255 | //! }; 256 | //! 257 | //! // Now we'll start to create our container. Using a builder, we can 258 | //! // easily tell our injector what services it should be able to provide. 259 | //! // We'll start by adding our module to it 260 | //! let mut builder = Injector::builder(); 261 | //! builder.add_module(module); 262 | //! 263 | //! // Let's add our connection string and integration parameters now 264 | //! builder.provide(constant("our_secret_connection_string".to_string())); 265 | //! #[cfg(feature = "integration")] 266 | //! builder.provide(constant(IntegrationTestParameters::default())); 267 | //! 268 | //! // Now we're ready to start creating our services! We have one single 269 | //! // way of creating each of our services, regardless of what the actual 270 | //! // implementation of that service is. Let's create our container now 271 | //! let injector = builder.build(); 272 | //! 273 | //! // We can get any service we want with `injector.get()`. Here, we're 274 | //! // relying on our container to pass in the connection string and test 275 | //! // parameters (if we're doing an integration test) without needing any 276 | //! // complicated logic to construct those types 277 | //! let database: Svc = injector.get().unwrap(); 278 | //! 279 | //! // We've already created our database, and we don't want to create it 280 | //! // again. Since we've declared our database as a singleton service, our 281 | //! // container will reuse the same instance when we get our authenticator 282 | //! let auth: Svc = injector.get().unwrap(); 283 | //! 284 | //! // If we want to mock out any of our services, all we need to do is 285 | //! // create a module which provides the mock implementation instead 286 | //! let _module = define_module! { 287 | //! interfaces = { 288 | //! dyn UserAuthenticator = [DatabaseUserAuthenticator.singleton()], 289 | //! dyn UserDatabase = [MockUserDatabase::default.singleton()], 290 | //! }, 291 | //! }; 292 | //! } 293 | //! ``` 294 | //! 295 | //! We still have all our different implementations of `UserDatabase` and 296 | //! `UserAuthenticator`, but now it's super easy to get the correct 297 | //! implementations of each of those services when we need to! We don't need 298 | //! any complicated helper functions, and we certainly don't need to litter our 299 | //! business logic with `#[cfg]` to be able to use it with unit and integration 300 | //! testing. Instead, rather than relying on our helpers to create the right 301 | //! implementations for us, we're just asking for an implementation and letting 302 | //! our container handle the rest. 303 | //! 304 | //! Something else that you might notice is that we are able to rely on our 305 | //! container to create a single instance of our user database and provide that 306 | //! single instance whenever we need it. Not only can we rely on our injector 307 | //! to call our constructors for us, but we can also rely on it to manage the 308 | //! lifetimes of our services. This would normally be very difficult without a 309 | //! container to do it for you. For example, suppose you want to create a 310 | //! single instance of your user database throughout the entire lifetime of 311 | //! your application since you don't want to open unnecessary connections to 312 | //! your database. Rather than relying on a static variable to hold that 313 | //! instance, we can instead rely on our container to create and provide that 314 | //! instance when we need it. If we wanted to provide a new instance each time, 315 | //! we could configure our container to do that instead! Similarly, if we 316 | //! wanted to create a single instance of our service for every HTTP request 317 | //! that we get, that's possible as well by creating a custom provider. We have 318 | //! complete control over our services, yet we don't have any of the extra 319 | //! complexities that normally comes with that level of control. 320 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/injector.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectResult, InjectorBuilder, Interface, Provider, Request, RequestInfo, 3 | ServiceInfo, Services, Svc, 4 | }; 5 | use std::collections::HashMap; 6 | 7 | pub(crate) type ProviderMap = 8 | HashMap>>>; 9 | 10 | pub(crate) trait MapContainerEx { 11 | fn new(value: T) -> Self; 12 | fn with_inner R>(&self, f: F) -> R; 13 | fn with_inner_mut R>(&self, f: F) -> R; 14 | } 15 | 16 | #[cfg(feature = "rc")] 17 | mod types { 18 | use super::MapContainerEx; 19 | use std::{cell::RefCell, rc::Rc}; 20 | 21 | pub type MapContainer = Rc>; 22 | 23 | impl MapContainerEx for MapContainer { 24 | fn new(value: T) -> Self { 25 | Rc::new(RefCell::new(value)) 26 | } 27 | 28 | fn with_inner R>(&self, f: F) -> R { 29 | f(&*self.borrow()) 30 | } 31 | 32 | fn with_inner_mut R>(&self, f: F) -> R { 33 | f(&mut *self.borrow_mut()) 34 | } 35 | } 36 | } 37 | 38 | #[cfg(feature = "arc")] 39 | mod types { 40 | use super::MapContainerEx; 41 | use std::sync::{Arc, Mutex}; 42 | 43 | pub type MapContainer = Arc>; 44 | 45 | impl MapContainerEx for MapContainer { 46 | fn new(value: T) -> Self { 47 | Arc::new(Mutex::new(value)) 48 | } 49 | 50 | fn with_inner R>(&self, f: F) -> R { 51 | f(&*self.lock().unwrap()) 52 | } 53 | 54 | fn with_inner_mut R>(&self, f: F) -> R { 55 | f(&mut *self.lock().unwrap()) 56 | } 57 | } 58 | } 59 | 60 | #[allow(clippy::wildcard_imports)] 61 | pub(crate) use types::*; 62 | 63 | /// A runtime dependency injection container. This holds all the bindings 64 | /// between service types and their providers, as well as all the mappings from 65 | /// interfaces to their implementations (if they differ). 66 | /// 67 | /// ## Injecting the injector 68 | /// 69 | /// Cloning the injector does not clone the providers inside of it. Instead, 70 | /// both injectors will use the same providers, meaning that an injector can be 71 | /// passed to a service as a dependency. The injector can be requested as 72 | /// itself without using a service pointer. It does not need to be registered 73 | /// as a dependency in the builder beforehand. 74 | /// 75 | /// Note that requesting the injector inside of your services is generally bad 76 | /// practice, and is known as the service locator antipattern. This is mostly 77 | /// useful for custom ad-hoc service factories where you can create instances 78 | /// of your services on demand. If you need to create instances of a service 79 | /// outside of your constructor, you may want to use [`Factory`] instead. 80 | /// 81 | /// [`Factory`]: crate::Factory 82 | /// 83 | /// ``` 84 | /// use runtime_injector::{ 85 | /// constant, InjectResult, Injector, IntoSingleton, IntoTransient, Svc, 86 | /// }; 87 | /// use std::sync::Mutex; 88 | /// 89 | /// struct FloatFactory(Injector); 90 | /// 91 | /// impl FloatFactory { 92 | /// pub fn get(&self) -> InjectResult { 93 | /// let int: Box = self.0.get()?; 94 | /// Ok(*int as f32) 95 | /// } 96 | /// } 97 | /// 98 | /// fn count(counter: Svc>) -> i32 { 99 | /// let mut counter = counter.lock().unwrap(); 100 | /// *counter += 1; 101 | /// *counter 102 | /// } 103 | /// 104 | /// let mut builder = Injector::builder(); 105 | /// builder.provide(constant(Mutex::new(0i32))); 106 | /// builder.provide(count.transient()); 107 | /// builder.provide(FloatFactory.singleton()); 108 | /// 109 | /// let injector = builder.build(); 110 | /// let float_factory: Svc = injector.get().unwrap(); 111 | /// let value1 = float_factory.get().unwrap(); 112 | /// let value2 = float_factory.get().unwrap(); 113 | /// 114 | /// assert_eq!(1.0, value1); 115 | /// assert_eq!(2.0, value2); 116 | /// ``` 117 | #[derive(Clone, Default)] 118 | pub struct Injector { 119 | provider_map: MapContainer, 120 | root_request_info: Svc, 121 | } 122 | 123 | impl Injector { 124 | /// Creates a builder for an injector. This is the preferred way of 125 | /// creating an injector. 126 | #[must_use] 127 | pub fn builder() -> InjectorBuilder { 128 | InjectorBuilder::default() 129 | } 130 | 131 | /// Creates a new injector directly from its providers and implementations. 132 | /// Prefer [`Injector::builder()`] for creating new injectors instead. 133 | #[must_use] 134 | #[deprecated( 135 | note = "prefer using a builder; this will be removed in 0.5", 136 | since = "0.3.1" 137 | )] 138 | pub fn new(providers: ProviderMap) -> Self { 139 | Injector { 140 | provider_map: MapContainerEx::new(providers), 141 | root_request_info: Svc::new(RequestInfo::default()), 142 | } 143 | } 144 | 145 | pub(crate) fn new_from_parts( 146 | providers: ProviderMap, 147 | request_info: RequestInfo, 148 | ) -> Self { 149 | Injector { 150 | provider_map: MapContainerEx::new(providers), 151 | root_request_info: Svc::new(request_info), 152 | } 153 | } 154 | 155 | /// Performs a request for a service. There are several types of requests 156 | /// that can be made to the service container by default: 157 | /// 158 | /// - [`Svc`](crate::Svc): Requests a service pointer to the given 159 | /// interface and creates an instance of the service if needed. If 160 | /// multiple service providers are registered for that interface, then 161 | /// returns an error instead. 162 | /// - [`Box`]: Requests an owned service pointer to the given interface 163 | /// and creates an instance of the service. Not all service providers can 164 | /// provide owned versions of their services, so this may fail for some 165 | /// services. 166 | /// - [`Option>`]/[`Option>`]: Requests a service pointer to 167 | /// the given interface and creates an instance of the service if needed. 168 | /// If no provider for that service is registered, then returns 169 | /// `Ok(None)` rather than returning an error. If multiple providers are 170 | /// registered, then instead returns an error. If an owned pointer is 171 | /// requested but the provider can't provide owned pointers, then returns 172 | /// an error. 173 | /// - [`Vec>`]/[`Vec>`]: Requests all the implementations of 174 | /// an interface. This will eagerly create the services as part of the 175 | /// request. If owned service pointers are requested and any providers 176 | /// can't provide owned pointers, then returns an error instead. 177 | /// - [`Services`]: Requests all the implementations of an interface. 178 | /// This will lazily create the services on demand. See the 179 | /// [documentation for `Services`](Services) for more details. 180 | /// - [`Injector`]: Requests a clone of the injector. While it doesn't make 181 | /// much sense to request this directly from the injector itself, this 182 | /// allows the injector to be requested as a dependency inside of 183 | /// services (for instance, factories). 184 | /// - [`RequestInfo`]: Requests information about the current request, 185 | /// including the current resolution path. 186 | /// - [`Factory`]: Lazily performs requests on demand. 187 | /// 188 | /// [`Factory`]: crate::Factory 189 | /// 190 | /// See the [documentation for `Request`](Request) for more information on 191 | /// what can be requested. Custom request types can also be created by 192 | /// implementing [`Request`] on your type. 193 | /// 194 | /// Requests to service pointers of sized types will attempt to use the 195 | /// registered provider to retrieve an instance of that service. For 196 | /// instance, a request for a singleton service will create an instance of 197 | /// that service if one doesn't exist already, and either return a service 198 | /// pointer to the instance that was already created, or return a service 199 | /// pointer to the new instance (if one didn't exist already). 200 | /// 201 | /// ``` 202 | /// use runtime_injector::{Injector, IntoSingleton, Svc}; 203 | /// 204 | /// #[derive(Default)] 205 | /// struct Bar; 206 | /// 207 | /// let mut builder = Injector::builder(); 208 | /// builder.provide(Bar::default.singleton()); 209 | /// 210 | /// let injector = builder.build(); 211 | /// let _bar: Svc = injector.get().unwrap(); 212 | /// ``` 213 | /// 214 | /// Requests for service pointers of `dyn Trait` interface types will 215 | /// instead request the implementation of that interface type. For example, 216 | /// if `dyn Foo`'s registered implementation is for the service type `Bar`, 217 | /// then a request for a service pointer of `dyn Foo` will return a service 218 | /// pointer to a `Bar`, although the return type will be `Svc`. 219 | /// 220 | /// ``` 221 | /// use runtime_injector::{ 222 | /// interface, Injector, IntoSingleton, Svc, TypedProvider, Service 223 | /// }; 224 | /// 225 | /// trait Foo: Service {} 226 | /// interface!(dyn Foo = [Bar]); 227 | /// 228 | /// #[derive(Default)] 229 | /// struct Bar; 230 | /// impl Foo for Bar {} 231 | /// 232 | /// let mut builder = Injector::builder(); 233 | /// builder.provide(Bar::default.singleton().with_interface::()); 234 | /// 235 | /// let injector = builder.build(); 236 | /// let _bar: Svc = injector.get().unwrap(); 237 | /// ``` 238 | /// 239 | /// If multiple providers for a service exist, then a request for a single 240 | /// service pointer to that service will fail. 241 | /// 242 | /// ``` 243 | /// use runtime_injector::{ 244 | /// interface, Injector, IntoSingleton, Svc, TypedProvider, Service 245 | /// }; 246 | /// 247 | /// trait Foo: Service {} 248 | /// interface!(dyn Foo = [Bar, Baz]); 249 | /// 250 | /// #[derive(Default)] 251 | /// struct Bar; 252 | /// impl Foo for Bar {} 253 | /// 254 | /// #[derive(Default)] 255 | /// struct Baz; 256 | /// impl Foo for Baz {} 257 | /// 258 | /// let mut builder = Injector::builder(); 259 | /// builder.provide(Bar::default.singleton().with_interface::()); 260 | /// builder.provide(Baz::default.singleton().with_interface::()); 261 | /// 262 | /// let injector = builder.build(); 263 | /// assert!(injector.get::>().is_err()); 264 | /// ``` 265 | pub fn get(&self) -> InjectResult { 266 | self.get_with(self.root_request_info.as_ref()) 267 | } 268 | 269 | /// Performs a request for a service with additional request information. 270 | /// 271 | /// ## Example 272 | /// 273 | /// ``` 274 | /// use runtime_injector::{ 275 | /// Injector, IntoTransient, Request, RequestInfo, Svc, 276 | /// }; 277 | /// 278 | /// struct Foo(String); 279 | /// impl Foo { 280 | /// fn new(request_info: RequestInfo) -> Self { 281 | /// let value = request_info 282 | /// .get_parameter("value") 283 | /// .unwrap() 284 | /// .downcast_ref::() 285 | /// .unwrap() 286 | /// .clone(); 287 | /// Foo(value) 288 | /// } 289 | /// } 290 | /// 291 | /// let mut builder = Injector::builder(); 292 | /// builder.provide(Foo::new.transient()); 293 | /// 294 | /// let injector = builder.build(); 295 | /// let mut request_info = RequestInfo::default(); 296 | /// request_info.insert_parameter("value", "foo".to_owned()); 297 | /// 298 | /// let foo: Svc = injector.get_with(&request_info).unwrap(); 299 | /// assert_eq!("foo", foo.0); 300 | /// ``` 301 | pub fn get_with( 302 | &self, 303 | request_info: &RequestInfo, 304 | ) -> InjectResult { 305 | R::request(self, request_info) 306 | } 307 | 308 | /// Gets implementations of a service from the container. This is 309 | /// equivalent to requesting [`Services`] from [`Injector::get()`]. 310 | pub(crate) fn get_service( 311 | &self, 312 | request_info: &RequestInfo, 313 | ) -> InjectResult> { 314 | Services::new( 315 | self.clone(), 316 | self.provider_map.clone(), 317 | request_info.clone(), 318 | ) 319 | } 320 | } 321 | 322 | #[cfg(test)] 323 | mod tests { 324 | use crate::{ 325 | DynSvc, InjectError, InjectResult, Injector, Provider, RequestInfo, 326 | ServiceInfo, Svc, 327 | }; 328 | use core::panic; 329 | 330 | #[test] 331 | fn get_exact_returns_error_on_invalid_provider() { 332 | struct BadProvider; 333 | impl Provider for BadProvider { 334 | fn result(&self) -> ServiceInfo { 335 | ServiceInfo::of::() 336 | } 337 | 338 | fn provide( 339 | &mut self, 340 | _injector: &Injector, 341 | _request_info: &RequestInfo, 342 | ) -> InjectResult { 343 | Ok(Svc::new(1.2_f32)) 344 | } 345 | } 346 | 347 | let mut builder = Injector::builder(); 348 | builder.provide(BadProvider); 349 | 350 | let injector = builder.build(); 351 | let bad: InjectResult> = injector.get(); 352 | 353 | match bad { 354 | Err(InjectError::InvalidProvider { service_info }) 355 | if service_info == ServiceInfo::of::() => {} 356 | Err(error) => Err(error).unwrap(), 357 | Ok(value) => { 358 | panic!("Value of {} was provided by an invalid provider", value) 359 | } 360 | } 361 | } 362 | } 363 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/iter.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectError, InjectResult, Injector, Interface, MapContainer, 3 | MapContainerEx, Provider, ProviderMap, RequestInfo, ServiceInfo, Svc, 4 | }; 5 | use std::{marker::PhantomData, slice::IterMut}; 6 | 7 | /// A collection of all the providers for a particular interface. 8 | /// 9 | /// If an interface will only have one implementation registered for it, then 10 | /// it may be easier to request [`Svc`] from the container instead. However, 11 | /// if multiple implementations are registered (or no implementations are 12 | /// registered), then this will allow all of those implementations to be 13 | /// iterated over. 14 | /// 15 | /// An iterator over all the implementations of an interface. Each service is 16 | /// activated on demand. 17 | /// 18 | /// ``` 19 | /// use runtime_injector::{ 20 | /// interface, Injector, IntoTransient, Services, Svc, TypedProvider, Service 21 | /// }; 22 | /// 23 | /// trait Fooable: Service { 24 | /// fn baz(&self) {} 25 | /// } 26 | /// 27 | /// interface!(dyn Fooable = [Foo, Bar]); 28 | /// 29 | /// #[derive(Default)] 30 | /// struct Foo; 31 | /// impl Fooable for Foo {} 32 | /// 33 | /// #[derive(Default)] 34 | /// struct Bar; 35 | /// impl Fooable for Bar {} 36 | /// 37 | /// let mut builder = Injector::builder(); 38 | /// builder.provide(Foo::default.transient().with_interface::()); 39 | /// builder.provide(Bar::default.transient().with_interface::()); 40 | /// 41 | /// let injector = builder.build(); 42 | /// let mut counter = 0; 43 | /// let mut fooables: Services = injector.get().unwrap(); 44 | /// for foo in fooables.get_all() { 45 | /// counter += 1; 46 | /// foo.unwrap().baz(); 47 | /// } 48 | /// 49 | /// assert_eq!(2, counter); 50 | /// ``` 51 | pub struct Services { 52 | injector: Injector, 53 | service_info: ServiceInfo, 54 | request_info: RequestInfo, 55 | provider_map: MapContainer, 56 | providers: Option>>, 57 | marker: PhantomData I>, 58 | } 59 | 60 | impl Services { 61 | pub(crate) fn new( 62 | injector: Injector, 63 | provider_map: MapContainer, 64 | request_info: RequestInfo, 65 | ) -> InjectResult { 66 | let service_info = ServiceInfo::of::(); 67 | let providers = provider_map.with_inner_mut(|provider_map| { 68 | let providers = provider_map 69 | .get_mut(&service_info) 70 | .ok_or(InjectError::MissingProvider { service_info })?; 71 | 72 | providers.take().ok_or_else(|| InjectError::CycleDetected { 73 | service_info, 74 | cycle: vec![service_info], 75 | }) 76 | })?; 77 | 78 | Ok(Services { 79 | injector, 80 | service_info, 81 | request_info, 82 | provider_map, 83 | providers: Some(providers), 84 | marker: PhantomData, 85 | }) 86 | } 87 | 88 | /// Lazily gets all the implementations of this interface. Each service 89 | /// will be requested on demand rather than all at once. 90 | #[allow(clippy::missing_panics_doc)] 91 | pub fn get_all(&mut self) -> ServicesIter<'_, I> { 92 | ServicesIter { 93 | provider_iter: self.providers.as_mut().unwrap().iter_mut(), /* Should never panic */ 94 | injector: &self.injector, 95 | request_info: &self.request_info, 96 | marker: PhantomData, 97 | } 98 | } 99 | 100 | /// Lazily gets all the implementations of this interface as owned service 101 | /// pointers. Each service will be requested on demand rather than all at 102 | /// once. Not all providers can provide owned service pointers, so some 103 | /// requests may fail. 104 | #[allow(clippy::missing_panics_doc)] 105 | pub fn get_all_owned(&mut self) -> OwnedServicesIter<'_, I> { 106 | OwnedServicesIter { 107 | provider_iter: self.providers.as_mut().unwrap().iter_mut(), /* Should never panic */ 108 | injector: &self.injector, 109 | request_info: &self.request_info, 110 | marker: PhantomData, 111 | } 112 | } 113 | 114 | /// Gets the max number of possible implementations of this interface. This 115 | /// does not take into account conditional providers, which may not return 116 | /// an implementation of the service. 117 | #[must_use] 118 | #[allow(clippy::missing_panics_doc)] 119 | pub fn len(&self) -> usize { 120 | // Should never panic 121 | self.providers.as_ref().unwrap().len() 122 | } 123 | 124 | /// Returns `true` if there are no possible implementations of this 125 | /// interface. This does not take into account conditional providers, which 126 | /// may not return an implementation of the service. 127 | #[must_use] 128 | #[allow(clippy::missing_panics_doc)] 129 | pub fn is_empty(&self) -> bool { 130 | // Should never panic 131 | self.providers.as_ref().unwrap().is_empty() 132 | } 133 | } 134 | 135 | impl Drop for Services { 136 | fn drop(&mut self) { 137 | let Services { 138 | ref service_info, 139 | ref mut provider_map, 140 | ref mut providers, 141 | .. 142 | } = self; 143 | 144 | let result = provider_map.with_inner_mut(|provider_map| { 145 | let provider_entry = 146 | provider_map.get_mut(service_info).ok_or_else(|| { 147 | InjectError::InternalError(format!( 148 | "activated provider for {} is no longer registered", 149 | service_info.name() 150 | )) 151 | })?; 152 | 153 | // Should never panic 154 | #[allow(clippy::missing_panics_doc)] 155 | if provider_entry.replace(providers.take().unwrap()).is_some() { 156 | Err(InjectError::InternalError(format!( 157 | "another provider for {} was added during its activation", 158 | service_info.name() 159 | ))) 160 | } else { 161 | Ok(()) 162 | } 163 | }); 164 | 165 | if let Err(error) = result { 166 | eprintln!( 167 | "An error occurred while releasing providiers for {}: {}", 168 | service_info.name(), 169 | error 170 | ); 171 | } 172 | } 173 | } 174 | 175 | /// An iterator over all the implementations of an interface. Each service is 176 | /// activated on demand. 177 | /// 178 | /// ``` 179 | /// use runtime_injector::{constant, Injector, IntoTransient, Services, Svc}; 180 | /// use std::sync::Mutex; 181 | /// 182 | /// struct Foo; 183 | /// 184 | /// fn make_foo(counter: Svc>) -> Foo { 185 | /// // Increment the counter to track how many Foos have been created 186 | /// let mut counter = counter.lock().unwrap(); 187 | /// *counter += 1; 188 | /// Foo 189 | /// } 190 | /// 191 | /// let mut builder = Injector::builder(); 192 | /// builder.provide(constant(Mutex::new(0usize))); 193 | /// builder.provide(make_foo.transient()); 194 | /// 195 | /// let injector = builder.build(); 196 | /// let counter: Svc> = injector.get().unwrap(); 197 | /// let mut foos: Services = injector.get().unwrap(); 198 | /// 199 | /// let mut iter = foos.get_all(); 200 | /// assert_eq!(0, *counter.lock().unwrap()); 201 | /// assert!(iter.next().is_some()); 202 | /// assert_eq!(1, *counter.lock().unwrap()); 203 | /// assert!(iter.next().is_none()); 204 | /// ``` 205 | pub struct ServicesIter<'a, I: ?Sized + Interface> { 206 | provider_iter: IterMut<'a, Box>, 207 | injector: &'a Injector, 208 | request_info: &'a RequestInfo, 209 | marker: PhantomData I>, 210 | } 211 | 212 | impl<'a, I: ?Sized + Interface> Iterator for ServicesIter<'a, I> { 213 | type Item = InjectResult>; 214 | 215 | fn next(&mut self) -> Option { 216 | let ServicesIter { 217 | provider_iter, 218 | injector, 219 | request_info, 220 | .. 221 | } = self; 222 | 223 | provider_iter.find_map(|provider| { 224 | match provider.provide(injector, request_info) { 225 | Ok(result) => Some(I::downcast(result)), 226 | Err(InjectError::ConditionsNotMet { .. }) => None, 227 | Err(InjectError::CycleDetected { mut cycle, .. }) => { 228 | let service_info = ServiceInfo::of::(); 229 | cycle.push(service_info); 230 | Some(Err(InjectError::CycleDetected { 231 | service_info, 232 | cycle, 233 | })) 234 | } 235 | Err(error) => Some(Err(error)), 236 | } 237 | }) 238 | } 239 | 240 | fn size_hint(&self) -> (usize, Option) { 241 | (0, Some(self.provider_iter.len())) 242 | } 243 | } 244 | 245 | /// An iterator over all the implementations of an interface. Each service is 246 | /// activated on demand. 247 | /// 248 | /// ``` 249 | /// use runtime_injector::{constant, Injector, IntoTransient, Services, Svc}; 250 | /// use std::sync::Mutex; 251 | /// 252 | /// #[derive(Clone, Copy, PartialEq, Eq, Debug)] 253 | /// struct Foo(usize); 254 | /// 255 | /// fn make_foo(counter: Svc>) -> Foo { 256 | /// // Increment the counter to track how many Foos have been created 257 | /// let mut counter = counter.lock().unwrap(); 258 | /// *counter += 1; 259 | /// Foo(*counter) 260 | /// } 261 | /// 262 | /// let mut builder = Injector::builder(); 263 | /// builder.provide(constant(Mutex::new(0usize))); 264 | /// builder.provide(make_foo.transient()); 265 | /// 266 | /// let injector = builder.build(); 267 | /// let counter: Svc> = injector.get().unwrap(); 268 | /// let mut foos: Services = injector.get().unwrap(); 269 | /// 270 | /// let mut iter = foos.get_all_owned(); 271 | /// assert_eq!(0, *counter.lock().unwrap()); 272 | /// assert_eq!(Foo(1), *iter.next().unwrap().unwrap()); 273 | /// assert_eq!(1, *counter.lock().unwrap()); 274 | /// assert!(iter.next().is_none()); 275 | /// ``` 276 | pub struct OwnedServicesIter<'a, I: ?Sized + Interface> { 277 | provider_iter: IterMut<'a, Box>, 278 | injector: &'a Injector, 279 | request_info: &'a RequestInfo, 280 | marker: PhantomData I>, 281 | } 282 | 283 | impl<'a, I: ?Sized + Interface> Iterator for OwnedServicesIter<'a, I> { 284 | type Item = InjectResult>; 285 | 286 | fn next(&mut self) -> Option { 287 | let OwnedServicesIter { 288 | provider_iter, 289 | injector, 290 | request_info, 291 | .. 292 | } = self; 293 | 294 | provider_iter.find_map(|provider| { 295 | match provider.provide_owned(injector, request_info) { 296 | Ok(result) => Some(I::downcast_owned(result)), 297 | Err(InjectError::ConditionsNotMet { .. }) => None, 298 | Err(InjectError::CycleDetected { mut cycle, .. }) => { 299 | let service_info = ServiceInfo::of::(); 300 | cycle.push(service_info); 301 | Some(Err(InjectError::CycleDetected { 302 | service_info, 303 | cycle, 304 | })) 305 | } 306 | Err(error) => Some(Err(error)), 307 | } 308 | }) 309 | } 310 | 311 | fn size_hint(&self) -> (usize, Option) { 312 | (0, Some(self.provider_iter.len())) 313 | } 314 | } 315 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! # Runtime dependency injection. 2 | //! 3 | //! By default, services provided by the [`Injector`] use thread-safe pointers. 4 | //! This is because [`Arc`](std::sync::Arc) is used to hold instances of the 5 | //! services. This can be changed to [`Rc`](std::rc::Rc) by disabling 6 | //! default features and enabling the "rc" feature: 7 | //! 8 | //! ```text 9 | //! [dependencies.runtime_injector] 10 | //! version = "*" # Replace with the version you want to use 11 | //! default-features = false 12 | //! features = ["rc"] 13 | //! ``` 14 | //! 15 | //! ## Getting started 16 | //! 17 | //! If you are unfamiliar with dependency injection, then you may want to check 18 | //! about how a container can help 19 | //! [simplify your application][ioc]. Otherwise, 20 | //! check out the [getting started guide][getting-started] 21 | //! 22 | //! [ioc]: crate::docs::inversion_of_control 23 | //! [getting-started]: crate::docs::getting_started 24 | //! 25 | //! ## Dependency injection at runtime (rather than compile-time) 26 | //! 27 | //! Runtime dependency injection allows for advanced configuration of services 28 | //! during runtime rather than needing to decide what services your application 29 | //! will use at compile time. This means you can read a config when your 30 | //! application starts, decide what implementations you want to use for your 31 | //! interfaces, and assign those at runtime. This is also slightly slower than 32 | //! compile-time dependency injection, so if pointer indirection, dynamic 33 | //! dispatch, or heap allocations are a concern, then a compile-time dependency 34 | //! injection library might work better instead. However, in asynchronous, 35 | //! I/O-based applications like a web server, the additional overhead is 36 | //! probably insignificant compared to the additional flexibility you get with 37 | //! runtime_injector. 38 | //! 39 | //! ## Interfaces 40 | //! 41 | //! Using interfaces allows you to write your services without worrying about 42 | //! how its dependencies are implemented. You can think of them like generic 43 | //! type parameters for your service, except rather than needing to add a new 44 | //! type parameter, you use a service pointer to the interface for your 45 | //! dependency. This makes your code easier to read and faster to write, and 46 | //! keeps your services decoupled from their dependencies and dependents. 47 | //! 48 | //! Interfaces are implemented as trait objects in runtime_injector. For 49 | //! instance, you may define a trait `UserDatabase` and implement it for 50 | //! several different types. [`Svc`](crate::Svc) is a 51 | //! reference-counted service pointer to an implementation of your trait. 52 | //! Similarly, `dyn UserDatabase` is your interface. You can read more about 53 | //! how interfaces work and how they're created in the 54 | //! [type-level docs](crate::Interface). 55 | //! 56 | //! ## Service lifetimes 57 | //! 58 | //! Lifetimes of services created by the [`Injector`] are controlled by the 59 | //! [`Provider`] used to construct those lifetimes. Currently, there are three 60 | //! built-in service provider types: 61 | //! 62 | //! - **[Transient](crate::TransientProvider):** A service is created each time 63 | //! it is requested. This will never return the same instance of a service 64 | //! more than once. 65 | //! - **[Singleton](crate::SingletonProvider):** A service is created only the 66 | //! first time it is requested, then that single instance is reused for each 67 | //! future request. 68 | //! - **[Constant](crate::ConstantProvider):** Used for services that are not 69 | //! created using a service factory and instead can have their instance 70 | //! provided to the container directly. This behaves similar to singleton in 71 | //! that the same instance is provided each time the service is requested. 72 | //! 73 | //! Custom service providers can also be created by implementing either the 74 | //! [`TypedProvider`] or [`Provider`] trait. 75 | //! 76 | //! ## Fallible service factories 77 | //! 78 | //! Not all types can always be successfully created. Sometimes, creating an 79 | //! instance of a service might fail. Rather than panicking on error, it's 80 | //! possible to instead return a [`Result`] from your constructors and 81 | //! inject the result as a [`Svc`]. Read more in the 82 | //! [docs for `IntoFallible`](crate::IntoFallible). 83 | //! 84 | //! ## Owned service pointers 85 | //! 86 | //! In general, providers need to be able to provide their services via 87 | //! reference-counted service pointers, or [`Svc`]. The issue with this is 88 | //! that you cannot get mutable or owned access to the contents of those 89 | //! pointers since they are shared pointers. As a result, you may need to clone 90 | //! some dependencies in your constructors if you want to be able to own them. 91 | //! 92 | //! If your dependency is a transient service, then it might make more sense 93 | //! to inject it as a [`Box`] than clone it from a reference-counted service 94 | //! pointer. In these cases, you can request a [`Box`] directly from the 95 | //! injector and avoid needing to clone your dependency entirely! 96 | //! 97 | //! ## Custom target-specific arguments 98 | //! 99 | //! Sometimes it's useful to be able to pass a specific value into your 100 | //! services. For example, if you're writing a database service and you need a 101 | //! connection string, you could define a new `ConnectionString` struct as a 102 | //! newtype for [`String`], but that would be a bit excessive for passing in a 103 | //! single value. If you had several arguments you needed to pass in this way, 104 | //! then that would mean you would need a new type for each one. 105 | //! 106 | //! Rather than creating a bunch of newtypes, you can use [`Arg`] to pass in 107 | //! pre-defined values directly to your services. For example, you can use 108 | //! `Arg` to pass in your connection string, plus you can use 109 | //! `Arg` to set the max size of your connection pool, and another 110 | //! `Arg` in your logging service to set your logging format without 111 | //! needing to worry about accidentally using your connection string as your 112 | //! logging format! 113 | //! 114 | //! ## Example 115 | //! 116 | //! ``` 117 | //! use runtime_injector::{ 118 | //! define_module, Module, interface, Injector, Svc, IntoSingleton, 119 | //! TypedProvider, IntoTransient, constant, Service 120 | //! }; 121 | //! use std::error::Error; 122 | //! 123 | //! // Some type that represents a user 124 | //! struct User; 125 | //! 126 | //! // This is our interface. In practice, multiple structs can implement this 127 | //! // trait, and we don't care what the concrete type is most of the time in 128 | //! // our other services as long as it implements this trait. Because of this, 129 | //! // we're going to use dynamic dispatch later so that we can determine the 130 | //! // concrete type at runtime (vs. generics, which are determined instead at 131 | //! // compile time). 132 | //! // 133 | //! // Since all implementations of this interface must be services for them to 134 | //! // be injected, we'll add that as a supertrait of `DataService`. With the 135 | //! // "arc" feature enabled, this means that implementations must be `Send`, 136 | //! // `Sync`, and `Any` (or 'static), but with the "rc" feature enabled this 137 | //! // only requires `Any`. 138 | //! trait DataService: Service { 139 | //! fn get_user(&self, user_id: &str) -> Option; 140 | //! } 141 | //! 142 | //! // We can use a data service which connects to a SQL database. 143 | //! #[derive(Default)] 144 | //! struct SqlDataService; 145 | //! impl DataService for SqlDataService { 146 | //! fn get_user(&self, _user_id: &str) -> Option { todo!() } 147 | //! } 148 | //! 149 | //! // ... Or we can mock out the data service entirely! 150 | //! #[derive(Default)] 151 | //! struct MockDataService; 152 | //! impl DataService for MockDataService { 153 | //! fn get_user(&self, _user_id: &str) -> Option { Some(User) } 154 | //! } 155 | //! 156 | //! // Specify which types implement the DataService interface. This does not 157 | //! // determine the actual implementation used. It only registers the types as 158 | //! // possible implementations of the DataService interface. 159 | //! interface!(dyn DataService = [SqlDataService, MockDataService]); 160 | //! 161 | //! // Here's another service our application uses. This service depends on our 162 | //! // data service, however it doesn't care how that service is actually 163 | //! // implemented as long as it works. Because of that, we're using dynamic 164 | //! // dispatch to allow the implementation to be determined at runtime. 165 | //! struct UserService { 166 | //! data_service: Svc, 167 | //! } 168 | //! 169 | //! impl UserService { 170 | //! // This is just a normal constructor. The only requirement is that each 171 | //! // parameter is a valid injectable dependency. 172 | //! pub fn new(data_service: Svc) -> Self { 173 | //! UserService { data_service } 174 | //! } 175 | //! 176 | //! pub fn get_user(&self, user_id: &str) -> Option { 177 | //! // UserService doesn't care how the user is actually retrieved 178 | //! self.data_service.get_user(user_id) 179 | //! } 180 | //! } 181 | //! 182 | //! fn main() -> Result<(), Box> { 183 | //! // This is where we register our services. Each call to `.provide` adds 184 | //! // a new service provider to our container, however nothing is actually 185 | //! // created until it is requested. This means we can add providers for 186 | //! // types we aren't actually going to use without worrying about 187 | //! // constructing instances of those types that we aren't actually using. 188 | //! let mut builder = Injector::builder(); 189 | //! 190 | //! // We can manually add providers to our builder 191 | //! builder.provide(UserService::new.singleton()); 192 | //! 193 | //! struct Foo(Svc); 194 | //! 195 | //! // Alternatively, modules can be used to group providers and 196 | //! // configurations together, and can be defined via the 197 | //! // define_module! macro 198 | //! let module = define_module! { 199 | //! services = [ 200 | //! // Simple tuple structs can be registered as services directly without 201 | //! // defining any additional constructors 202 | //! Foo.singleton(), 203 | //! 204 | //! // Note that we can register closures as providers as well 205 | //! (|_: Svc| "Hello, world!").singleton(), 206 | //! (|_: Option>| 120.9).transient(), 207 | //! 208 | //! // Since we know our dependency is transient, we can request an 209 | //! // owned pointer to it rather than a reference-counted pointer 210 | //! (|value: Box| format!("{}", value)).transient(), 211 | //! 212 | //! // We can also provide constant values directly to our services 213 | //! constant(8usize), 214 | //! ], 215 | //! interfaces = { 216 | //! // Let's choose to use the MockDataService as our data service 217 | //! dyn DataService = [MockDataService::default.singleton()], 218 | //! }, 219 | //! }; 220 | //! 221 | //! // You can easily add a module to your builder 222 | //! builder.add_module(module); 223 | //! 224 | //! // Now that we've registered all our providers and implementations, we 225 | //! // can start relying on our container to create our services for us! 226 | //! let injector = builder.build(); 227 | //! let user_service: Svc = injector.get()?; 228 | //! let _user = user_service.get_user("john"); 229 | //! 230 | //! Ok(()) 231 | //! } 232 | //! ``` 233 | 234 | #![forbid(unsafe_code)] 235 | #![deny(clippy::all, clippy::pedantic)] 236 | #![warn(missing_docs)] 237 | #![allow( 238 | clippy::module_name_repetitions, 239 | clippy::missing_errors_doc, 240 | clippy::doc_markdown, 241 | clippy::needless_doctest_main, 242 | clippy::needless_pass_by_value 243 | )] 244 | 245 | #[cfg(not(any(feature = "arc", feature = "rc")))] 246 | compile_error!( 247 | "Either the 'arc' or 'rc' feature must be enabled (but not both)." 248 | ); 249 | 250 | #[cfg(all(feature = "arc", feature = "rc"))] 251 | compile_error!( 252 | "The 'arc' and 'rc' features are mutually exclusive and cannot be enabled together." 253 | ); 254 | 255 | mod any; 256 | mod builder; 257 | mod injector; 258 | mod iter; 259 | mod module; 260 | mod requests; 261 | mod services; 262 | 263 | pub use any::*; 264 | pub use builder::*; 265 | pub use injector::*; 266 | pub use iter::*; 267 | pub use module::*; 268 | pub use requests::*; 269 | pub use services::*; 270 | 271 | pub mod docs; 272 | 273 | #[cfg(test)] 274 | mod tests; 275 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/module.rs: -------------------------------------------------------------------------------- 1 | use crate::{Provider, ProviderMap, RequestParameter}; 2 | use std::collections::HashMap; 3 | 4 | /// A collection of providers that can be added all at once to an 5 | /// [`InjectorBuilder`](crate::InjectorBuilder). Modules can be used to group 6 | /// together related services and configure the injector in pieces rather than 7 | /// all at once. 8 | /// 9 | /// For creating a module easily via a domain specific language, see 10 | /// [`define_module!`]. 11 | #[derive(Default)] 12 | pub struct Module { 13 | pub(crate) providers: ProviderMap, 14 | pub(crate) parameters: HashMap>, 15 | } 16 | 17 | impl Module { 18 | /// Assigns the provider for a service type. Multiple providers can be 19 | /// registered for a service. 20 | #[allow(clippy::missing_panics_doc)] 21 | pub fn provide(&mut self, provider: P) { 22 | // Should never panic 23 | self.providers 24 | .entry(provider.result()) 25 | .or_insert_with(|| Some(Vec::new())) 26 | .as_mut() 27 | .unwrap() 28 | .push(Box::new(provider)); 29 | } 30 | 31 | /// Sets the of a value request parameter for requests made by the injector 32 | /// this module is added to. If a parameter has already been set to a 33 | /// value in this module, then that value is returned. 34 | pub fn insert_parameter( 35 | &mut self, 36 | key: &str, 37 | value: impl RequestParameter, 38 | ) -> Option> { 39 | self.parameters.insert(key.to_owned(), Box::new(value)) 40 | } 41 | 42 | /// Removes and returns the value of a parameter if it has been set. 43 | pub fn remove_parameter( 44 | &mut self, 45 | key: &str, 46 | ) -> Option> { 47 | self.parameters.remove(key) 48 | } 49 | } 50 | 51 | /// Defines a new module using a domain specific language. 52 | /// 53 | /// ## Example 54 | /// 55 | /// ``` 56 | /// use runtime_injector::{ 57 | /// define_module, interface, Arg, Injector, IntoSingleton, IntoTransient, 58 | /// Service, Svc, 59 | /// }; 60 | /// 61 | /// struct Foo(Arg); 62 | /// struct Bar(); 63 | /// struct Baz(Vec>); 64 | /// #[cfg(test)] 65 | /// struct Quux(); 66 | /// 67 | /// trait Fooable: Service {} 68 | /// impl Fooable for Foo {} 69 | /// impl Fooable for Bar {} 70 | /// interface! { 71 | /// dyn Fooable = [ 72 | /// Foo, 73 | /// Bar, 74 | /// #[cfg(test)] 75 | /// Quux, 76 | /// ] 77 | /// }; 78 | /// 79 | /// let module = define_module! { 80 | /// services = [ 81 | /// Baz.singleton(), 82 | /// ], 83 | /// interfaces = { 84 | /// dyn Fooable = [ 85 | /// Foo.singleton(), 86 | /// Bar.singleton(), 87 | /// ], 88 | /// }, 89 | /// arguments = { 90 | /// Foo = [12i32], 91 | /// }, 92 | /// 93 | /// // If there are multiple interface or service definitions, they are 94 | /// // merged together. This means we can have providers registered only in 95 | /// // certain environments. 96 | /// #[cfg(test)] 97 | /// interfaces = { 98 | /// dyn Fooable = [ 99 | /// Quux.singleton(), 100 | /// ], 101 | /// }, 102 | /// }; 103 | /// 104 | /// let mut builder = Injector::builder(); 105 | /// builder.add_module(module); 106 | /// 107 | /// let injector = builder.build(); 108 | /// let baz: Svc = injector.get().unwrap(); 109 | /// 110 | /// #[cfg(not(test))] 111 | /// assert_eq!(2, baz.0.len()); 112 | /// #[cfg(test)] 113 | /// assert_eq!(3, baz.0.len()); 114 | /// ``` 115 | #[macro_export] 116 | macro_rules! define_module { 117 | { 118 | $( 119 | $(#[$($attr:meta),*])* 120 | $key:ident = $value:tt 121 | ),* 122 | $(,)? 123 | } => { 124 | { 125 | #[allow(unused_mut)] 126 | let mut module = <$crate::Module as ::std::default::Default>::default(); 127 | $( 128 | $(#[$($attr),*])* 129 | $crate::define_module!(@provide &mut module, $key = $value); 130 | )* 131 | module 132 | } 133 | }; 134 | ( 135 | @provide $module:expr, 136 | services = [ 137 | $($service:expr),* 138 | $(,)? 139 | ] 140 | ) => { 141 | $($module.provide($service);)* 142 | }; 143 | ( 144 | @provide $module:expr, 145 | interfaces = { 146 | $($interface:ty = [ 147 | $($implementation:expr),* 148 | $(,)? 149 | ]),* 150 | $(,)? 151 | } 152 | ) => { 153 | $( 154 | $($module.provide($crate::TypedProvider::with_interface::<$interface>($implementation));)* 155 | )* 156 | }; 157 | ( 158 | @provide $module:expr, 159 | arguments = { 160 | $($service:ty = [ 161 | $($arg:expr),* 162 | $(,)? 163 | ]),* 164 | $(,)? 165 | } 166 | ) => { 167 | $( 168 | $($crate::WithArg::with_arg::<$service, _>($module, $arg);)* 169 | )* 170 | }; 171 | } 172 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests.rs: -------------------------------------------------------------------------------- 1 | mod arg; 2 | mod factory; 3 | mod info; 4 | mod parameter; 5 | mod request; 6 | 7 | pub use arg::*; 8 | pub use factory::*; 9 | pub use info::*; 10 | pub use parameter::*; 11 | pub use request::*; 12 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests/arg.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | AsAny, InjectError, InjectResult, Injector, InjectorBuilder, Module, 3 | Request, RequestInfo, RequestParameter, Service, ServiceInfo, 4 | }; 5 | use std::{ 6 | error::Error, 7 | fmt::{Debug, Display, Formatter}, 8 | ops::{Deref, DerefMut}, 9 | }; 10 | 11 | /// Allows custom pre-defined values to be passed as arguments to services. 12 | /// 13 | /// ## Example 14 | /// 15 | /// ``` 16 | /// use runtime_injector::{Arg, Injector, IntoTransient, WithArg}; 17 | /// 18 | /// struct Foo(Arg); 19 | /// 20 | /// let mut builder = Injector::builder(); 21 | /// builder.provide(Foo.transient()); 22 | /// builder.with_arg::(12); 23 | /// 24 | /// let injector = builder.build(); 25 | /// let foo: Box = injector.get().unwrap(); 26 | /// assert_eq!(12, *foo.0); 27 | /// ``` 28 | pub struct Arg(T); 29 | 30 | impl Arg { 31 | pub(crate) fn param_name(target: ServiceInfo) -> String { 32 | format!( 33 | "runtime_injector::Arg[target={:?},type={:?}]", 34 | target.id(), 35 | ServiceInfo::of::().id() 36 | ) 37 | } 38 | 39 | /// Converts an argument into its inner value. 40 | pub fn into_inner(arg: Self) -> T { 41 | arg.0 42 | } 43 | } 44 | 45 | impl Deref for Arg { 46 | type Target = T; 47 | 48 | fn deref(&self) -> &Self::Target { 49 | &self.0 50 | } 51 | } 52 | 53 | impl DerefMut for Arg { 54 | fn deref_mut(&mut self) -> &mut Self::Target { 55 | &mut self.0 56 | } 57 | } 58 | 59 | /// Allows custom pre-defined values to be passed as arguments to services. 60 | impl Request for Arg { 61 | fn request(_injector: &Injector, info: &RequestInfo) -> InjectResult { 62 | let parent_request = info.service_path().last().ok_or_else(|| { 63 | InjectError::ActivationFailed { 64 | service_info: ServiceInfo::of::(), 65 | inner: Box::new(ArgRequestError::NoParentRequest), 66 | } 67 | })?; 68 | 69 | let request_name = Self::param_name(*parent_request); 70 | let param = info.get_parameter(&request_name).ok_or_else(|| { 71 | InjectError::ActivationFailed { 72 | service_info: ServiceInfo::of::(), 73 | inner: Box::new(ArgRequestError::MissingParameter), 74 | } 75 | })?; 76 | 77 | let param: &T = param.downcast_ref().ok_or_else(|| { 78 | InjectError::ActivationFailed { 79 | service_info: ServiceInfo::of::(), 80 | inner: Box::new(ArgRequestError::ParameterTypeInvalid), 81 | } 82 | })?; 83 | 84 | Ok(Arg(param.clone())) 85 | } 86 | } 87 | 88 | /// An error occurred while injecting an instance of [`Arg`]. 89 | #[derive(Debug)] 90 | pub enum ArgRequestError { 91 | /// The argument value was not provided. 92 | MissingParameter, 93 | /// The argument value is the wrong type. This should never happen. 94 | ParameterTypeInvalid, 95 | /// There is no parent request. 96 | NoParentRequest, 97 | } 98 | 99 | impl Error for ArgRequestError {} 100 | 101 | impl Display for ArgRequestError { 102 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 103 | match self { 104 | ArgRequestError::MissingParameter => { 105 | write!(f, "no value assigned for this argument") 106 | } 107 | ArgRequestError::ParameterTypeInvalid => { 108 | write!(f, "argument value is the wrong type") 109 | } 110 | ArgRequestError::NoParentRequest => { 111 | write!(f, "no parent request was found") 112 | } 113 | } 114 | } 115 | } 116 | 117 | /// Allows defining pre-defined arguments to services. 118 | pub trait WithArg { 119 | /// Adds an argument for a service. See the docs for [`Arg`]. 120 | fn with_arg( 121 | &mut self, 122 | value: T, 123 | ) -> Option>; 124 | } 125 | 126 | impl WithArg for RequestInfo { 127 | fn with_arg( 128 | &mut self, 129 | value: T, 130 | ) -> Option> { 131 | self.insert_parameter( 132 | &Arg::::param_name(ServiceInfo::of::()), 133 | value, 134 | ) 135 | } 136 | } 137 | 138 | impl WithArg for InjectorBuilder { 139 | fn with_arg( 140 | &mut self, 141 | value: T, 142 | ) -> Option> { 143 | self.root_info_mut().with_arg::(value) 144 | } 145 | } 146 | 147 | impl WithArg for Module { 148 | fn with_arg( 149 | &mut self, 150 | value: T, 151 | ) -> Option> { 152 | self.insert_parameter( 153 | &Arg::::param_name(ServiceInfo::of::()), 154 | value, 155 | ) 156 | } 157 | } 158 | 159 | #[cfg(test)] 160 | mod tests { 161 | use crate::{ 162 | define_module, Arg, ArgRequestError, InjectError, Injector, 163 | IntoSingleton, ServiceInfo, Svc, 164 | }; 165 | 166 | #[test] 167 | fn request_fails_if_missing_arg() { 168 | struct Foo(Arg); 169 | 170 | let module = define_module! { 171 | services = [Foo.singleton()], 172 | }; 173 | 174 | let mut builder = Injector::builder(); 175 | builder.add_module(module); 176 | 177 | let injector = builder.build(); 178 | match injector.get::>() { 179 | Ok(_) => unreachable!("request should have failed"), 180 | Err(InjectError::ActivationFailed { 181 | service_info, 182 | inner, 183 | }) => { 184 | assert_eq!(ServiceInfo::of::>(), service_info); 185 | let inner: &ArgRequestError = 186 | inner.downcast_ref().expect("failed to downcast error"); 187 | match inner { 188 | ArgRequestError::MissingParameter => {} 189 | inner => Err(inner).unwrap(), 190 | } 191 | } 192 | Err(error) => Err(error).unwrap(), 193 | } 194 | } 195 | 196 | #[test] 197 | fn request_fails_if_arg_has_no_parent_request() { 198 | let builder = Injector::builder(); 199 | let injector = builder.build(); 200 | match injector.get::>() { 201 | Ok(_) => unreachable!("request should have failed"), 202 | Err(InjectError::ActivationFailed { 203 | service_info, 204 | inner, 205 | }) => { 206 | assert_eq!(ServiceInfo::of::>(), service_info); 207 | let inner: &ArgRequestError = 208 | inner.downcast_ref().expect("failed to downcast error"); 209 | match inner { 210 | ArgRequestError::NoParentRequest => {} 211 | inner => Err(inner).unwrap(), 212 | } 213 | } 214 | Err(error) => Err(error).unwrap(), 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests/factory.rs: -------------------------------------------------------------------------------- 1 | use crate::{InjectResult, Injector, Request, RequestInfo}; 2 | use std::marker::PhantomData; 3 | 4 | /// Lazy request factory allowing requests to be made outside of service 5 | /// creation. 6 | /// 7 | /// This lets a service make requests to the injector outside of the service's 8 | /// constructor. For instance, the service can make requests for one of its 9 | /// dependencies at any time, and as many times as it wants, outside of the 10 | /// service's constructor. 11 | /// 12 | /// [`Factory`] can be cloned, making it easy to specialize each request made 13 | /// by the factory as needed. 14 | /// 15 | /// ## Example 16 | /// 17 | /// ``` 18 | /// use runtime_injector::{ 19 | /// Factory, Injector, IntoSingleton, IntoTransient, Svc, 20 | /// }; 21 | /// 22 | /// #[derive(Default)] 23 | /// struct Foo; 24 | /// struct Bar(Factory>); 25 | /// 26 | /// let mut builder = Injector::builder(); 27 | /// builder.provide(Foo::default.transient()); 28 | /// builder.provide(Bar.singleton()); 29 | /// 30 | /// let injector = builder.build(); 31 | /// let bar: Svc = injector.get().unwrap(); 32 | /// let _foo1 = bar.0.get().unwrap(); 33 | /// let _foo2 = bar.0.get().unwrap(); 34 | /// // ... 35 | /// ``` 36 | pub struct Factory { 37 | injector: Injector, 38 | request_info: RequestInfo, 39 | marker: PhantomData R>, 40 | } 41 | 42 | impl Clone for Factory { 43 | fn clone(&self) -> Self { 44 | Factory { 45 | injector: self.injector.clone(), 46 | request_info: self.request_info.clone(), 47 | marker: PhantomData, 48 | } 49 | } 50 | } 51 | 52 | impl Factory { 53 | /// Performs the factory's inner request. 54 | pub fn get(&self) -> InjectResult { 55 | R::request(&self.injector, &self.request_info) 56 | } 57 | 58 | /// Gets this factory's inner [`RequestInfo`]. This request info is used by 59 | /// all requests the factory makes. 60 | #[must_use] 61 | pub fn request_info(&self) -> &RequestInfo { 62 | &self.request_info 63 | } 64 | 65 | /// Mutably gets this factory's inner [`RequestInfo`]. This request info is 66 | /// used by all requests the factory makes. 67 | /// 68 | /// Modifying this request info affects future requests the factory makes, 69 | /// meaning additional arguments can be added to requests prior to them 70 | /// being executed. Since the factory can be cloned, requests can be 71 | /// specialized by first cloning the factory, then modifying the 72 | /// [`RequestInfo`] on the clone and using it to make the request instead. 73 | /// 74 | /// ## Example 75 | /// 76 | /// ``` 77 | /// use runtime_injector::{ 78 | /// Arg, Factory, InjectResult, Injector, IntoSingleton, IntoTransient, 79 | /// Svc, WithArg, 80 | /// }; 81 | /// 82 | /// struct Foo(Arg); 83 | /// 84 | /// struct Bar(Factory>); 85 | /// impl Bar { 86 | /// fn get_foo(&self, arg: i32) -> InjectResult> { 87 | /// let mut factory = self.0.clone(); 88 | /// factory.request_info_mut().with_arg::(arg); 89 | /// factory.get() 90 | /// } 91 | /// } 92 | /// 93 | /// let mut builder = Injector::builder(); 94 | /// builder.provide(Foo.transient()); 95 | /// builder.provide(Bar.singleton()); 96 | /// 97 | /// let injector = builder.build(); 98 | /// let bar: Svc = injector.get().unwrap(); 99 | /// let foo1 = bar.get_foo(1).unwrap(); 100 | /// let foo2 = bar.get_foo(2).unwrap(); 101 | /// 102 | /// assert_eq!(1, *foo1.0); 103 | /// assert_eq!(2, *foo2.0); 104 | /// ``` 105 | #[must_use] 106 | pub fn request_info_mut(&mut self) -> &mut RequestInfo { 107 | &mut self.request_info 108 | } 109 | } 110 | 111 | /// Lazy request factory allowing requests to be made outside of service 112 | /// creation. 113 | impl Request for Factory { 114 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 115 | Ok(Factory { 116 | injector: injector.clone(), 117 | request_info: info.clone(), 118 | marker: PhantomData, 119 | }) 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests/info.rs: -------------------------------------------------------------------------------- 1 | use crate::{RequestParameter, ServiceInfo}; 2 | use std::{ 3 | collections::HashMap, 4 | fmt::{Debug, Formatter}, 5 | }; 6 | 7 | /// Information about an active request. 8 | #[derive(Clone)] 9 | pub struct RequestInfo { 10 | service_path: Vec, 11 | parameters: HashMap>, 12 | } 13 | 14 | impl RequestInfo { 15 | /// Creates a new, empty instance of [`RequestInfo`]. 16 | #[must_use] 17 | pub fn new() -> Self { 18 | RequestInfo { 19 | service_path: Vec::new(), 20 | parameters: HashMap::new(), 21 | } 22 | } 23 | 24 | /// Creates a new child instance of [`RequestInfo`] with the given service 25 | /// appended to the end of the request path. 26 | #[must_use] 27 | pub fn with_request(&self, service: ServiceInfo) -> Self { 28 | let mut child = self.clone(); 29 | child.service_path.push(service); 30 | child 31 | } 32 | 33 | /// Gets the current request path. This can be used to configure a service 34 | /// based on what it's being injected into. 35 | /// 36 | /// ## Example 37 | /// 38 | /// ``` 39 | /// use runtime_injector::{ 40 | /// Injector, IntoTransient, RequestInfo, ServiceInfo, Svc, 41 | /// }; 42 | /// 43 | /// struct Foo(pub Svc); 44 | /// struct Bar(pub Svc); 45 | /// struct Baz(pub i32); 46 | /// 47 | /// impl Baz { 48 | /// pub fn new(request_info: RequestInfo) -> Self { 49 | /// let service_path = request_info.service_path(); 50 | /// let value = match service_path.get(0) { 51 | /// Some(root) if root == &ServiceInfo::of::() => 1, 52 | /// Some(root) if root == &ServiceInfo::of::() => 2, 53 | /// _ => 0, 54 | /// }; 55 | /// 56 | /// Baz(value) 57 | /// } 58 | /// } 59 | /// 60 | /// let mut builder = Injector::builder(); 61 | /// builder.provide(Foo.transient()); 62 | /// builder.provide(Bar.transient()); 63 | /// builder.provide(Baz::new.transient()); 64 | /// 65 | /// let injector = builder.build(); 66 | /// let foo: Svc = injector.get().unwrap(); 67 | /// let bar: Svc = injector.get().unwrap(); 68 | /// let baz: Svc = injector.get().unwrap(); 69 | #[rustfmt::skip] 70 | /// assert_eq!(1, foo.0.0); 71 | /// assert_eq!(2, bar.0.0); 72 | /// assert_eq!(0, baz.0); 73 | /// ``` 74 | #[must_use] 75 | pub fn service_path(&self) -> &[ServiceInfo] { 76 | &self.service_path 77 | } 78 | 79 | /// Sets the value of a request parameter for the request. If a parameter 80 | /// has already been set to a value, then that value is returned. 81 | pub fn insert_parameter( 82 | &mut self, 83 | key: &str, 84 | value: impl RequestParameter, 85 | ) -> Option> { 86 | self.insert_parameter_boxed(key, Box::new(value)) 87 | } 88 | 89 | pub(crate) fn insert_parameter_boxed( 90 | &mut self, 91 | key: &str, 92 | value: Box, 93 | ) -> Option> { 94 | self.parameters.insert(key.to_owned(), value) 95 | } 96 | 97 | /// Removes and returns the value of a parameter if it has been set. 98 | pub fn remove_parameter( 99 | &mut self, 100 | key: &str, 101 | ) -> Option> { 102 | self.parameters.remove(key) 103 | } 104 | 105 | /// Gets the value of a parameter if it has been set. 106 | #[must_use] 107 | pub fn get_parameter(&self, key: &str) -> Option<&dyn RequestParameter> { 108 | self.parameters.get(key).map(AsRef::as_ref) 109 | } 110 | 111 | /// Mutably gets the value of a parameter if it has been set. 112 | #[must_use] 113 | pub fn get_parameter_mut( 114 | &mut self, 115 | key: &str, 116 | ) -> Option<&mut dyn RequestParameter> { 117 | self.parameters.get_mut(key).map(AsMut::as_mut) 118 | } 119 | } 120 | 121 | impl Default for RequestInfo { 122 | fn default() -> Self { 123 | RequestInfo::new() 124 | } 125 | } 126 | 127 | impl Debug for RequestInfo { 128 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 129 | // TODO: maybe use finish_non_exhaustive when 1.53 hits stable 130 | f.debug_struct("RequestInfo") 131 | .field("service_path", &self.service_path) 132 | .finish() 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests/parameter.rs: -------------------------------------------------------------------------------- 1 | use crate::{AsAny, Service}; 2 | 3 | /// A parameter for configuring requested services. 4 | pub trait RequestParameter: Service + AsAny { 5 | /// Clones this parameter into a boxed trait object. 6 | fn clone_dyn(&self) -> Box; 7 | } 8 | 9 | impl RequestParameter for T { 10 | fn clone_dyn(&self) -> Box { 11 | Box::new(self.clone()) 12 | } 13 | } 14 | 15 | impl dyn RequestParameter { 16 | /// Tries to downcast this request parameter to a concrete type. 17 | pub fn downcast_ref(&self) -> Option<&T> { 18 | self.as_any().downcast_ref() 19 | } 20 | } 21 | 22 | impl Clone for Box { 23 | fn clone(&self) -> Self { 24 | self.as_ref().clone_dyn() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/requests/request.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectError, InjectResult, Injector, Interface, RequestInfo, ServiceInfo, 3 | Services, Svc, 4 | }; 5 | 6 | /// A request to an injector. 7 | /// 8 | /// ## Grouping requests 9 | /// 10 | /// Requests can be grouped together by using tuples to make multiple requests 11 | /// at once. Since there is a limit of 12 supported parameters/dependencies for 12 | /// factories, tuples can also be used to get around that limitation. 13 | /// 14 | /// ``` 15 | /// use runtime_injector::{Injector, IntoSingleton, Svc}; 16 | /// 17 | /// struct Bar; 18 | /// struct Baz; 19 | /// struct Foo(Svc, Svc); 20 | /// 21 | /// impl Foo { 22 | /// pub fn new((bar, baz): (Svc, Svc)) -> Self { 23 | /// Foo(bar, baz) 24 | /// } 25 | /// } 26 | /// 27 | /// let mut builder = Injector::builder(); 28 | /// builder.provide(Foo::new.singleton()); 29 | /// 30 | /// let _injector = builder.build(); 31 | /// ``` 32 | /// 33 | /// ## Owned service requests 34 | /// 35 | /// Some services can be provided directly via owned pointers ([`Box`]). 36 | /// These services can be requested directly via [`Box`], as a collection 37 | /// via [`Vec>`](Vec), or lazily via an iterator via [`Services`]. 38 | /// 39 | /// ``` 40 | /// use runtime_injector::{define_module, Injector, IntoTransient, Svc}; 41 | /// 42 | /// #[derive(Default)] 43 | /// struct Foo; 44 | /// struct Bar(Box); 45 | /// 46 | /// let module = define_module! { 47 | /// services = [ 48 | /// Foo::default.transient(), 49 | /// Bar.transient(), 50 | /// ] 51 | /// }; 52 | /// 53 | /// let mut builder = Injector::builder(); 54 | /// builder.add_module(module); 55 | /// 56 | /// let injector = builder.build(); 57 | /// let _bar: Box = injector.get().unwrap(); 58 | /// ``` 59 | pub trait Request: Sized { 60 | /// Performs the request to the injector. 61 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult; 62 | } 63 | 64 | /// Requests the injector used to resolve services. 65 | impl Request for Injector { 66 | fn request(injector: &Injector, _info: &RequestInfo) -> InjectResult { 67 | Ok(injector.clone()) 68 | } 69 | } 70 | 71 | /// Requests the information about the current request. 72 | impl Request for RequestInfo { 73 | fn request(_injector: &Injector, info: &RequestInfo) -> InjectResult { 74 | Ok(info.clone()) 75 | } 76 | } 77 | 78 | /// Requests a service pointer to a service or interface. This request fails if 79 | /// there is not exactly one implementation of the given interface. 80 | impl Request for Svc { 81 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 82 | let mut services: Services = injector.get_with(info)?; 83 | if services.len() > 1 { 84 | Err(InjectError::MultipleProviders { 85 | service_info: ServiceInfo::of::(), 86 | providers: services.len(), 87 | }) 88 | } else { 89 | let service = services.get_all().next().transpose()?.ok_or( 90 | InjectError::MissingProvider { 91 | service_info: ServiceInfo::of::(), 92 | }, 93 | )?; 94 | 95 | Ok(service) 96 | } 97 | } 98 | } 99 | 100 | /// Requests an owned pointer to a service or interface. Not all providers can 101 | /// provide owned pointers to their service, so this may fail where [`Svc`] 102 | /// requests would otherwise succeed. 103 | impl Request for Box { 104 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 105 | let mut services: Services = injector.get_with(info)?; 106 | if services.len() > 1 { 107 | Err(InjectError::MultipleProviders { 108 | service_info: ServiceInfo::of::(), 109 | providers: services.len(), 110 | }) 111 | } else { 112 | let service = services.get_all_owned().next().transpose()?.ok_or( 113 | InjectError::MissingProvider { 114 | service_info: ServiceInfo::of::(), 115 | }, 116 | )?; 117 | 118 | Ok(service) 119 | } 120 | } 121 | } 122 | 123 | /// Lazily requests all the implementations of an interface. 124 | impl Request for Services { 125 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 126 | injector.get_service(info) 127 | } 128 | } 129 | 130 | /// Requests all the implementations of an interface. For sized types, this 131 | /// will return at most one implementation. If no provider is registered for 132 | /// the given interface, then this will return an empty [`Vec`]. 133 | impl Request for Vec> { 134 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 135 | let mut impls: Services = injector.get_with(info)?; 136 | impls.get_all().collect() 137 | } 138 | } 139 | 140 | /// Requests all the implementations of an interface as owned service pointers. 141 | /// If no provider is registered for the given interface, then this will return 142 | /// an empty [`Vec`]. If any provider cannot provide an owned service 143 | /// pointer, then an error is returned instead. 144 | impl Request for Vec> { 145 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 146 | let mut impls: Services = injector.get_with(info)?; 147 | impls.get_all_owned().collect() 148 | } 149 | } 150 | 151 | /// Tries to request a service pointer for a service or interface. If no 152 | /// provider has been registered for it, then returns `None`. This fails if 153 | /// there are multiple implementations of the given interface. 154 | impl Request for Option> { 155 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 156 | match injector.get_with(info) { 157 | Ok(response) => Ok(Some(response)), 158 | Err(InjectError::MissingProvider { .. }) => Ok(None), 159 | Err(error) => Err(error), 160 | } 161 | } 162 | } 163 | 164 | /// Tries to request an ownedservice pointer for a service or interface. If no 165 | /// provider has been registered for it, then returns `None`. This fails if 166 | /// there are multiple implementations of the given interface or if the service 167 | /// cannot be provided via an owned service pointer. 168 | impl Request for Option> { 169 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 170 | match injector.get_with(info) { 171 | Ok(response) => Ok(Some(response)), 172 | Err(InjectError::MissingProvider { .. }) => Ok(None), 173 | Err(error) => Err(error), 174 | } 175 | } 176 | } 177 | 178 | macro_rules! impl_tuple_request { 179 | () => { 180 | impl_tuple_request!(@impl ()); 181 | }; 182 | ($first:ident $(, $rest:ident)*) => { 183 | impl_tuple_request!(@impl ($first $(, $rest)*)); 184 | impl_tuple_request!($($rest),*); 185 | }; 186 | (@impl ($($type_name:ident),*)) => { 187 | /// Performs multiple requests at once. This is useful for grouping 188 | /// together related requests. 189 | impl <$($type_name),*> Request for ($($type_name,)*) 190 | where 191 | $($type_name: Request,)* 192 | { 193 | #[allow(unused_variables)] 194 | fn request(injector: &Injector, info: &RequestInfo) -> InjectResult { 195 | let result = ($(injector.get_with::<$type_name>(info)?,)*); 196 | Ok(result) 197 | } 198 | } 199 | }; 200 | } 201 | 202 | impl_tuple_request!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); 203 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services.rs: -------------------------------------------------------------------------------- 1 | mod conditional; 2 | mod constant; 3 | mod fallible; 4 | mod func; 5 | mod interface; 6 | mod providers; 7 | mod service; 8 | mod singleton; 9 | mod transient; 10 | 11 | pub use conditional::*; 12 | pub use constant::*; 13 | pub use fallible::*; 14 | pub use func::*; 15 | pub use interface::*; 16 | pub use providers::*; 17 | pub use service::*; 18 | pub use singleton::*; 19 | pub use transient::*; 20 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/conditional.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectError, InjectResult, Injector, RequestInfo, Service, ServiceInfo, 3 | Svc, TypedProvider, 4 | }; 5 | 6 | /// A [`TypedProvider`] which conditionally provides its service. If the 7 | /// condition is not met, then the provider is skipped during resolution. 8 | /// 9 | /// See the [docs for `WithCondition`](crate::WithCondition) for more 10 | /// information. 11 | pub struct ConditionalProvider 12 | where 13 | P: TypedProvider, 14 | F: Service + Fn(&Injector, &RequestInfo) -> bool, 15 | { 16 | inner: P, 17 | condition: F, 18 | } 19 | 20 | impl TypedProvider for ConditionalProvider 21 | where 22 | P: TypedProvider, 23 | F: Service + Fn(&Injector, &RequestInfo) -> bool, 24 | { 25 | type Result = P::Result; 26 | 27 | #[inline] 28 | fn provide_typed( 29 | &mut self, 30 | injector: &Injector, 31 | request_info: &RequestInfo, 32 | ) -> InjectResult> { 33 | if (self.condition)(injector, request_info) { 34 | self.inner.provide_typed(injector, request_info) 35 | } else { 36 | Err(InjectError::ConditionsNotMet { 37 | service_info: ServiceInfo::of::(), 38 | }) 39 | } 40 | } 41 | 42 | #[inline] 43 | fn provide_owned_typed( 44 | &mut self, 45 | injector: &Injector, 46 | request_info: &RequestInfo, 47 | ) -> InjectResult> { 48 | if (self.condition)(injector, request_info) { 49 | self.inner.provide_owned_typed(injector, request_info) 50 | } else { 51 | Err(InjectError::ConditionsNotMet { 52 | service_info: ServiceInfo::of::(), 53 | }) 54 | } 55 | } 56 | } 57 | 58 | /// Defines a conversion into a conditional provider. This trait is 59 | /// automatically implemented for all types that implement [`TypedProvider`]. 60 | pub trait WithCondition: TypedProvider { 61 | /// Creates a conditional provider. Conditional providers create their 62 | /// values only if their condition is met. If the condition is not met, 63 | /// then the provider is skipped. 64 | /// 65 | /// ## Example 66 | /// 67 | /// ``` 68 | /// use runtime_injector::{Injector, IntoSingleton, Svc, WithCondition}; 69 | /// 70 | /// #[derive(Default)] 71 | /// struct Foo; 72 | /// 73 | /// let mut builder = Injector::builder(); 74 | /// builder.provide(Foo::default.singleton().with_condition(|_, _| false)); 75 | /// 76 | /// let injector = builder.build(); 77 | /// let foo: Option> = injector.get().unwrap(); 78 | /// 79 | /// assert!(foo.is_none()); 80 | /// ``` 81 | #[must_use] 82 | fn with_condition(self, condition: F) -> ConditionalProvider 83 | where 84 | F: Service + Fn(&Injector, &RequestInfo) -> bool; 85 | } 86 | 87 | impl

WithCondition for P 88 | where 89 | P: TypedProvider, 90 | { 91 | #[inline] 92 | fn with_condition(self, condition: F) -> ConditionalProvider 93 | where 94 | F: Service + Fn(&Injector, &RequestInfo) -> bool, 95 | { 96 | ConditionalProvider { 97 | condition, 98 | inner: self, 99 | } 100 | } 101 | } 102 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/constant.rs: -------------------------------------------------------------------------------- 1 | use crate::{InjectResult, Injector, RequestInfo, Service, Svc, TypedProvider}; 2 | 3 | /// A provider which returns a constant, predetermined value. Note that this is 4 | /// technically a singleton service in that it does not recreate the value each 5 | /// time it is requested. 6 | pub struct ConstantProvider 7 | where 8 | R: Service, 9 | { 10 | result: Svc, 11 | } 12 | 13 | impl ConstantProvider 14 | where 15 | R: Service, 16 | { 17 | /// Creates a new [`ConstantProvider`] using a predetermined value. 18 | #[must_use] 19 | pub fn new(value: R) -> Self { 20 | ConstantProvider { 21 | result: Svc::new(value), 22 | } 23 | } 24 | } 25 | 26 | impl TypedProvider for ConstantProvider 27 | where 28 | R: Service, 29 | { 30 | type Result = R; 31 | 32 | fn provide_typed( 33 | &mut self, 34 | _injector: &Injector, 35 | _request_info: &RequestInfo, 36 | ) -> InjectResult> { 37 | Ok(self.result.clone()) 38 | } 39 | } 40 | 41 | impl From for ConstantProvider { 42 | fn from(value: T) -> Self { 43 | constant(value) 44 | } 45 | } 46 | 47 | /// Create a provider from a constant value. 48 | /// 49 | /// While the service itself will never be exposed through a mutable reference, 50 | /// if it supports interior mutability, its fields still can be mutated. Since 51 | /// the provider created with this function doesn't recreate the value each 52 | /// time it's requested, state can be stored in this manner. 53 | /// 54 | /// ## Example 55 | /// 56 | /// ``` 57 | /// use runtime_injector::{constant, Injector, Svc}; 58 | /// 59 | /// let mut builder = Injector::builder(); 60 | /// builder.provide(constant(8i32)); 61 | /// 62 | /// let injector = builder.build(); 63 | /// let value: Svc = injector.get().unwrap(); 64 | /// 65 | /// assert_eq!(8, *value); 66 | /// ``` 67 | /// 68 | /// ## Interior mutability 69 | /// 70 | /// One use case for constant values is to create a mutex to lock static, 71 | /// synchronized values that can be accessed from any service. For instance, 72 | /// suppose you wanted to create a counter to keep track of how many instances 73 | /// of a service you created: 74 | /// 75 | /// ``` 76 | /// use runtime_injector::{constant, Injector, IntoTransient, Svc}; 77 | /// use std::sync::Mutex; 78 | /// 79 | /// struct Foo; 80 | /// impl Foo { 81 | /// pub fn new(counter: Svc>) -> Self { 82 | /// let mut counter = counter.lock().unwrap(); 83 | /// *counter += 1; 84 | /// Foo 85 | /// } 86 | /// } 87 | /// 88 | /// let mut builder = Injector::builder(); 89 | /// builder.provide(Foo::new.transient()); 90 | /// builder.provide(constant(Mutex::new(0i32))); 91 | /// 92 | /// let injector = builder.build(); 93 | /// let foo: Svc = injector.get().unwrap(); 94 | /// let value: Svc> = injector.get().unwrap(); 95 | /// 96 | /// assert_eq!(1, *value.lock().unwrap()); 97 | /// ``` 98 | pub fn constant(value: T) -> ConstantProvider { 99 | ConstantProvider::new(value) 100 | } 101 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/fallible.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectError, InjectResult, Injector, RequestInfo, Service, ServiceFactory, 3 | ServiceInfo, 4 | }; 5 | use std::{error::Error, marker::PhantomData}; 6 | 7 | /// A service factory that may fail during service creation with a custom error 8 | /// type. During activation failure, an instance of 9 | /// [`InjectError::ActivationFailed`] is returned as an error. 10 | pub struct FallibleServiceFactory 11 | where 12 | D: Service, 13 | R: Service, 14 | E: Service + Error, 15 | F: ServiceFactory>, 16 | { 17 | inner: F, 18 | marker: PhantomData Result>, 19 | } 20 | 21 | impl ServiceFactory for FallibleServiceFactory 22 | where 23 | D: Service, 24 | R: Service, 25 | E: Service + Error, 26 | F: ServiceFactory>, 27 | { 28 | type Result = R; 29 | 30 | fn invoke( 31 | &mut self, 32 | injector: &Injector, 33 | request_info: &RequestInfo, 34 | ) -> InjectResult { 35 | let result = self.inner.invoke(injector, request_info)?; 36 | match result { 37 | Ok(result) => Ok(result), 38 | Err(error) => Err(InjectError::ActivationFailed { 39 | service_info: ServiceInfo::of::(), 40 | inner: Box::new(error), 41 | }), 42 | } 43 | } 44 | } 45 | 46 | /// Defines a conversion into a fallible service factory. This trait is 47 | /// automatically implemented for all service factories that return a 48 | /// [`Result`] with an error type that implements [`Error`] and 49 | /// [`Service`]. 50 | pub trait IntoFallible 51 | where 52 | D: Service, 53 | R: Service, 54 | E: Service + Error, 55 | F: ServiceFactory>, 56 | { 57 | /// Marks a service factory as being able to fail. On failure, an injection 58 | /// error is returned during activation. On success, the service is 59 | /// injected unwrapped from the result. In other words, a [`Result`] 60 | /// can be requested as a [`Svc`](crate::Svc), however if the 61 | /// constructor fails, an injection error is returned from the request. 62 | /// 63 | /// ## Example 64 | /// 65 | /// ``` 66 | /// use runtime_injector::{ 67 | /// InjectError, InjectResult, Injector, IntoFallible, IntoTransient, Svc, 68 | /// }; 69 | /// use std::{ 70 | /// error::Error, 71 | /// fmt::{Display, Formatter}, 72 | /// }; 73 | /// 74 | /// #[derive(Debug)] 75 | /// struct FooError; 76 | /// 77 | /// impl Error for FooError {} 78 | /// impl Display for FooError { 79 | /// fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 80 | /// write!(f, "An error occurred while creating a Foo") 81 | /// } 82 | /// } 83 | /// 84 | /// struct Foo(Svc); 85 | /// fn make_foo(a: Svc) -> Result { 86 | /// Err(FooError) 87 | /// } 88 | /// 89 | /// let mut builder = Injector::builder(); 90 | /// builder.provide(make_foo.fallible().transient()); 91 | /// builder.provide((|| 0).transient()); 92 | /// 93 | /// let injector = builder.build(); 94 | /// let foo_result: InjectResult> = injector.get(); 95 | /// match foo_result { 96 | /// Err(InjectError::ActivationFailed { .. }) => {} 97 | /// Err(error) => Err(error).unwrap(), 98 | /// _ => unreachable!("activation should have failed"), 99 | /// } 100 | /// ``` 101 | #[must_use] 102 | fn fallible(self) -> FallibleServiceFactory; 103 | } 104 | 105 | impl IntoFallible for F 106 | where 107 | D: Service, 108 | R: Service, 109 | E: Service + Error, 110 | F: ServiceFactory>, 111 | { 112 | fn fallible(self) -> FallibleServiceFactory { 113 | FallibleServiceFactory { 114 | inner: self, 115 | marker: PhantomData, 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/func.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectResult, Injector, Request, RequestInfo, Service, ServiceInfo, 3 | }; 4 | 5 | /// A factory for creating instances of a service. All functions of arity 12 or 6 | /// less are automatically service factories if the arguments to that function 7 | /// are valid service requests and the return value is a valid service type. 8 | /// 9 | /// ## Type parameters 10 | /// * `D` - Tuple of this service's dependencies. 11 | /// 12 | /// ## Example 13 | /// 14 | /// ``` 15 | /// use runtime_injector::{Injector, RequestInfo, ServiceFactory, Svc}; 16 | /// 17 | /// struct Foo; 18 | /// struct Bar; 19 | /// 20 | /// # fn _no_run() { 21 | /// fn factory(foo: Svc) -> Bar { 22 | /// todo!() 23 | /// } 24 | /// let injector: Injector = todo!(); 25 | /// factory.invoke(&injector, &RequestInfo::new()); 26 | /// # } 27 | /// ``` 28 | pub trait ServiceFactory: Service { 29 | /// The resulting service from invoking this service factory. 30 | type Result: Service; 31 | 32 | /// Invokes this service factory, creating an instance of the service. 33 | fn invoke( 34 | &mut self, 35 | injector: &Injector, 36 | request_info: &RequestInfo, 37 | ) -> InjectResult; 38 | } 39 | 40 | macro_rules! impl_provider_function { 41 | () => { 42 | impl_provider_function!(@impl ()); 43 | }; 44 | ($first:ident $(, $rest:ident)*) => { 45 | impl_provider_function!(@impl ($first $(, $rest)*)); 46 | impl_provider_function!($($rest),*); 47 | }; 48 | (@impl ($($type_name:ident),*)) => { 49 | impl ServiceFactory<($($type_name,)*)> for F 50 | where 51 | F: Service + FnMut($($type_name),*) -> R, 52 | R: Service, 53 | $($type_name: Request,)* 54 | { 55 | type Result = F::Output; 56 | 57 | #[allow(unused_variables, unused_mut, unused_assignments, non_snake_case)] 58 | fn invoke( 59 | &mut self, 60 | injector: &Injector, 61 | request_info: &RequestInfo 62 | ) -> InjectResult { 63 | let request_info = request_info.with_request(ServiceInfo::of::()); 64 | let result = self($( 65 | match <$type_name as Request>::request(&injector, &request_info) { 66 | Ok(dependency) => dependency, 67 | Err($crate::InjectError::MissingProvider { service_info }) => { 68 | return Err($crate::InjectError::MissingDependency { 69 | dependency_info: service_info, 70 | service_info: $crate::ServiceInfo::of::(), 71 | }) 72 | }, 73 | Err(error) => return Err(error), 74 | } 75 | ),*); 76 | Ok(result) 77 | } 78 | } 79 | }; 80 | } 81 | 82 | impl_provider_function!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11); 83 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/interface.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | DynSvc, InjectError, InjectResult, OwnedDynSvc, Service, ServiceInfo, Svc, 3 | }; 4 | 5 | /// Indicates functionality that can be implemented. 6 | /// 7 | /// For example, each [`Sized`] [`Service`] type is an interface that 8 | /// implements itself. This is done by requesting instances of itself from the 9 | /// injector. However, the injector cannot provide instances of dynamic types 10 | /// (`dyn Trait`) automatically because they are unsized. For this reason, any 11 | /// interfaces using traits must be declared explicitly before use. This trait 12 | /// should usually be implemented by the [`interface!`] macro in the same 13 | /// module the trait was declared in. 14 | /// 15 | /// Since implementations of interfaces must be services, interfaces should be 16 | /// declared with a supertrait of [`Service`]. This will ensure that the 17 | /// implementors can be cast to [`dyn Any`](std::any::Any), and with the "arc" 18 | /// feature enabled, those implementors are also [`Send`] + [`Sync`]. 19 | /// Additionally, trait interfaces must be convertible to trait objects so they 20 | /// can be used behind service pointers. You can read more about trait objects 21 | /// [here](https://doc.rust-lang.org/book/ch17-02-trait-objects.html). 22 | /// 23 | /// See the documentation for the [`interface!`] macro for more information. 24 | pub trait Interface: Service { 25 | /// Downcasts a dynamic service pointer into a service pointer of this 26 | /// interface type. 27 | fn downcast(service: DynSvc) -> InjectResult>; 28 | 29 | /// Downcasts an owned dynamic service pointer into an owned service 30 | /// pointer of this interface type. 31 | fn downcast_owned(service: OwnedDynSvc) -> InjectResult>; 32 | } 33 | 34 | impl Interface for T { 35 | fn downcast(service: DynSvc) -> InjectResult> { 36 | service 37 | .downcast() 38 | .map_err(|_| InjectError::InvalidProvider { 39 | service_info: ServiceInfo::of::(), 40 | }) 41 | } 42 | 43 | fn downcast_owned(service: OwnedDynSvc) -> InjectResult> { 44 | service 45 | .downcast() 46 | .map_err(|_| InjectError::InvalidProvider { 47 | service_info: ServiceInfo::of::(), 48 | }) 49 | } 50 | } 51 | 52 | /// Marker trait that indicates that a type is an interface for another type. 53 | /// 54 | /// Each sized type is an interface for itself, and each `dyn Trait` is an 55 | /// interface for the types that it can resolve. This trait should usually be 56 | /// implemented by the [`interface!`] macro, and is strictly used to enforce 57 | /// stronger type checking when assigning implementations for interfaces. 58 | pub trait InterfaceFor: Interface {} 59 | impl InterfaceFor for T {} 60 | 61 | /// Marks a trait as being an interface for many other types. This means that 62 | /// a request for the given trait can resolve to any of the types indicated by 63 | /// this macro invocation. 64 | /// 65 | /// With the "arc" feature enabled, the trait must be a subtrait of [`Send`] 66 | /// and [`Sync`]. This is necessary to allow the service pointers to be 67 | /// downcasted. If the "rc" feature is enabled, this is not required. 68 | /// Additionally, instances of the trait must have a `'static` lifetime. This 69 | /// can be done easily by making your interface a subtrait of [`Service`]. 70 | /// 71 | /// ## Example 72 | /// 73 | /// ``` 74 | /// use runtime_injector::{interface, Service}; 75 | /// 76 | /// struct Bar; 77 | /// #[cfg(test)] 78 | /// struct MockBar; 79 | /// 80 | /// trait Foo: Service {} 81 | /// impl Foo for Bar {} 82 | /// #[cfg(test)] 83 | /// impl Foo for MockBar {} 84 | /// 85 | /// // Requests for `dyn Foo` can resolve to either `Bar` or, in a test run, 86 | /// // `MockBar`. Note that attributes are allowed on each of the listed types. 87 | /// interface! { 88 | /// dyn Foo = [ 89 | /// Bar, 90 | /// #[cfg(test)] 91 | /// MockBar, 92 | /// ], 93 | /// }; 94 | /// ``` 95 | #[macro_export] 96 | macro_rules! interface { 97 | { 98 | $( 99 | $interface:ty = [ 100 | $($(#[$($attr:meta),*])* $impl:ty),* 101 | $(,)? 102 | ] 103 | ),* 104 | $(,)? 105 | } => { 106 | $( 107 | impl $crate::Interface for $interface { 108 | #[allow(unused_assignments)] 109 | fn downcast(mut service: $crate::DynSvc) -> $crate::InjectResult<$crate::Svc> { 110 | $( 111 | $(#[$($attr),*])* 112 | match service.downcast::<$impl>() { 113 | Ok(downcasted) => return Ok(downcasted as $crate::Svc), 114 | Err(input) => service = input, 115 | } 116 | )* 117 | 118 | Err($crate::InjectError::MissingProvider { service_info: $crate::ServiceInfo::of::() }) 119 | } 120 | 121 | #[allow(unused_assignments)] 122 | fn downcast_owned(mut service: $crate::OwnedDynSvc) -> $crate::InjectResult<::std::boxed::Box> { 123 | $( 124 | $(#[$($attr),*])* 125 | match service.downcast::<$impl>() { 126 | Ok(downcasted) => return Ok(downcasted as ::std::boxed::Box), 127 | Err(input) => service = input, 128 | } 129 | )* 130 | 131 | Err($crate::InjectError::MissingProvider { service_info: $crate::ServiceInfo::of::() }) 132 | } 133 | } 134 | 135 | $( 136 | $(#[$($attr),*])* 137 | impl $crate::InterfaceFor<$impl> for $interface {} 138 | )* 139 | )* 140 | }; 141 | } 142 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/providers.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use crate::{ 4 | DynSvc, InjectError, InjectResult, Injector, Interface, InterfaceFor, 5 | OwnedDynSvc, RequestInfo, Service, ServiceInfo, Svc, 6 | }; 7 | 8 | /// Weakly typed service provider. 9 | /// 10 | /// Given an injector, this can provide an instance of a service. This is 11 | /// automatically implemented for all types that implement [`TypedProvider`], 12 | /// and [`TypedProvider`] should be preferred if possible for custom service 13 | /// providers to allow for stronger type checking. 14 | pub trait Provider: Service { 15 | /// The [`ServiceInfo`] which describes the type returned by this provider. 16 | fn result(&self) -> ServiceInfo; 17 | 18 | /// Provides an instance of the service. 19 | fn provide( 20 | &mut self, 21 | injector: &Injector, 22 | request_info: &RequestInfo, 23 | ) -> InjectResult; 24 | 25 | /// Provides an owned instance of the service. 26 | fn provide_owned( 27 | &mut self, 28 | _injector: &Injector, 29 | _request_info: &RequestInfo, 30 | ) -> InjectResult { 31 | Err(InjectError::OwnedNotSupported { 32 | service_info: self.result(), 33 | }) 34 | } 35 | } 36 | 37 | impl Provider for T 38 | where 39 | T: TypedProvider, 40 | { 41 | fn result(&self) -> ServiceInfo { 42 | ServiceInfo::of::() 43 | } 44 | 45 | fn provide( 46 | &mut self, 47 | injector: &Injector, 48 | request_info: &RequestInfo, 49 | ) -> InjectResult { 50 | let result = self.provide_typed(injector, request_info)?; 51 | Ok(result as DynSvc) 52 | } 53 | 54 | fn provide_owned( 55 | &mut self, 56 | injector: &Injector, 57 | request_info: &RequestInfo, 58 | ) -> InjectResult { 59 | let result = self.provide_owned_typed(injector, request_info)?; 60 | Ok(result as OwnedDynSvc) 61 | } 62 | } 63 | 64 | /// A strongly-typed service provider. 65 | /// 66 | /// Types which implement this trait can provide strongly-typed instances of a 67 | /// particular service type. Examples of typed providers include providers 68 | /// created from service factories or constant providers. This should be 69 | /// preferred over [`Provider`] for custom service providers if possible due to 70 | /// the strong type guarantees this provides. [`Provider`] is automatically 71 | /// implemented for all types which implement [`TypedProvider`]. 72 | /// 73 | /// ## Example 74 | /// 75 | /// ``` 76 | /// use runtime_injector::{ 77 | /// InjectResult, Injector, RequestInfo, Svc, TypedProvider, 78 | /// }; 79 | /// 80 | /// struct Foo; 81 | /// 82 | /// struct FooProvider; 83 | /// impl TypedProvider for FooProvider { 84 | /// type Result = Foo; 85 | /// 86 | /// fn provide_typed( 87 | /// &mut self, 88 | /// _injector: &Injector, 89 | /// _request_info: &RequestInfo, 90 | /// ) -> InjectResult> { 91 | /// Ok(Svc::new(Foo)) 92 | /// } 93 | /// } 94 | /// 95 | /// let mut builder = Injector::builder(); 96 | /// builder.provide(FooProvider); 97 | /// 98 | /// let injector = builder.build(); 99 | /// let _foo: Svc = injector.get().unwrap(); 100 | /// ``` 101 | pub trait TypedProvider: Sized + Provider { 102 | /// The type of service this can provide. 103 | type Result: Interface; 104 | 105 | /// Provides an instance of the service. The [`Injector`] passed in can be 106 | /// used to retrieve instances of any dependencies this service has. 107 | fn provide_typed( 108 | &mut self, 109 | injector: &Injector, 110 | request_info: &RequestInfo, 111 | ) -> InjectResult>; 112 | 113 | /// Provides an owned instance of the service. Not all providers can 114 | /// provide an owned variant of the service. 115 | fn provide_owned_typed( 116 | &mut self, 117 | _injector: &Injector, 118 | _request_info: &RequestInfo, 119 | ) -> InjectResult> { 120 | Err(InjectError::OwnedNotSupported { 121 | service_info: ServiceInfo::of::(), 122 | }) 123 | } 124 | 125 | /// Provides this service as an implementation of a particular interface. 126 | /// Rather than requesting this service with its concrete type, it can 127 | /// instead be requested by its interface type. 128 | /// 129 | /// *Note: it cannot be requested with its concrete type once it has been 130 | /// assigned an interface.* 131 | /// 132 | /// ## Example 133 | /// 134 | /// ``` 135 | /// use runtime_injector::{ 136 | /// interface, InjectResult, Injector, IntoSingleton, Service, Svc, 137 | /// TypedProvider, 138 | /// }; 139 | /// 140 | /// trait Fooable: Service { 141 | /// fn bar(&self) {} 142 | /// } 143 | /// 144 | /// interface!(dyn Fooable = [Foo]); 145 | /// 146 | /// #[derive(Default)] 147 | /// struct Foo; 148 | /// impl Fooable for Foo {} 149 | /// 150 | /// let mut builder = Injector::builder(); 151 | /// builder.provide(Foo::default.singleton().with_interface::()); 152 | /// 153 | /// // Foo can now be requested through its interface of `dyn Fooable`. 154 | /// let injector = builder.build(); 155 | /// let fooable: Svc = injector.get().unwrap(); 156 | /// fooable.bar(); 157 | /// 158 | /// // It can't be requested through its original type 159 | /// assert!(injector.get::>().is_err()); 160 | /// ``` 161 | fn with_interface>( 162 | self, 163 | ) -> InterfaceProvider { 164 | InterfaceProvider { 165 | inner: self, 166 | marker: PhantomData, 167 | } 168 | } 169 | } 170 | 171 | /// Provides a service as an implementation of an interface. See 172 | /// [`TypedProvider::with_interface()`] for more information. 173 | pub struct InterfaceProvider 174 | where 175 | P: TypedProvider, 176 | I: ?Sized + InterfaceFor, 177 | { 178 | inner: P, 179 | marker: PhantomData I>, 180 | } 181 | 182 | impl Provider for InterfaceProvider 183 | where 184 | P: TypedProvider, 185 | I: ?Sized + InterfaceFor, 186 | { 187 | fn result(&self) -> ServiceInfo { 188 | ServiceInfo::of::() 189 | } 190 | 191 | fn provide( 192 | &mut self, 193 | injector: &Injector, 194 | request_info: &RequestInfo, 195 | ) -> InjectResult { 196 | self.inner.provide(injector, request_info) 197 | } 198 | 199 | fn provide_owned( 200 | &mut self, 201 | injector: &Injector, 202 | request_info: &RequestInfo, 203 | ) -> InjectResult { 204 | self.inner.provide_owned(injector, request_info) 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/service.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{Any, TypeId}, 3 | error::Error, 4 | fmt::{Display, Formatter}, 5 | }; 6 | 7 | #[cfg(feature = "rc")] 8 | macro_rules! feature_unique { 9 | ({ $($common:tt)* }, { $($rc:tt)* }, { $($_arc:tt)* }) => { 10 | $($common)* 11 | $($rc)* 12 | }; 13 | } 14 | 15 | #[cfg(feature = "arc")] 16 | macro_rules! feature_unique { 17 | ({ $($common:tt)* }, { $($_rc:tt)* }, { $($arc:tt)* }) => { 18 | $($common)* 19 | $($arc)* 20 | }; 21 | } 22 | 23 | feature_unique!( 24 | { 25 | /// A reference-counted pointer holding a service. The pointer type is 26 | /// determined by the feature flags passed to this crate. 27 | /// 28 | /// - **rc**: Pointer type is [`Rc`](std::rc::Rc) 29 | /// - **arc**: Pointer type is [`Arc`](std::sync::Arc) (default) 30 | }, 31 | { 32 | pub type Svc = std::rc::Rc; 33 | }, 34 | { 35 | pub type Svc = std::sync::Arc; 36 | } 37 | ); 38 | 39 | feature_unique!( 40 | { 41 | /// A reference-counted service pointer holding an instance of `dyn 42 | /// Any`. 43 | }, 44 | { 45 | pub type DynSvc = Svc; 46 | }, 47 | { 48 | pub type DynSvc = Svc; 49 | } 50 | ); 51 | 52 | feature_unique!( 53 | { 54 | /// An owned service pointer holding an instance of `dyn Any`. 55 | }, 56 | { 57 | pub type OwnedDynSvc = Box; 58 | }, 59 | { 60 | pub type OwnedDynSvc = Box; 61 | } 62 | ); 63 | 64 | feature_unique!( 65 | { 66 | /// Implemented automatically on types that are capable of being a 67 | /// service. 68 | }, 69 | { 70 | pub trait Service: Any {} 71 | impl Service for T {} 72 | }, 73 | { 74 | pub trait Service: Any + Send + Sync {} 75 | impl Service for T {} 76 | } 77 | ); 78 | 79 | /// A result from attempting to inject dependencies into a service and 80 | /// construct an instance of it. 81 | pub type InjectResult = Result; 82 | 83 | /// Type information about a service. 84 | #[derive(Clone, Copy, PartialEq, Eq, Debug, Hash)] 85 | pub struct ServiceInfo { 86 | id: TypeId, 87 | name: &'static str, 88 | } 89 | 90 | impl ServiceInfo { 91 | /// Creates a [`ServiceInfo`] for the given type. 92 | #[must_use] 93 | pub fn of() -> Self { 94 | ServiceInfo { 95 | id: TypeId::of::(), 96 | name: std::any::type_name::(), 97 | } 98 | } 99 | 100 | /// Gets the [`TypeId`] for this service. 101 | #[must_use] 102 | pub fn id(&self) -> TypeId { 103 | self.id 104 | } 105 | 106 | /// Gets the type name of this service. 107 | #[must_use] 108 | pub fn name(&self) -> &'static str { 109 | self.name 110 | } 111 | } 112 | 113 | /// An error that has occurred during creation of a service. 114 | #[derive(Debug)] 115 | pub enum InjectError { 116 | /// Failed to find a provider for the requested type. 117 | MissingProvider { 118 | /// The service that was requested. 119 | service_info: ServiceInfo, 120 | }, 121 | 122 | /// A provider for a dependency of the requested service is missing. 123 | MissingDependency { 124 | /// The service that was requested. 125 | service_info: ServiceInfo, 126 | 127 | /// The dependency that is missing a provider. 128 | dependency_info: ServiceInfo, 129 | }, 130 | 131 | /// A cycle was detected during activation of a service. 132 | CycleDetected { 133 | /// The service that was requested. 134 | service_info: ServiceInfo, 135 | 136 | /// The chain of services that were requested during resolution of this 137 | /// service. 138 | cycle: Vec, 139 | }, 140 | 141 | /// The requested implementer is not valid for the requested service. 142 | InvalidImplementation { 143 | /// The service that was requested. 144 | service_info: ServiceInfo, 145 | 146 | /// The implementation that was requested for this service. 147 | implementation: ServiceInfo, 148 | }, 149 | 150 | /// The registered provider returned the wrong service type. 151 | InvalidProvider { 152 | /// The service that was requested. 153 | service_info: ServiceInfo, 154 | }, 155 | 156 | /// The requested service has too many providers registered. 157 | MultipleProviders { 158 | /// The service that was requested. 159 | service_info: ServiceInfo, 160 | /// The number of providers registered for that service. 161 | providers: usize, 162 | }, 163 | 164 | /// The registered provider can't provide an owned variant of the requested 165 | /// service. 166 | OwnedNotSupported { 167 | /// The service that was requested. 168 | service_info: ServiceInfo, 169 | }, 170 | 171 | /// This provider's conditions for providing its service have not and it 172 | /// should be ignored. 173 | /// 174 | /// Returning this from a provider causes the provider to be ignored during 175 | /// service resolution. See [`ConditionalProvider`] for more information. 176 | /// 177 | /// [`ConditionalProvider`]: crate::ConditionalProvider 178 | ConditionsNotMet { 179 | /// The service that was requested. 180 | service_info: ServiceInfo, 181 | }, 182 | 183 | /// An error occurred during activation of a service. 184 | ActivationFailed { 185 | /// The service that was requested. 186 | service_info: ServiceInfo, 187 | /// The error that was thrown during service initialization. 188 | inner: Box, 189 | }, 190 | 191 | /// An unexpected error has occurred. This is usually caused by a bug in 192 | /// the library itself. 193 | InternalError(String), 194 | } 195 | 196 | impl Error for InjectError { 197 | fn source(&self) -> Option<&(dyn Error + 'static)> { 198 | match self { 199 | InjectError::ActivationFailed { inner, .. } => Some(inner.as_ref()), 200 | _ => None, 201 | } 202 | } 203 | } 204 | 205 | impl Display for InjectError { 206 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 207 | write!(f, "an error occurred during injection: ")?; 208 | match self { 209 | InjectError::MissingProvider { service_info } => { 210 | write!(f, "{} has no provider", service_info.name()) 211 | } 212 | InjectError::MissingDependency { 213 | service_info, 214 | .. 215 | } => write!(f, "{} is missing a dependency", service_info.name()), 216 | InjectError::CycleDetected { 217 | service_info, 218 | cycle, 219 | } => write!( 220 | f, 221 | "a cycle was detected during activation of {} [{}]", 222 | service_info.name(), 223 | fmt_cycle(cycle) 224 | ), 225 | InjectError::InvalidImplementation { 226 | service_info, 227 | implementation, 228 | } => write!( 229 | f, 230 | "{} is not registered as an implementer of {}", 231 | implementation.name(), 232 | service_info.name() 233 | ), 234 | InjectError::InvalidProvider { service_info } => { 235 | write!(f, "the registered provider for {} returned the wrong type", service_info.name()) 236 | } 237 | InjectError::MultipleProviders { 238 | service_info, 239 | providers, 240 | } => write!( 241 | f, 242 | "the requested service {} has {} providers registered (did you mean to request a Services instead?)", 243 | service_info.name(), 244 | providers 245 | ), 246 | InjectError::OwnedNotSupported { 247 | service_info 248 | } => write!( 249 | f, 250 | "the registered provider can't provide an owned variant of {}", 251 | service_info.name() 252 | ), 253 | InjectError::ConditionsNotMet { service_info } => { 254 | write!( 255 | f, 256 | "the conditions for providing the service {} have not been met", 257 | service_info.name() 258 | ) 259 | } 260 | InjectError::ActivationFailed { service_info, .. } => { 261 | write!(f, "an error occurred during activation of {}", service_info.name()) 262 | }, 263 | InjectError::InternalError(message) => { 264 | write!(f, "an unexpected error occurred (please report this): {}", message) 265 | }, 266 | } 267 | } 268 | } 269 | 270 | fn fmt_cycle(cycle: &[ServiceInfo]) -> String { 271 | let mut joined = String::new(); 272 | for item in cycle.iter().rev() { 273 | if !joined.is_empty() { 274 | joined.push_str(" -> "); 275 | } 276 | joined.push_str(item.name()); 277 | } 278 | joined 279 | } 280 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/singleton.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectResult, Injector, RequestInfo, Service, ServiceFactory, Svc, 3 | TypedProvider, 4 | }; 5 | use std::marker::PhantomData; 6 | 7 | /// A service provider that only creates a single instance of the service. 8 | /// The service is created only during its first request. Any subsequent 9 | /// requests return service pointers to the same service. 10 | pub struct SingletonProvider 11 | where 12 | R: Service, 13 | F: ServiceFactory, 14 | { 15 | factory: F, 16 | result: Option>, 17 | marker: PhantomData R>, 18 | } 19 | 20 | impl SingletonProvider 21 | where 22 | R: Service, 23 | F: ServiceFactory, 24 | { 25 | /// Creates a new [`SingletonProvider`] using a service factory. 26 | #[must_use] 27 | pub fn new(func: F) -> Self { 28 | SingletonProvider { 29 | factory: func, 30 | result: None, 31 | marker: PhantomData, 32 | } 33 | } 34 | } 35 | 36 | impl TypedProvider for SingletonProvider 37 | where 38 | D: Service, 39 | R: Service, 40 | F: ServiceFactory, 41 | { 42 | type Result = R; 43 | 44 | fn provide_typed( 45 | &mut self, 46 | injector: &Injector, 47 | request_info: &RequestInfo, 48 | ) -> InjectResult> { 49 | if let Some(ref service) = self.result { 50 | return Ok(service.clone()); 51 | } 52 | 53 | let result = self.factory.invoke(injector, request_info)?; 54 | let result = Svc::new(result); 55 | self.result = Some(result.clone()); 56 | Ok(result) 57 | } 58 | } 59 | 60 | /// Defines a conversion into a singleton provider. This trait is automatically 61 | /// implemented for all service factories. 62 | pub trait IntoSingleton 63 | where 64 | R: Service, 65 | F: ServiceFactory, 66 | { 67 | /// Creates a singleton provider. Singleton providers create their values 68 | /// only once (when first requested) and reuse that value for each future 69 | /// request. 70 | /// 71 | /// ## Example 72 | /// 73 | /// ``` 74 | /// use runtime_injector::{Injector, IntoSingleton, Svc}; 75 | /// 76 | /// #[derive(Default)] 77 | /// struct Foo; 78 | /// 79 | /// let mut builder = Injector::builder(); 80 | /// builder.provide(Foo::default.singleton()); 81 | /// 82 | /// let injector = builder.build(); 83 | /// let foo1: Svc = injector.get().unwrap(); 84 | /// let foo2: Svc = injector.get().unwrap(); 85 | /// 86 | /// assert!(Svc::ptr_eq(&foo1, &foo2)); 87 | /// ``` 88 | #[must_use] 89 | fn singleton(self) -> SingletonProvider; 90 | } 91 | 92 | impl IntoSingleton for F 93 | where 94 | R: Service, 95 | F: ServiceFactory, 96 | { 97 | fn singleton(self) -> SingletonProvider { 98 | SingletonProvider::new(self) 99 | } 100 | } 101 | 102 | impl From for SingletonProvider 103 | where 104 | R: Service, 105 | F: ServiceFactory, 106 | { 107 | fn from(func: F) -> Self { 108 | func.singleton() 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/services/transient.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | InjectResult, Injector, RequestInfo, Service, ServiceFactory, Svc, 3 | TypedProvider, 4 | }; 5 | use std::marker::PhantomData; 6 | 7 | /// A service provider that creates an instance of the service each time it is 8 | /// requested. This will never return two service pointers to the same instance 9 | /// of a service. 10 | pub struct TransientProvider 11 | where 12 | R: Service, 13 | F: ServiceFactory, 14 | { 15 | factory: F, 16 | marker: PhantomData InjectResult>, 17 | } 18 | 19 | impl TransientProvider 20 | where 21 | R: Service, 22 | F: ServiceFactory, 23 | { 24 | /// Creates a new [`TransientProvider`] using a service factory. 25 | #[must_use] 26 | pub fn new(func: F) -> Self { 27 | TransientProvider { 28 | factory: func, 29 | marker: PhantomData, 30 | } 31 | } 32 | } 33 | 34 | impl TypedProvider for TransientProvider 35 | where 36 | D: Service, 37 | R: Service, 38 | F: ServiceFactory, 39 | { 40 | type Result = R; 41 | 42 | fn provide_typed( 43 | &mut self, 44 | injector: &Injector, 45 | request_info: &RequestInfo, 46 | ) -> InjectResult> { 47 | let result = self.factory.invoke(injector, request_info)?; 48 | Ok(Svc::new(result)) 49 | } 50 | 51 | fn provide_owned_typed( 52 | &mut self, 53 | injector: &Injector, 54 | request_info: &RequestInfo, 55 | ) -> InjectResult> { 56 | let result = self.factory.invoke(injector, request_info)?; 57 | Ok(Box::new(result)) 58 | } 59 | } 60 | 61 | /// Defines a conversion into a transient provider. This trait is automatically 62 | /// implemented for all service factories. 63 | pub trait IntoTransient 64 | where 65 | R: Service, 66 | F: ServiceFactory, 67 | { 68 | /// Creates a transient provider. Transient providers create their values 69 | /// each time the service is requested and will never return service 70 | /// pointers to the same instance more than once. 71 | /// 72 | /// ## Example 73 | /// 74 | /// ``` 75 | /// use runtime_injector::{Injector, IntoTransient, Svc}; 76 | /// 77 | /// #[derive(Default)] 78 | /// struct Foo; 79 | /// 80 | /// let mut builder = Injector::builder(); 81 | /// builder.provide(Foo::default.transient()); 82 | /// 83 | /// let injector = builder.build(); 84 | /// let foo1: Svc = injector.get().unwrap(); 85 | /// let foo2: Svc = injector.get().unwrap(); 86 | /// 87 | /// assert!(!Svc::ptr_eq(&foo1, &foo2)); 88 | /// ``` 89 | #[must_use] 90 | fn transient(self) -> TransientProvider; 91 | } 92 | 93 | impl IntoTransient for F 94 | where 95 | R: Service, 96 | F: ServiceFactory, 97 | { 98 | fn transient(self) -> TransientProvider { 99 | TransientProvider::new(self) 100 | } 101 | } 102 | 103 | impl From for TransientProvider 104 | where 105 | R: Service, 106 | F: ServiceFactory, 107 | { 108 | fn from(func: F) -> Self { 109 | func.transient() 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /crates/runtime_injector/src/tests.rs: -------------------------------------------------------------------------------- 1 | #![allow(clippy::blacklisted_name)] 2 | 3 | use crate::{ 4 | constant, interface, InjectError, InjectResult, Injector, IntoSingleton, 5 | IntoTransient, RequestInfo, Service, ServiceInfo, Services, Svc, 6 | TypedProvider, 7 | }; 8 | use std::sync::Mutex; 9 | 10 | #[derive(Default)] 11 | struct Svc1(pub i32); 12 | 13 | struct Svc2 { 14 | pub dep1: Svc, 15 | } 16 | 17 | impl Svc2 { 18 | pub fn new(dep1: Svc) -> Self { 19 | Svc2 { dep1 } 20 | } 21 | } 22 | 23 | struct Svc3 { 24 | pub dep1: Svc, 25 | pub dep2: Svc, 26 | } 27 | 28 | impl Svc3 { 29 | pub fn new(dep1: Svc, dep2: Svc) -> Self { 30 | Svc3 { dep1, dep2 } 31 | } 32 | } 33 | 34 | #[test] 35 | fn can_make_svc1() { 36 | let mut builder = Injector::builder(); 37 | builder.provide(Svc1::default.transient()); 38 | 39 | let injector = builder.build(); 40 | let _service: Svc = injector.get().unwrap(); 41 | } 42 | 43 | #[test] 44 | fn cant_make_svc1_when_no_provider() { 45 | let injector = Injector::builder().build(); 46 | let svc: InjectResult> = injector.get(); 47 | match svc { 48 | Err(InjectError::MissingProvider { service_info }) 49 | if service_info == ServiceInfo::of::() => {} 50 | Err(error) => Err(error).unwrap(), 51 | Ok(_) => unreachable!(), 52 | } 53 | 54 | let svc: Option> = injector.get().unwrap(); 55 | match svc { 56 | None => {} 57 | Some(_) => panic!("service should not have been created"), 58 | } 59 | } 60 | 61 | #[test] 62 | fn can_make_svc3() { 63 | let mut builder = Injector::builder(); 64 | builder.provide(Svc1::default.transient()); 65 | builder.provide(Svc2::new.transient()); 66 | builder.provide(Svc3::new.transient()); 67 | 68 | let injector = builder.build(); 69 | let _service: Svc = injector.get().unwrap(); 70 | } 71 | 72 | #[test] 73 | fn cant_make_svc3_when_no_provider_for_dependency() { 74 | let mut builder = Injector::builder(); 75 | builder.provide(Svc2::new.transient()); 76 | builder.provide(Svc3::new.transient()); 77 | 78 | let injector = builder.build(); 79 | match injector.get::>() { 80 | Err(InjectError::MissingDependency { 81 | dependency_info, .. 82 | }) if dependency_info == ServiceInfo::of::() => {} 83 | Err(error) => Err(error).unwrap(), 84 | Ok(_) => unreachable!("service should not be able to be activated"), 85 | } 86 | } 87 | 88 | #[test] 89 | fn singleton() { 90 | type Counter = Mutex; 91 | 92 | fn make_svc1(counter: Svc) -> Svc1 { 93 | let mut counter = counter.lock().unwrap(); 94 | *counter += 1; 95 | Svc1(*counter) 96 | } 97 | 98 | let mut builder = Injector::builder(); 99 | builder.provide((|| Mutex::new(0)).singleton()); 100 | builder.provide(make_svc1.transient()); 101 | builder.provide(Svc2::new.transient()); 102 | builder.provide(Svc3::new.transient()); 103 | 104 | let injector = builder.build(); 105 | let svc1: Svc = injector.get().unwrap(); 106 | let svc2: Svc = injector.get().unwrap(); 107 | let svc3: Svc = injector.get().unwrap(); 108 | 109 | assert_ne!(svc1.0, svc2.dep1.0); 110 | assert_ne!(svc1.0, svc3.dep1.0); 111 | assert_ne!(svc2.dep1.0, svc3.dep1.0); 112 | } 113 | 114 | #[test] 115 | fn constants() { 116 | type Counter = Mutex; 117 | 118 | fn make_svc1(counter: Svc) -> Svc1 { 119 | let mut counter = counter.lock().unwrap(); 120 | *counter += 1; 121 | Svc1(*counter) 122 | } 123 | 124 | let mut builder = Injector::builder(); 125 | builder.provide(constant(Mutex::new(0))); 126 | builder.provide(make_svc1.transient()); 127 | builder.provide(Svc2::new.transient()); 128 | builder.provide(Svc3::new.transient()); 129 | 130 | let injector = builder.build(); 131 | let svc1: Svc = injector.get().unwrap(); 132 | let svc2: Svc = injector.get().unwrap(); 133 | let svc3: Svc = injector.get().unwrap(); 134 | 135 | assert_ne!(svc1.0, svc2.dep1.0); 136 | assert_ne!(svc1.0, svc3.dep1.0); 137 | assert_ne!(svc2.dep1.0, svc3.dep1.0); 138 | } 139 | 140 | #[test] 141 | fn interfaces() { 142 | pub trait Foo: Service { 143 | fn bar(&self) -> i32; 144 | } 145 | 146 | interface!( 147 | dyn Foo = [ 148 | Svc1, 149 | #[cfg(test)] 150 | Svc2, 151 | #[cfg(not(test))] 152 | Svc3, 153 | ] 154 | ); 155 | 156 | impl Foo for Svc1 { 157 | fn bar(&self) -> i32 { 158 | 4 159 | } 160 | } 161 | 162 | impl Foo for Svc2 { 163 | fn bar(&self) -> i32 { 164 | 5 165 | } 166 | } 167 | 168 | struct Svc4 { 169 | pub foo: Svc, 170 | } 171 | 172 | impl Svc4 { 173 | pub fn new(foo: Svc) -> Self { 174 | Svc4 { foo } 175 | } 176 | } 177 | 178 | // Svc1 179 | let mut builder = Injector::builder(); 180 | builder.provide(Svc1::default.transient().with_interface::()); 181 | 182 | let injector = builder.build(); 183 | let svc: Svc = injector.get().unwrap(); 184 | 185 | assert_eq!(4, svc.bar()); 186 | 187 | // Svc2 188 | let mut builder = Injector::builder(); 189 | builder.provide(Svc1::default.transient()); 190 | builder.provide(Svc2::new.transient().with_interface::()); 191 | 192 | let injector = builder.build(); 193 | let svc: Svc = injector.get().unwrap(); 194 | 195 | assert_eq!(5, svc.bar()); 196 | 197 | // Svc4 198 | let mut builder = Injector::builder(); 199 | builder.provide(Svc1::default.transient()); 200 | builder.provide(Svc2::new.transient().with_interface::()); 201 | builder.provide(Svc4::new.transient()); 202 | 203 | let injector = builder.build(); 204 | let svc: Svc = injector.get().unwrap(); 205 | 206 | assert_eq!(5, svc.foo.bar()); 207 | } 208 | 209 | #[test] 210 | fn multi_injection() { 211 | trait Foo: Service {} 212 | 213 | impl Foo for Svc1 {} 214 | impl Foo for Svc2 {} 215 | impl Foo for Svc3 {} 216 | 217 | interface!(dyn Foo = [Svc1, Svc2, Svc3]); 218 | 219 | let mut builder = Injector::builder(); 220 | builder.provide(Svc1::default.transient().with_interface::()); 221 | 222 | let injector = builder.build(); 223 | let mut foos: Services = injector.get().unwrap(); 224 | assert_eq!(1, foos.len()); 225 | 226 | let foos: Vec> = 227 | foos.get_all().collect::>().unwrap(); 228 | assert_eq!(1, foos.len()); 229 | } 230 | 231 | #[test] 232 | fn injector_returns_error_on_cycles() { 233 | struct Foo(Svc); 234 | struct Bar(Svc); 235 | 236 | let mut builder = Injector::builder(); 237 | builder.provide(Foo.singleton()); 238 | builder.provide(Bar.singleton()); 239 | 240 | let injector = builder.build(); 241 | match injector.get::>() { 242 | Err(InjectError::CycleDetected { 243 | service_info, 244 | cycle, 245 | }) if service_info == ServiceInfo::of::() => { 246 | assert_eq!(3, cycle.len()); 247 | assert_eq!(ServiceInfo::of::(), cycle[0]); 248 | assert_eq!(ServiceInfo::of::(), cycle[1]); 249 | assert_eq!(ServiceInfo::of::(), cycle[2]); 250 | } 251 | Ok(_) => panic!("somehow created a Foo with a cyclic dependency"), 252 | Err(error) => Err(error).unwrap(), 253 | } 254 | } 255 | 256 | #[test] 257 | fn request_info_has_correct_path() { 258 | struct Foo(Svc, RequestInfo); 259 | struct Bar(RequestInfo); 260 | 261 | let mut builder = Injector::builder(); 262 | builder.provide(Foo.transient()); 263 | builder.provide(Bar.transient()); 264 | 265 | let injector = builder.build(); 266 | let bar: Svc = injector.get().unwrap(); 267 | let foo: Svc = injector.get().unwrap(); 268 | 269 | assert_eq!(&[ServiceInfo::of::()], bar.0.service_path()); 270 | assert_eq!(&[ServiceInfo::of::()], foo.1.service_path()); 271 | assert_eq!( 272 | &[ServiceInfo::of::(), ServiceInfo::of::()], 273 | foo.0 .0.service_path() 274 | ); 275 | } 276 | -------------------------------------------------------------------------------- /crates/runtime_injector_actix/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "runtime_injector_actix" 3 | version = "0.2.0" 4 | edition = "2018" 5 | authors = ["TehPers "] 6 | license = "MIT OR Apache-2.0" 7 | description = "Runtime dependency injection container for actix-web" 8 | repository = "https://github.com/TehPers/runtime_injector" 9 | documentation = "https://docs.rs/runtime_injector_actix" 10 | keywords = ["dependency-injection", "di", "ioc", "actix", "web"] 11 | readme = "README.md" 12 | exclude = [] 13 | 14 | [features] 15 | default = [] 16 | arc = [] # Ignored, just used for CI 17 | 18 | [dependencies] 19 | actix-web = "3" 20 | futures-util = "0.3" 21 | 22 | [dependencies.runtime_injector] 23 | version = "0.4" 24 | path = "../runtime_injector" 25 | default_features = false 26 | features = ["arc"] 27 | 28 | [dev-dependencies] 29 | serde = { version = "1", features = ["derive"] } 30 | -------------------------------------------------------------------------------- /crates/runtime_injector_actix/README.md: -------------------------------------------------------------------------------- 1 | # runtime_injector_actix 2 | 3 | [![Current version][crate-badge]][crates-io] 4 | [![Current documentation][doc-badge]][docs] 5 | 6 | This library provides types to help with injecting services into actix-web 7 | applications. 8 | 9 | ## Getting started 10 | 11 | Create your injector, then add it as app data to your application: 12 | 13 | ```rust 14 | #[actix::main] 15 | async fn main() -> std::io::Result<()> { 16 | // Define a module so the container knows what to inject 17 | let module = define_module! { 18 | #[cfg(not(test))] 19 | services = [ 20 | UserAuthenticator::new.transient(), 21 | ], 22 | #[cfg(not(debug_assertions))] 23 | interfaces = { 24 | dyn UserDatabase = [SqlUserDatabase::new.singleton()], 25 | }, 26 | #[cfg(debug_assertions)] 27 | interfaces = { 28 | dyn UserDatabase = [JsonUserDatabase::new.singleton()], 29 | }, 30 | }; 31 | 32 | // Configure and build the container 33 | let mut builder = Injector::builder(); 34 | builder.add_module(module); 35 | let injector = builder.build(); 36 | 37 | // Now add it as app data to the application 38 | HttpServer::new(|| App::new().app_data(injector.clone()).service(index)) 39 | .bind(("127.0.0.1", 8080))? 40 | .run() 41 | .await 42 | } 43 | ``` 44 | 45 | Inject dependencies with `Injected` in your request handlers: 46 | 47 | ```rust 48 | #[get("/")] 49 | async fn index( 50 | user_db: Injected>, 51 | user_auth: Injected>, 52 | ) -> impl Responder { 53 | todo!() 54 | } 55 | ``` 56 | 57 | ## Minimum supported Rust version 58 | 59 | As the library is still in development, the only supported Rust version is the most recent version of stable Rust. The library may work on older versions, but there is no guarantee. 60 | 61 | ## License 62 | 63 | This library is licensed under your choice of either [MIT](./LICENSE-MIT) or [Apache 2.0](./LICENSE-APACHE). 64 | 65 | [crate-badge]: https://img.shields.io/crates/v/runtime_injector_actix?style=flat-square 66 | [doc-badge]: https://img.shields.io/docsrs/runtime_injector_actix?style=flat-square 67 | [crates-io]: https://crates.io/crates/runtime_injector_actix 68 | [docs]: https://docs.rs/runtime_injector_actix 69 | -------------------------------------------------------------------------------- /crates/runtime_injector_actix/examples/query_string_auth.rs: -------------------------------------------------------------------------------- 1 | //! Spawns a web server that listens on localhost. A password must be sent to 2 | //! access the index page via the query string. Try connecting to 3 | //! without any query strings, then connect with the 4 | //! query string `?code=my_secret_password`. The authenticator service is 5 | //! injected via dependency injection into the request handler. 6 | 7 | use actix_web::{ 8 | get, web::Query, App, HttpRequest, HttpResponse, HttpServer, Responder, 9 | }; 10 | use runtime_injector_actix::{ 11 | define_module, Injected, Injector, IntoSingleton, Svc, 12 | }; 13 | use serde::Deserialize; 14 | 15 | #[derive(Default)] 16 | pub struct QueryRequestAuthenticator; 17 | 18 | impl QueryRequestAuthenticator { 19 | fn is_allowed(&self, request: &HttpRequest) -> bool { 20 | #[derive(Deserialize)] 21 | struct QueryData { 22 | code: String, 23 | } 24 | 25 | let query = match Query::::from_query(request.query_string()) 26 | { 27 | Ok(query) => query, 28 | Err(_) => return false, 29 | }; 30 | 31 | query.code == "my_secret_password" 32 | } 33 | } 34 | 35 | fn configure_services() -> Injector { 36 | let module = define_module! { 37 | services = [QueryRequestAuthenticator::default.singleton()] 38 | }; 39 | 40 | let mut builder = Injector::builder(); 41 | builder.add_module(module); 42 | builder.build() 43 | } 44 | 45 | #[actix_web::main] 46 | async fn main() -> std::io::Result<()> { 47 | let injector = configure_services(); 48 | 49 | HttpServer::new(move || { 50 | App::new().app_data(injector.clone()).service(index) 51 | }) 52 | .bind(("127.0.0.1", 8080))? 53 | .run() 54 | .await 55 | } 56 | 57 | #[get("/")] 58 | async fn index( 59 | request: HttpRequest, 60 | auth: Injected>, 61 | ) -> impl Responder { 62 | if auth.is_allowed(&request) { 63 | HttpResponse::Ok().body("You got the password right!") 64 | } else { 65 | HttpResponse::Forbidden().body("Incorrect password") 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /crates/runtime_injector_actix/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Utility library for injecting dependencies into actix-web applications. 2 | 3 | #![forbid(unsafe_code)] 4 | #![deny(clippy::all, clippy::pedantic)] 5 | #![warn(missing_docs)] 6 | #![allow( 7 | clippy::module_name_repetitions, 8 | clippy::missing_errors_doc, 9 | clippy::doc_markdown, 10 | clippy::needless_doctest_main, 11 | clippy::needless_pass_by_value 12 | )] 13 | 14 | pub use runtime_injector::*; 15 | 16 | mod service; 17 | 18 | pub use service::*; 19 | -------------------------------------------------------------------------------- /crates/runtime_injector_actix/src/service.rs: -------------------------------------------------------------------------------- 1 | use actix_web::{ 2 | dev::Payload, error::ErrorInternalServerError, FromRequest, HttpRequest, 3 | }; 4 | use futures_util::future::{err, ok, Ready}; 5 | use runtime_injector::{Injector, Request}; 6 | use std::ops::Deref; 7 | 8 | /// An injected request. Any request to the [`Injector`] can be injected by 9 | /// wrapping it in this type and providing it as a parameter to your request 10 | /// handler. 11 | /// 12 | /// ## Example 13 | /// 14 | /// ```no_run 15 | /// use actix_web::{get, App, HttpResponse, HttpServer, Responder}; 16 | /// use runtime_injector_actix::{ 17 | /// constant, define_module, Injected, Injector, Svc, 18 | /// }; 19 | /// 20 | /// #[actix_web::main] 21 | /// async fn main() -> std::io::Result<()> { 22 | /// let mut builder = Injector::builder(); 23 | /// builder.add_module(define_module! { 24 | /// services = [constant(4i32)], 25 | /// }); 26 | /// 27 | /// let injector = builder.build(); 28 | /// HttpServer::new(move || { 29 | /// App::new().app_data(injector.clone()).service(index) 30 | /// }) 31 | /// .bind(("127.0.0.1", 8080))? 32 | /// .run() 33 | /// .await 34 | /// } 35 | /// 36 | /// #[get("/")] 37 | /// async fn index(my_service: Injected>) -> impl Responder { 38 | /// HttpResponse::Ok().body(format!("injected value is {}", *my_service)) 39 | /// } 40 | /// ``` 41 | #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] 42 | pub struct Injected(R); 43 | 44 | impl Injected { 45 | /// Converts an [`Injected`] to its inner value. 46 | pub fn into_inner(value: Injected) -> R { 47 | value.0 48 | } 49 | } 50 | 51 | impl Deref for Injected { 52 | type Target = R; 53 | 54 | fn deref(&self) -> &Self::Target { 55 | &self.0 56 | } 57 | } 58 | 59 | impl FromRequest for Injected { 60 | type Error = actix_web::Error; 61 | type Future = Ready>; 62 | type Config = (); 63 | 64 | fn from_request(req: &HttpRequest, _payload: &mut Payload) -> Self::Future { 65 | let injector: &Injector = match req.app_data() { 66 | Some(app_data) => app_data, 67 | None => { 68 | return err(ErrorInternalServerError( 69 | "no injector is present in app_data", 70 | )) 71 | } 72 | }; 73 | 74 | let inner = match injector.get() { 75 | Ok(inner) => inner, 76 | Err(error) => return err(ErrorInternalServerError(error)), 77 | }; 78 | 79 | ok(Injected(inner)) 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /rustfmt.toml: -------------------------------------------------------------------------------- 1 | newline_style = "Native" 2 | use_field_init_shorthand = true 3 | max_width = 80 4 | 5 | # Unstable features 6 | unstable_features = true 7 | format_code_in_doc_comments = true 8 | imports_granularity = "Crate" 9 | --------------------------------------------------------------------------------