├── .gitignore ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README-zh_cn.md ├── README.md ├── coverage.sh ├── examples ├── all-scope │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── axum-example │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── condition │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── fake-real-world │ ├── Cargo.toml │ └── src │ │ ├── di.rs │ │ ├── main.rs │ │ ├── self_components.rs │ │ └── third_components.rs ├── hello-world-with-generic │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── hello-world-without-auto-register │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── hello-world-without-macro │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── hello-world │ ├── Cargo.toml │ └── src │ │ └── main.rs ├── leptos-example │ ├── Cargo.toml │ ├── index.html │ └── src │ │ └── main.rs ├── poem-openapi-example │ ├── Cargo.toml │ └── src │ │ └── main.rs └── reference │ ├── Cargo.toml │ └── src │ └── main.rs ├── rudi-core ├── Cargo.toml └── src │ └── lib.rs ├── rudi-macro ├── Cargo.toml └── src │ ├── commons.rs │ ├── di_attr.rs │ ├── docs │ └── attribute_macro.md │ ├── field_or_argument_attr.rs │ ├── impl_fn_or_enum_variant_attr.rs │ ├── item_enum_gen.rs │ ├── item_fn_gen.rs │ ├── item_impl_gen.rs │ ├── item_struct_gen.rs │ ├── lib.rs │ └── struct_or_function_attr.rs └── rudi ├── Cargo.toml ├── src ├── auto_register.rs ├── context.rs ├── definition.rs ├── docs │ └── lib.md ├── future.rs ├── lib.rs ├── macros.rs ├── module.rs ├── provider.rs ├── registry.rs ├── single.rs └── ty.rs └── tests ├── components └── mod.rs ├── context_allow_override.rs ├── context_auto_register.rs ├── context_call_sync_in_async.rs ├── context_circular_dependency.rs ├── context_dynamic_modules.rs ├── context_standalone_variables.rs ├── feat_eager_create_instance.rs ├── feat_generic.rs ├── feat_impl_trait_fn_as_constructor.rs ├── feat_multiple_module.rs ├── feat_override_and_eager_create.rs ├── macro_field_or_argument_attr.rs ├── macro_struct_or_function_attr.rs ├── module_resolve.rs ├── provider_binding.rs ├── provider_override.rs ├── provider_resolve.rs └── scope_behaviour.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | /Cargo.lock 3 | 4 | .DS_Store 5 | 6 | /examples/leptos-example/dist 7 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | edition = "2021" 2 | unstable_features = true 3 | combine_control_expr = false 4 | enum_discrim_align_threshold = 60 5 | format_macro_matchers = true 6 | newline_style = "Unix" 7 | reorder_impl_items = true 8 | # imports 9 | imports_granularity = "Crate" 10 | group_imports = "StdExternalCrate" 11 | # comments 12 | format_code_in_doc_comments = true 13 | normalize_comments = true 14 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | resolver = "2" 3 | members = ["rudi", "rudi-core", "rudi-macro", "examples/*"] 4 | 5 | [workspace.package] 6 | version = "0.8.3" 7 | edition = "2021" 8 | authors = ["zihan "] 9 | license = "MIT/Apache-2.0" 10 | homepage = "https://github.com/ZihanType/rudi" 11 | repository = "https://github.com/ZihanType/rudi" 12 | include = ["src/**/*", "Cargo.toml"] 13 | readme = "README.md" 14 | 15 | [workspace.dependencies] 16 | # self 17 | rudi = { version = "0.8.3", path = "./rudi", default-features = false } 18 | rudi-macro = { version = "0.8.3", path = "./rudi-macro", default-features = false } 19 | rudi-core = { version = "0.1.0", path = "./rudi-core", default-features = false } 20 | 21 | # dependencies 22 | proc-macro2 = { version = "1", default-features = false } 23 | quote = { version = "1", default-features = false } 24 | syn = { version = "2", default-features = false } 25 | from-attr = { version = "0.1", default-features = false } 26 | 27 | # optional dependencies 28 | inventory = { version = "0.3", default-features = false } 29 | tracing = { version = "0.1", default-features = false } 30 | 31 | # dev dependencies 32 | tokio = { version = "1", default-features = false, features = [ 33 | "macros", 34 | "rt-multi-thread", 35 | ] } 36 | async-trait = { version = "0.1", default-features = false } 37 | poem = { version = "3", default-features = false } 38 | poem-openapi = { version = "5", default-features = false } 39 | axum = { version = "0.7", default-features = false } 40 | tracing-subscriber = { version = "0.3", default-features = false, features = [ 41 | "fmt", 42 | "ansi", 43 | "env-filter", 44 | ] } 45 | leptos = { version = "0.7", default-features = false } 46 | 47 | [workspace.lints.rust] 48 | unsafe_code = "forbid" 49 | private_interfaces = "deny" 50 | private_bounds = "deny" 51 | unreachable_pub = "deny" 52 | missing_docs = "warn" 53 | 54 | [workspace.lints.rustdoc] 55 | broken_intra_doc_links = "warn" 56 | -------------------------------------------------------------------------------- /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. -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /README-zh_cn.md: -------------------------------------------------------------------------------- 1 | # Rudi 2 | 3 | [![Crates.io version](https://img.shields.io/crates/v/rudi.svg?style=flat-square)](https://crates.io/crates/rudi) 4 | [![docs.rs docs](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/rudi) 5 | 6 | [English](./README.md) | 简体中文 7 | 8 | Rudi - 一个开箱即用的 Rust 依赖注入框架。 9 | 10 | ```rust 11 | use rudi::{Context, Singleton, Transient}; 12 | 13 | // 将 `fn(cx) -> A { A }` 注册为 `A` 的构造函数 14 | #[derive(Debug)] 15 | #[Transient] 16 | struct A; 17 | 18 | #[derive(Debug)] 19 | struct B(A); 20 | 21 | // 将 `fn(cx) -> B { B::new(cx.resolve::()) }` 注册为 `B` 的构造函数 22 | #[Transient] 23 | impl B { 24 | #[di] 25 | fn new(a: A) -> B { 26 | B(a) 27 | } 28 | } 29 | 30 | // 将 `fn(cx) -> C { C::B(cx.resolve::()) }` 注册为 `C` 的构造函数 31 | #[allow(dead_code)] 32 | #[Transient] 33 | enum C { 34 | A(A), 35 | 36 | #[di] 37 | B(B), 38 | } 39 | 40 | // 将 `fn(cx) -> () { Run(cx.resolve::(), cx.resolve::()) }` 注册为 `()` 的构造函数 41 | #[Singleton] 42 | fn Run(b: B, c: C) { 43 | println!("{:?}", b); 44 | assert!(matches!(c, C::B(_))); 45 | } 46 | 47 | fn main() { 48 | // 自动注册所有标记了 `#[Singleton]`、`#[Transient]` 或 `#[SingleOwner]` 属性宏的类型和函数 49 | let mut cx = Context::auto_register(); 50 | 51 | // 从 `Context` 中获取一个 `()` 的实例,这将会调用 `Run` 函数 52 | // 这等价于 `cx.resolve::<()>();` 53 | cx.resolve() 54 | } 55 | ``` 56 | 57 | ## 特性 58 | 59 | - 3 种作用域: [`Singleton`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.Singleton), [`Transient`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.Transient) and [`SingleOwner`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.SingleOwner) ([example](./examples/all-scope/))。 60 | - 异步函数和异步构造器。 61 | - 可以用在 `struct`、`enum`、`impl block` 和 `function` 上的属性宏。 62 | - 手动注册和自动注册 (感谢 [inventory](https://github.com/dtolnay/inventory))。 63 | - 方便的绑定 trait 实现和 trait 对象。 64 | - 使用类型和名称区分不同的实例。 65 | - 泛型 (但是必须单态化后手动注册) ([example](./examples/hello-world-with-generic/))。 66 | - 条件注册 ([example](./examples/condition/))。 67 | - 引用 (只能是 `Singleton` 和 `SingleOwner` 作用域) ([example](./examples/reference/))。 68 | 69 | ## 一个更复杂的例子 70 | 71 | ```rust 72 | use std::{fmt::Debug, rc::Rc}; 73 | 74 | use rudi::{Context, Singleton, Transient}; 75 | 76 | // 将 `async fn(cx) -> i32 { 42 }` 注册为 `i32` 的构造函数,并将该 `i32` 类型的实例命名为 `"number"` 77 | #[Singleton(name = "number")] 78 | async fn Number() -> i32 { 79 | 42 80 | } 81 | 82 | // 注册 `async fn(cx) -> Foo { Foo { number: cx.resolve_with_name_async("number").await } }` 为 `Foo` 的构造函数, 83 | // 并将该 `Foo` 类型的实例命名为 `"foo"` 84 | #[derive(Debug, Clone)] 85 | #[Singleton(async, name = "foo")] 86 | struct Foo { 87 | #[di(name = "number")] 88 | number: i32, 89 | } 90 | 91 | #[derive(Debug)] 92 | struct Bar(Foo); 93 | 94 | impl Bar { 95 | fn into_debug(self) -> Rc { 96 | Rc::new(self) 97 | } 98 | } 99 | 100 | // 将 `async fn(cx) -> Bar { Bar::new(cx.resolve_with_name_async("foo").await).await }` 注册为 `Bar` 的构造函数, 101 | // 102 | // 将 `Debug` trait 的实现和 `Debug` trait 对象绑定, 103 | // 这将会注册 `async fn(cx) -> Rc { Bar::into_debug(cx.resolve_async().await) }` 为 `Rc` 的构造函数。 104 | #[Transient(binds = [Self::into_debug])] 105 | impl Bar { 106 | #[di] 107 | async fn new(#[di(name = "foo")] f: Foo) -> Bar { 108 | Bar(f) 109 | } 110 | } 111 | 112 | #[Singleton] 113 | async fn Run(bar: Bar, debug: Rc, #[di(name = "foo")] f: Foo) { 114 | println!("{:?}", bar); 115 | assert_eq!(format!("{:?}", bar), format!("{:?}", debug)); 116 | assert_eq!(format!("{:?}", bar.0.number), format!("{:?}", f.number)); 117 | } 118 | 119 | #[tokio::main] 120 | async fn main() { 121 | let mut cx = Context::auto_register(); 122 | 123 | cx.resolve_async().await 124 | } 125 | ``` 126 | 127 | 更多例子可以在 [examples](./examples/) 和 [tests](./rudi/tests/) 目录中找到。 128 | 129 | ## 鸣谢 130 | 131 | - [Koin](https://github.com/InsertKoinIO/koin): 本项目的 API 设计和测试用例受到了 Koin 的启发。 132 | - [inventory](https://github.com/dtolnay/inventory): 本项目使用 inventory 实现了自动注册,使得 Rust 的自动注册变得非常简单。 133 | 134 | ## 做出贡献 135 | 136 | 感谢您的帮助改进项目!我们很高兴有您的加入! 137 | 138 | ## 开源许可 139 | 140 | 本项目使用 Apache-2.0 和 MIT 双重许可,您可以在以下两个许可之一下自由使用本项目的代码: 141 | 142 | - Apache License, Version 2.0,([LICENSE-APACHE](./LICENSE-APACHE) or ) 143 | - MIT license ([LICENSE-MIT](./LICENSE-MIT) or ) 144 | 145 | ### 贡献 146 | 147 | 除非您另有明确声明,否则您有意提交以纳入作品的任何贡献(如 Apache-2.0 许可中的定义)均应获得上述双重许可,且无任何附加条款或条件。 148 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rudi 2 | 3 | [![Crates.io version](https://img.shields.io/crates/v/rudi.svg?style=flat-square)](https://crates.io/crates/rudi) 4 | [![docs.rs docs](https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square)](https://docs.rs/rudi) 5 | 6 | English | [简体中文](./README-zh_cn.md) 7 | 8 | Rudi - an out-of-the-box dependency injection framework for Rust. 9 | 10 | ```rust 11 | use rudi::{Context, Singleton, Transient}; 12 | 13 | // Register `fn(cx) -> A { A }` as the constructor for `A` 14 | #[derive(Debug)] 15 | #[Transient] 16 | struct A; 17 | 18 | #[derive(Debug)] 19 | struct B(A); 20 | 21 | // Register `fn(cx) -> B { B::new(cx.resolve::()) }` as the constructor for `B` 22 | #[Transient] 23 | impl B { 24 | #[di] 25 | fn new(a: A) -> B { 26 | B(a) 27 | } 28 | } 29 | 30 | // Register `fn(cx) -> C { C::B(cx.resolve::()) }` as the constructor for `C` 31 | #[allow(dead_code)] 32 | #[Transient] 33 | enum C { 34 | A(A), 35 | 36 | #[di] 37 | B(B), 38 | } 39 | 40 | // Register `fn(cx) -> () { Run(cx.resolve::(), cx.resolve::()) }` as the constructor for `()` 41 | #[Singleton] 42 | fn Run(b: B, c: C) { 43 | println!("{:?}", b); 44 | assert!(matches!(c, C::B(_))); 45 | } 46 | 47 | fn main() { 48 | // Automatically register all types and functions with the `#[Singleton]`, `#[Transient]` or `#[SingleOwner]` attribute. 49 | let mut cx = Context::auto_register(); 50 | 51 | // Get an instance of `()` from the `Context`, which will call the `Run` function. 52 | // This is equivalent to `cx.resolve::<()>();` 53 | cx.resolve() 54 | } 55 | ``` 56 | 57 | ## Features 58 | 59 | - Three scopes: [`Singleton`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.Singleton), [`Transient`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.Transient) and [`SingleOwner`](https://docs.rs/rudi/latest/rudi/enum.Scope.html#variant.SingleOwner) ([example](./examples/all-scope/)). 60 | - Async functions and async constructors. 61 | - Attribute macros can be used on `struct`, `enum`, `impl block` and `function`. 62 | - Manual and automatic registration (thanks to [inventory](https://github.com/dtolnay/inventory)). 63 | - Easy binding of trait implementations and trait objects. 64 | - Distinguishing different instances with types and names. 65 | - Generics (but must be monomorphized and manually registered) ([example](./examples/hello-world-with-generic/)). 66 | - Conditional registration ([example](./examples/condition/)). 67 | - References (only `Singleton` and `SingleOwner` scope) ([example](./examples/reference/)). 68 | 69 | ## More complex example 70 | 71 | ```rust 72 | use std::{fmt::Debug, rc::Rc}; 73 | 74 | use rudi::{Context, Singleton, Transient}; 75 | 76 | // Register `async fn(cx) -> i32 { 42 }` as the constructor for `i32`, 77 | // and specify the name of the instance of this `i32` type as `"number"`. 78 | #[Singleton(name = "number")] 79 | async fn Number() -> i32 { 80 | 42 81 | } 82 | 83 | // Register `async fn(cx) -> Foo { Foo { number: cx.resolve_with_name_async("number").await } }` 84 | // as the constructor for `Foo`, and specify the name of the instance of this `Foo` type as `"foo"`. 85 | #[derive(Debug, Clone)] 86 | #[Singleton(async, name = "foo")] 87 | struct Foo { 88 | #[di(name = "number")] 89 | number: i32, 90 | } 91 | 92 | #[derive(Debug)] 93 | struct Bar(Foo); 94 | 95 | impl Bar { 96 | fn into_debug(self) -> Rc { 97 | Rc::new(self) 98 | } 99 | } 100 | 101 | // Register `async fn(cx) -> Bar { Bar::new(cx.resolve_with_name_async("foo").await).await }` 102 | // as the constructor for `Bar`. 103 | // 104 | // Bind the implementation of the `Debug` trait and the trait object of the `Debug` trait, 105 | // it will register `asycn fn(cx) -> Rc { Bar::into_debug(cx.resolve_async().await) }` 106 | // as the constructor for `Rc`. 107 | #[Transient(binds = [Self::into_debug])] 108 | impl Bar { 109 | #[di] 110 | async fn new(#[di(name = "foo")] f: Foo) -> Bar { 111 | Bar(f) 112 | } 113 | } 114 | 115 | #[Singleton] 116 | async fn Run(bar: Bar, debug: Rc, #[di(name = "foo")] f: Foo) { 117 | println!("{:?}", bar); 118 | assert_eq!(format!("{:?}", bar), format!("{:?}", debug)); 119 | assert_eq!(format!("{:?}", bar.0.number), format!("{:?}", f.number)); 120 | } 121 | 122 | #[tokio::main] 123 | async fn main() { 124 | let mut cx = Context::auto_register(); 125 | 126 | cx.resolve_async().await 127 | } 128 | ``` 129 | 130 | More examples can be found in the [examples](./examples/) and [tests](./rudi/tests/) directories. 131 | 132 | ## Credits 133 | 134 | - [Koin](https://github.com/InsertKoinIO/koin): This project's API design and test cases were inspired by Koin. 135 | - [inventory](https://github.com/dtolnay/inventory): This project uses inventory to implement automatic registration, making Rust's automatic registration very simple. 136 | 137 | ## Contributing 138 | 139 | Thanks for your help improving the project! We are so happy to have you! 140 | 141 | ## License 142 | 143 | Licensed under either of 144 | 145 | - Apache License, Version 2.0,([LICENSE-APACHE](./LICENSE-APACHE) or ) 146 | - MIT license ([LICENSE-MIT](./LICENSE-MIT) or ) 147 | at your option. 148 | 149 | ### Contribution 150 | 151 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 152 | -------------------------------------------------------------------------------- /coverage.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # rustup component add llvm-tools 4 | # cargo install grcov 5 | # cargo install cargo-nextest --locked 6 | 7 | TMPDIR=`pwd` 8 | cargo clean 9 | CARGO_INCREMENTAL=0 RUSTFLAGS='-Cinstrument-coverage' LLVM_PROFILE_FILE='%t/target/coverage/profraw/%p-%m.profraw' cargo nextest run -p rudi 10 | grcov $TMPDIR/target/coverage/profraw/ --binary-path $TMPDIR/target/debug/ -s . -t html --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o $TMPDIR/target/coverage/ 11 | grcov $TMPDIR/target/coverage/profraw/ --binary-path $TMPDIR/target/debug/ -s . -t lcov --branch --ignore-not-existing --ignore '../*' --ignore "/*" -o $TMPDIR/target/coverage/lcov.info 12 | -------------------------------------------------------------------------------- /examples/all-scope/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "all-scope" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | -------------------------------------------------------------------------------- /examples/all-scope/src/main.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Context, SingleOwner, Singleton, Transient}; 2 | 3 | #[derive(Clone)] 4 | struct Cloneable(i32); 5 | 6 | struct NotCloneable(i32); 7 | 8 | #[Transient(name = "transient_one")] 9 | fn One() -> NotCloneable { 10 | NotCloneable(1) 11 | } 12 | 13 | #[Singleton(name = "singleton_two")] 14 | fn Two() -> Cloneable { 15 | Cloneable(2) 16 | } 17 | 18 | #[SingleOwner(name = "single_owner_three")] 19 | fn Three() -> NotCloneable { 20 | NotCloneable(3) 21 | } 22 | 23 | #[Singleton] 24 | fn Run( 25 | // Transient only get owned 26 | #[di(name = "transient_one")] owned_one: NotCloneable, 27 | // Singleton can get owned and ref, but must implement Clone 28 | #[di(name = "singleton_two")] owned_two: Cloneable, 29 | #[di(name = "singleton_two", ref)] ref_two: &Cloneable, 30 | // SingleOwner only get ref 31 | #[di(name = "single_owner_three", ref)] ref_three: &NotCloneable, 32 | ) { 33 | assert_eq!(owned_one.0, 1); 34 | assert_eq!(owned_two.0, 2); 35 | assert_eq!(ref_two.0, 2); 36 | assert_eq!(ref_three.0, 3); 37 | } 38 | 39 | fn main() { 40 | let mut cx = Context::auto_register(); 41 | cx.resolve() 42 | } 43 | -------------------------------------------------------------------------------- /examples/axum-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "axum-example" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | axum = { workspace = true, features = ["http1", "tokio"] } 16 | tokio = { workspace = true } 17 | tracing-subscriber = { workspace = true } 18 | -------------------------------------------------------------------------------- /examples/axum-example/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use axum::{ 4 | async_trait, 5 | extract::{Path, State}, 6 | routing::{delete, get, post}, 7 | Router, 8 | }; 9 | use rudi::{Context, Singleton}; 10 | use tokio::{net::TcpListener, sync::Mutex}; 11 | use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; 12 | 13 | #[async_trait] 14 | trait Service: Send + Sync { 15 | async fn insert(&self, name: String); 16 | async fn search(&self, name: &str) -> Option; 17 | async fn delete(&self, name: &str); 18 | } 19 | 20 | #[derive(Clone)] 21 | #[Singleton(binds = [Self::into_service])] 22 | struct ServiceImpl { 23 | db: Arc>>, 24 | } 25 | 26 | impl ServiceImpl { 27 | fn into_service(self) -> Arc { 28 | Arc::new(self) 29 | } 30 | } 31 | 32 | #[async_trait] 33 | impl Service for ServiceImpl { 34 | async fn insert(&self, name: String) { 35 | self.db.lock().await.push(name); 36 | } 37 | 38 | async fn search(&self, name: &str) -> Option { 39 | self.db 40 | .lock() 41 | .await 42 | .iter() 43 | .find(|n| n.contains(name)) 44 | .cloned() 45 | } 46 | 47 | async fn delete(&self, name: &str) { 48 | self.db.lock().await.retain(|n| n != name); 49 | } 50 | } 51 | 52 | async fn insert(Path(name): Path, State(svc): State>) { 53 | svc.insert(name).await; 54 | } 55 | 56 | async fn search(Path(name): Path, State(svc): State>) -> String { 57 | svc.search(&name).await.unwrap_or("".to_string()) 58 | } 59 | 60 | async fn del(Path(name): Path, State(svc): State>) { 61 | svc.delete(&name).await; 62 | } 63 | 64 | #[Singleton] 65 | fn EmptyVec() -> Arc>> { 66 | Arc::new(Mutex::new(Vec::new())) 67 | } 68 | 69 | #[Singleton] 70 | async fn Run(svc: Arc) { 71 | let app = Router::new() 72 | .route("/insert/:name", post(insert)) 73 | .route("/search/:name", get(search)) 74 | .route("/delete/:name", delete(del)) 75 | .with_state(svc); 76 | 77 | let listener = TcpListener::bind("0.0.0.0:3000").await.unwrap(); 78 | axum::serve(listener, app).await.unwrap(); 79 | } 80 | 81 | #[tokio::main] 82 | async fn main() { 83 | tracing_subscriber::registry() 84 | .with( 85 | tracing_subscriber::EnvFilter::try_from_default_env() 86 | .unwrap_or_else(|_| "axum_example=debug".into()), 87 | ) 88 | .with(tracing_subscriber::fmt::layer()) 89 | .init(); 90 | 91 | let mut cx = Context::auto_register(); 92 | 93 | // cx.resolve_async::<()>().await; 94 | cx.resolve_async().await 95 | } 96 | -------------------------------------------------------------------------------- /examples/condition/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "condition" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | -------------------------------------------------------------------------------- /examples/condition/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::{collections::HashMap, env, fmt::Debug, rc::Rc}; 2 | 3 | use rudi::{Context, Singleton, Transient}; 4 | 5 | #[derive(Clone)] 6 | struct Environment { 7 | map: HashMap, 8 | } 9 | 10 | #[Singleton(eager_create)] 11 | impl Environment { 12 | #[di] 13 | fn new() -> Self { 14 | Self { 15 | map: env::vars().collect(), 16 | } 17 | } 18 | } 19 | 20 | trait Service: Debug {} 21 | 22 | fn transfer(t: T) -> Rc { 23 | Rc::new(t) 24 | } 25 | 26 | fn condition(cx: &Context, value: &str) -> bool { 27 | cx.get_single::() 28 | .map 29 | .get("env") 30 | .map(|a| a == value) 31 | .unwrap_or(false) 32 | } 33 | 34 | #[derive(Debug)] 35 | #[Transient(condition = |cx| condition(cx, "dev"), binds = [transfer])] 36 | struct A; 37 | 38 | impl Service for A {} 39 | 40 | #[derive(Debug)] 41 | #[Transient(condition = |cx| condition(cx, "prod"), binds = [transfer])] 42 | struct B; 43 | 44 | impl Service for B {} 45 | 46 | fn main() { 47 | env::set_var("env", "dev"); 48 | let mut cx = Context::auto_register(); 49 | let svc = cx.resolve::>(); 50 | assert_eq!(format!("{:?}", svc), "A"); 51 | 52 | env::set_var("env", "prod"); 53 | let mut cx = Context::auto_register(); 54 | let svc = cx.resolve::>(); 55 | assert_eq!(format!("{:?}", svc), "B"); 56 | } 57 | -------------------------------------------------------------------------------- /examples/fake-real-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fake-real-world" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | tokio = { workspace = true } 16 | -------------------------------------------------------------------------------- /examples/fake-real-world/src/di.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Singleton, Transient}; 2 | 3 | use crate::{ 4 | self_components::{DatabaseConfig, RedisConfig}, 5 | third_components::{DatabaseConnection, Middleware, RedisClient}, 6 | }; 7 | 8 | pub const LOG_NAME: &str = "log"; 9 | 10 | pub const fn migrator_name() -> &'static str { 11 | "migrator" 12 | } 13 | 14 | #[Singleton(name = LOG_NAME)] 15 | fn InitLog() { 16 | println!("Init log"); 17 | } 18 | 19 | #[Transient(name = migrator_name())] 20 | async fn Migrator(_database_connection: DatabaseConnection) { 21 | println!("Migrator"); 22 | } 23 | 24 | #[Singleton] 25 | async fn NewRedis(config: RedisConfig) -> RedisClient { 26 | RedisClient::open(&config.url).await 27 | } 28 | 29 | #[Singleton] 30 | async fn NewDatabase(config: DatabaseConfig) -> DatabaseConnection { 31 | DatabaseConnection::open(&config.url).await 32 | } 33 | 34 | #[Transient] 35 | async fn NewMiddleware( 36 | redis_client: RedisClient, 37 | database_connection: DatabaseConnection, 38 | ) -> Middleware { 39 | Middleware::new(redis_client, database_connection) 40 | } 41 | -------------------------------------------------------------------------------- /examples/fake-real-world/src/main.rs: -------------------------------------------------------------------------------- 1 | mod di; 2 | mod self_components; 3 | mod third_components; 4 | 5 | use rudi::{Context, Singleton}; 6 | use self_components::Controller; 7 | use third_components::Middleware; 8 | 9 | #[allow(unused_variables)] 10 | #[Singleton] 11 | async fn Run( 12 | #[di(name = di::LOG_NAME)] _: (), 13 | #[di(name = di::migrator_name())] _: (), 14 | controller: Controller, 15 | middleware: Middleware, 16 | ) { 17 | println!("Hello, world!") 18 | } 19 | 20 | #[tokio::main] 21 | async fn main() { 22 | let mut cx = Context::auto_register(); 23 | cx.resolve_async().await 24 | } 25 | -------------------------------------------------------------------------------- /examples/fake-real-world/src/self_components.rs: -------------------------------------------------------------------------------- 1 | use std::{rc::Rc, sync::Arc}; 2 | 3 | use rudi::Singleton; 4 | 5 | use crate::third_components::{DatabaseConnection, RedisClient}; 6 | 7 | pub trait Service {} 8 | 9 | #[derive(Clone)] 10 | #[Singleton(binds = [Self::into_svc], async)] 11 | pub struct ServiceImpl(RedisClient, DatabaseConnection); 12 | 13 | impl Service for ServiceImpl {} 14 | 15 | impl ServiceImpl { 16 | pub fn into_svc(self) -> Arc { 17 | Arc::new(self) 18 | } 19 | } 20 | 21 | #[allow(dead_code)] 22 | #[derive(Clone)] 23 | #[Singleton(async)] 24 | pub struct Controller(Arc); 25 | 26 | #[derive(Clone)] 27 | pub struct ApplicationConfig; 28 | 29 | #[Singleton(binds = [Rc::new])] 30 | impl ApplicationConfig { 31 | #[di] 32 | fn load() -> Self { 33 | ApplicationConfig 34 | } 35 | } 36 | 37 | #[derive(Clone)] 38 | pub struct RedisConfig { 39 | pub url: String, 40 | } 41 | 42 | #[Singleton] 43 | impl RedisConfig { 44 | #[di] 45 | fn load(#[di(ref)] _application_config: &ApplicationConfig) -> Self { 46 | RedisConfig { 47 | url: "redis://localhost:6379".to_string(), 48 | } 49 | } 50 | } 51 | 52 | #[derive(Clone)] 53 | pub struct DatabaseConfig { 54 | pub url: String, 55 | } 56 | 57 | #[Singleton] 58 | impl DatabaseConfig { 59 | #[di] 60 | fn load(#[di(ref)] _application_config: &ApplicationConfig) -> Self { 61 | DatabaseConfig { 62 | url: "postgres://localhost:5432".to_string(), 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /examples/fake-real-world/src/third_components.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone)] 2 | pub struct RedisClient; 3 | 4 | impl RedisClient { 5 | pub async fn open(_url: &str) -> Self { 6 | RedisClient 7 | } 8 | } 9 | 10 | #[derive(Clone)] 11 | pub struct DatabaseConnection; 12 | 13 | impl DatabaseConnection { 14 | pub async fn open(_url: &str) -> Self { 15 | DatabaseConnection 16 | } 17 | } 18 | 19 | pub struct Middleware; 20 | 21 | impl Middleware { 22 | pub fn new(_redis_client: RedisClient, _database_connection: DatabaseConnection) -> Self { 23 | Middleware 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /examples/hello-world-with-generic/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-with-generic" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro"] } 15 | -------------------------------------------------------------------------------- /examples/hello-world-with-generic/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 4 | 5 | trait Service { 6 | fn hello(&self) -> &str; 7 | } 8 | 9 | #[derive(Clone)] 10 | #[Singleton] 11 | struct ServiceImpl; 12 | 13 | impl Service for ServiceImpl { 14 | fn hello(&self) -> &str { 15 | "Hello World!" 16 | } 17 | } 18 | 19 | #[derive(Clone)] 20 | #[Singleton] 21 | struct Controller 22 | where 23 | T: Clone + 'static, 24 | { 25 | s: T, 26 | } 27 | 28 | impl Controller 29 | where 30 | T: Service + Clone, 31 | { 32 | fn hello(&self) -> &str { 33 | self.s.hello() 34 | } 35 | } 36 | 37 | #[Singleton] 38 | fn Run(controller: Controller) 39 | where 40 | T: Service + Clone + 'static, 41 | { 42 | println!("{}", controller.hello()); 43 | } 44 | 45 | struct MyModule(PhantomData); 46 | 47 | impl Module for MyModule 48 | where 49 | T: Service + Clone + 'static, 50 | { 51 | fn providers() -> Vec { 52 | components![ServiceImpl, Controller, Run] 53 | } 54 | } 55 | 56 | fn main() { 57 | let mut cx = Context::create(modules![MyModule]); 58 | 59 | // cx.resolve::<()>(); 60 | cx.resolve() 61 | } 62 | -------------------------------------------------------------------------------- /examples/hello-world-without-auto-register/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-without-auto-register" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro"] } 15 | -------------------------------------------------------------------------------- /examples/hello-world-without-auto-register/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 4 | 5 | trait Service { 6 | fn hello(&self) -> &str; 7 | } 8 | 9 | #[derive(Clone)] 10 | #[Singleton(name = "hello", binds = [Self::into_service])] 11 | struct ServiceImpl; 12 | 13 | impl ServiceImpl { 14 | fn into_service(self) -> Rc { 15 | Rc::new(self) 16 | } 17 | } 18 | 19 | impl Service for ServiceImpl { 20 | fn hello(&self) -> &str { 21 | "Hello World!" 22 | } 23 | } 24 | 25 | #[derive(Clone)] 26 | #[Singleton(name = "controller")] 27 | struct Controller { 28 | #[di(name = "hello")] 29 | s: Rc, 30 | } 31 | 32 | impl Controller { 33 | fn hello(&self) -> &str { 34 | self.s.hello() 35 | } 36 | } 37 | 38 | #[derive(Clone)] 39 | struct Hello; 40 | 41 | #[Singleton] 42 | impl Hello { 43 | #[di] 44 | fn new() -> Hello { 45 | println!("Hello::new"); 46 | Hello 47 | } 48 | } 49 | 50 | #[Singleton] 51 | fn Run(#[di(name = "controller")] controller: Controller, num: i32, success: bool, _: Hello) { 52 | println!("{}", controller.hello()); 53 | 54 | println!("num: {}", num); 55 | 56 | println!("success: {}", success); 57 | } 58 | 59 | struct MyModule; 60 | 61 | impl Module for MyModule { 62 | fn providers() -> Vec { 63 | components![ServiceImpl, Controller, Hello, Run] 64 | } 65 | } 66 | 67 | fn main() { 68 | let mut cx = Context::options() 69 | .singleton(42) 70 | .singleton(true) 71 | .create(modules![MyModule]); 72 | 73 | // cx.resolve::<()>(); 74 | cx.resolve() 75 | } 76 | -------------------------------------------------------------------------------- /examples/hello-world-without-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world-without-macro" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true } 15 | -------------------------------------------------------------------------------- /examples/hello-world-without-macro/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use rudi::{modules, providers, singleton, Context, DynProvider, Module}; 4 | 5 | trait Service { 6 | fn hello(&self) -> &str; 7 | } 8 | 9 | #[derive(Clone)] 10 | struct ServiceImpl; 11 | 12 | impl ServiceImpl { 13 | fn into_service(self) -> Rc { 14 | Rc::new(self) 15 | } 16 | } 17 | 18 | impl Service for ServiceImpl { 19 | fn hello(&self) -> &str { 20 | "Hello World!" 21 | } 22 | } 23 | 24 | #[derive(Clone)] 25 | struct Controller { 26 | s: Rc, 27 | } 28 | 29 | impl Controller { 30 | fn hello(&self) -> &str { 31 | self.s.hello() 32 | } 33 | } 34 | 35 | #[derive(Clone)] 36 | struct Hello; 37 | 38 | impl Hello { 39 | fn new() -> Hello { 40 | println!("Hello::new"); 41 | Hello 42 | } 43 | } 44 | 45 | fn run(controller: Controller, num: i32, success: bool, _: Hello) { 46 | println!("{}", controller.hello()); 47 | 48 | println!("i32: {}", num); 49 | 50 | println!("bool: {}", success); 51 | } 52 | 53 | struct MyModule; 54 | 55 | impl Module for MyModule { 56 | fn providers() -> Vec { 57 | providers![ 58 | singleton(|_| ServiceImpl) 59 | .name("hello") 60 | .bind(ServiceImpl::into_service), 61 | singleton(|cx| Controller { 62 | s: cx.resolve_with_name("hello") 63 | }) 64 | .name("controller"), 65 | singleton(|_| Hello::new()), 66 | singleton(|cx| run( 67 | cx.resolve_with_name("controller"), 68 | cx.resolve(), 69 | cx.resolve(), 70 | cx.resolve(), 71 | )) 72 | ] 73 | } 74 | } 75 | 76 | fn main() { 77 | let mut cx = Context::options() 78 | .singleton(42) 79 | .singleton(true) 80 | .create(modules![MyModule]); 81 | 82 | // cx.resolve::<()>(); 83 | cx.resolve() 84 | } 85 | -------------------------------------------------------------------------------- /examples/hello-world/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hello-world" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | -------------------------------------------------------------------------------- /examples/hello-world/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use rudi::{Context, Singleton}; 4 | 5 | trait Service { 6 | fn hello(&self) -> &str; 7 | } 8 | 9 | #[derive(Clone)] 10 | #[Singleton(name = "hello", binds = [Self::into_service])] 11 | struct ServiceImpl; 12 | 13 | impl ServiceImpl { 14 | fn into_service(self) -> Rc { 15 | Rc::new(self) 16 | } 17 | } 18 | 19 | impl Service for ServiceImpl { 20 | fn hello(&self) -> &str { 21 | "Hello World!" 22 | } 23 | } 24 | 25 | #[derive(Clone)] 26 | #[Singleton(name = "controller")] 27 | struct Controller { 28 | #[di(name = "hello")] 29 | s: Rc, 30 | } 31 | 32 | impl Controller { 33 | fn hello(&self) -> &str { 34 | self.s.hello() 35 | } 36 | } 37 | 38 | #[derive(Clone)] 39 | struct Hello; 40 | 41 | #[Singleton] 42 | impl Hello { 43 | #[di] 44 | fn new() -> Hello { 45 | println!("Hello::new"); 46 | Hello 47 | } 48 | } 49 | 50 | #[Singleton] 51 | fn Run(#[di(name = "controller")] controller: Controller, num: i32, success: bool, _: Hello) { 52 | println!("{}", controller.hello()); 53 | 54 | println!("num: {}", num); 55 | 56 | println!("success: {}", success); 57 | } 58 | 59 | fn main() { 60 | let mut cx = Context::options() 61 | .singleton(42) 62 | .singleton(true) 63 | .auto_register(); 64 | 65 | // cx.resolve::<()>(); 66 | cx.resolve() 67 | } 68 | -------------------------------------------------------------------------------- /examples/leptos-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "leptos-example" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro"] } 15 | leptos = { workspace = true, features = ["csr"] } 16 | -------------------------------------------------------------------------------- /examples/leptos-example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Rudi-Leptos 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /examples/leptos-example/src/main.rs: -------------------------------------------------------------------------------- 1 | use leptos::prelude::*; 2 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 3 | 4 | #[component] 5 | pub fn SimpleCounter(initial_value: i32, step: i32) -> impl IntoView { 6 | let value = RwSignal::new(initial_value); 7 | 8 | view! { 9 |
10 | 11 | 12 | "Value: " { move || value.get() } "!" 13 | 14 |
15 | } 16 | } 17 | 18 | #[Singleton] 19 | fn Number() -> i32 { 20 | 42 21 | } 22 | 23 | #[Singleton] 24 | fn Run(initial_value: i32) { 25 | mount_to_body(move || { 26 | view! { 27 | 31 | } 32 | }) 33 | } 34 | 35 | struct MyModule; 36 | 37 | impl Module for MyModule { 38 | fn providers() -> Vec { 39 | components![Number, Run] 40 | } 41 | } 42 | 43 | fn main() { 44 | let mut cx = Context::create(modules![MyModule]); 45 | cx.resolve() 46 | } 47 | -------------------------------------------------------------------------------- /examples/poem-openapi-example/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "poem-openapi-example" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | async-trait = { workspace = true } 16 | poem = { workspace = true } 17 | poem-openapi = { workspace = true, features = ["swagger-ui"] } 18 | tokio = { workspace = true } 19 | tracing-subscriber = { workspace = true } 20 | -------------------------------------------------------------------------------- /examples/poem-openapi-example/src/main.rs: -------------------------------------------------------------------------------- 1 | use std::sync::Arc; 2 | 3 | use async_trait::async_trait; 4 | use poem::{ 5 | listener::TcpListener, 6 | middleware::{Cors, Tracing}, 7 | EndpointExt, Route, Server, 8 | }; 9 | use poem_openapi::{param::Query, payload::PlainText, OpenApi, OpenApiService}; 10 | use rudi::{Context, Singleton}; 11 | use tokio::sync::Mutex; 12 | 13 | #[async_trait] 14 | trait Service: Send + Sync { 15 | async fn insert(&self, name: String); 16 | async fn search(&self, name: &str) -> Option; 17 | async fn delete(&self, name: &str); 18 | } 19 | 20 | #[derive(Clone)] 21 | #[Singleton(binds = [Self::into_service])] 22 | struct ServiceImpl { 23 | db: Arc>>, 24 | } 25 | 26 | impl ServiceImpl { 27 | fn into_service(self) -> Arc { 28 | Arc::new(self) 29 | } 30 | } 31 | 32 | #[async_trait] 33 | impl Service for ServiceImpl { 34 | async fn insert(&self, name: String) { 35 | self.db.lock().await.push(name); 36 | } 37 | 38 | async fn search(&self, name: &str) -> Option { 39 | self.db 40 | .lock() 41 | .await 42 | .iter() 43 | .find(|n| n.contains(name)) 44 | .cloned() 45 | } 46 | 47 | async fn delete(&self, name: &str) { 48 | self.db.lock().await.retain(|n| n != name); 49 | } 50 | } 51 | 52 | #[derive(Clone)] 53 | #[Singleton] 54 | struct Controller { 55 | svc: Arc, 56 | } 57 | 58 | #[OpenApi] 59 | impl Controller { 60 | #[oai(path = "/insert", method = "post")] 61 | async fn insert(&self, Query(name): Query) { 62 | self.svc.insert(name).await; 63 | } 64 | 65 | #[oai(path = "/search", method = "get")] 66 | async fn search(&self, Query(name): Query) -> PlainText { 67 | PlainText(self.svc.search(&name).await.unwrap_or("".to_string())) 68 | } 69 | 70 | #[oai(path = "/delete", method = "delete")] 71 | async fn delete(&self, Query(name): Query) { 72 | self.svc.delete(&name).await; 73 | } 74 | } 75 | 76 | #[Singleton] 77 | fn EmptyVec() -> Arc>> { 78 | Arc::new(Mutex::new(Vec::new())) 79 | } 80 | 81 | #[Singleton] 82 | async fn Run(controller: Controller) { 83 | let api_service = 84 | OpenApiService::new(controller, "Hello World", "1.0").server("http://localhost:3000/api"); 85 | let ui = api_service.swagger_ui(); 86 | 87 | Server::new(TcpListener::bind("127.0.0.1:3000")) 88 | .run( 89 | Route::new() 90 | .nest("/api", api_service) 91 | .nest("/", ui) 92 | .with(Cors::new()) 93 | .with(Tracing), 94 | ) 95 | .await 96 | .unwrap() 97 | } 98 | 99 | #[tokio::main] 100 | async fn main() { 101 | if std::env::var_os("RUST_LOG").is_none() { 102 | std::env::set_var("RUST_LOG", "poem=debug"); 103 | } 104 | tracing_subscriber::fmt::init(); 105 | 106 | let mut cx = Context::auto_register(); 107 | 108 | // cx.resolve_async::<()>().await; 109 | cx.resolve_async().await 110 | } 111 | -------------------------------------------------------------------------------- /examples/reference/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "reference" 3 | version.workspace = true 4 | edition.workspace = true 5 | authors.workspace = true 6 | license.workspace = true 7 | homepage.workspace = true 8 | repository.workspace = true 9 | include.workspace = true 10 | 11 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 12 | 13 | [dependencies] 14 | rudi = { workspace = true, features = ["rudi-macro", "auto-register"] } 15 | -------------------------------------------------------------------------------- /examples/reference/src/main.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Context, SingleOwner, Singleton, Transient}; 2 | 3 | #[SingleOwner] // SingleOwner scope 4 | struct NotCloneable; 5 | 6 | #[Singleton] // Singleton scope 7 | struct Cloneable; 8 | 9 | // Singleton must implement Clone 10 | impl Clone for Cloneable { 11 | fn clone(&self) -> Self { 12 | unimplemented!("actually this method will not be called") 13 | } 14 | } 15 | 16 | struct DbConfig; 17 | 18 | #[Transient] 19 | impl DbConfig { 20 | // from reference 21 | #[di] 22 | fn from_single_owner_reference(#[di(ref)] _: &NotCloneable) -> Self { 23 | Self 24 | } 25 | } 26 | 27 | struct RedisConfig; 28 | 29 | #[Transient] 30 | impl RedisConfig { 31 | // from option reference 32 | #[di] 33 | fn from_singleton_reference(#[di(option, ref)] _: Option<&Cloneable>) -> Self { 34 | Self 35 | } 36 | } 37 | 38 | #[Singleton] 39 | fn Run(_: DbConfig, _: RedisConfig) { 40 | println!("run!"); 41 | } 42 | 43 | fn main() { 44 | let mut cx = Context::auto_register(); 45 | cx.resolve() 46 | } 47 | -------------------------------------------------------------------------------- /rudi-core/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rudi-core" 3 | version = "0.1.0" 4 | description = "Rudi core types" 5 | keywords = ["dependency-injection", "ioc", "di", "dependency"] 6 | edition.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | include.workspace = true 12 | readme.workspace = true 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | -------------------------------------------------------------------------------- /rudi-core/src/lib.rs: -------------------------------------------------------------------------------- 1 | /// Represents the scope of the provider. 2 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 3 | pub enum Scope { 4 | /// singleton scope. 5 | /// 6 | /// 1. the constructor run only once. 7 | /// 2. the type implements [`Clone`] trait. 8 | /// 3. instances taken from context can be either instances with ownership or reference instances. 9 | Singleton, 10 | /// transient scope. 11 | /// 12 | /// 1. the constructor run every time. 13 | /// 2. instances taken from the context are instances with ownership. 14 | Transient, 15 | /// single owner scope. 16 | /// 17 | /// 1. the constructor run only once. 18 | /// 2. instances taken from the context are reference instances. 19 | SingleOwner, 20 | } 21 | 22 | /// Represents the color of the function, i.e., async or sync. 23 | #[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)] 24 | pub enum Color { 25 | /// async function 26 | Async, 27 | /// sync function 28 | Sync, 29 | } 30 | -------------------------------------------------------------------------------- /rudi-macro/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rudi-macro" 3 | description = "Macros for Rudi." 4 | keywords = ["dependency-injection", "ioc", "di", "dependency"] 5 | version.workspace = true 6 | edition.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | include.workspace = true 12 | readme.workspace = true 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [lib] 17 | proc-macro = true 18 | 19 | [dependencies] 20 | rudi-core = { workspace = true } 21 | from-attr = { workspace = true } 22 | proc-macro2 = { workspace = true } 23 | quote = { workspace = true } 24 | syn = { workspace = true, features = [ 25 | "extra-traits", 26 | "parsing", 27 | "proc-macro", 28 | "printing", 29 | "full", 30 | ] } 31 | 32 | [dev-dependencies] 33 | # cannot contain `workspace = true` to avoid circular dependencies. 34 | rudi = { path = "../rudi", default-features = false, features = [ 35 | "rudi-macro", 36 | "auto-register", 37 | ] } 38 | 39 | tokio = { workspace = true } 40 | 41 | [features] 42 | default = ["auto-register"] 43 | auto-register = [] 44 | -------------------------------------------------------------------------------- /rudi-macro/src/commons.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{AttrsValue, FlagOrValue, FromAttr}; 2 | use proc_macro2::TokenStream; 3 | use quote::{format_ident, quote}; 4 | use rudi_core::{Color, Scope}; 5 | use syn::{ 6 | parse_quote, punctuated::Punctuated, spanned::Spanned, AngleBracketedGenericArguments, 7 | Attribute, Field, Fields, FieldsNamed, FieldsUnnamed, FnArg, GenericArgument, Ident, PatType, 8 | Path, PathArguments, PathSegment, Stmt, Token, Type, TypePath, TypeReference, 9 | }; 10 | 11 | use crate::field_or_argument_attr::FieldOrArgumentAttr; 12 | 13 | pub(crate) fn generate_create_provider(scope: Scope, color: Color) -> TokenStream { 14 | match (scope, color) { 15 | (Scope::Singleton, Color::Async) => quote! { 16 | singleton_async 17 | }, 18 | (Scope::Singleton, Color::Sync) => quote! { 19 | singleton 20 | }, 21 | (Scope::Transient, Color::Async) => quote! { 22 | transient_async 23 | }, 24 | (Scope::Transient, Color::Sync) => quote! { 25 | transient 26 | }, 27 | (Scope::SingleOwner, Color::Async) => quote! { 28 | single_owner_async 29 | }, 30 | (Scope::SingleOwner, Color::Sync) => quote! { 31 | single_owner 32 | }, 33 | } 34 | } 35 | 36 | fn extract_ref_type(ty: &Type) -> syn::Result<&Type> { 37 | fn require_type_ref(ty: &Type) -> Option<&TypeReference> { 38 | match ty { 39 | Type::Reference(type_ref) => Some(type_ref), 40 | _ => None, 41 | } 42 | } 43 | 44 | fn get_type_from_ref( 45 | TypeReference { 46 | mutability, elem, .. 47 | }: &TypeReference, 48 | ) -> syn::Result<&Type> { 49 | if mutability.is_some() { 50 | Err(syn::Error::new( 51 | mutability.span(), 52 | "not support mutable reference", 53 | )) 54 | } else { 55 | Ok(elem) 56 | } 57 | } 58 | 59 | let mut ty: &Type = match require_type_ref(ty) { 60 | Some(type_ref) => get_type_from_ref(type_ref)?, 61 | None => { 62 | return Err(syn::Error::new( 63 | ty.span(), 64 | "not support non-reference type, \ 65 | please change to a reference type, \ 66 | or if using a type alias, specify the original type using `#[di(ref = T)]`, \ 67 | where `T` is a non-reference type", 68 | )) 69 | } 70 | }; 71 | 72 | loop { 73 | ty = match require_type_ref(ty) { 74 | Some(type_ref) => get_type_from_ref(type_ref)?, 75 | None => break, 76 | }; 77 | } 78 | 79 | Ok(ty) 80 | } 81 | 82 | fn extract_path_type<'a>(ty: &'a Type, ty_name: &str) -> syn::Result<&'a Type> { 83 | let Type::Path(TypePath { 84 | qself: None, 85 | path: Path { 86 | leading_colon: None, 87 | segments, 88 | }, 89 | }) = ty 90 | else { 91 | return Err(syn::Error::new( 92 | ty.span(), 93 | format!("only support `{}` type", ty_name), 94 | )); 95 | }; 96 | 97 | let Some(segment) = segments.last() else { 98 | return Err(syn::Error::new( 99 | ty.span(), 100 | "not support path type with empty segments", 101 | )); 102 | }; 103 | 104 | let PathSegment { 105 | ident, 106 | arguments: PathArguments::AngleBracketed(AngleBracketedGenericArguments { args, .. }), 107 | } = segment 108 | else { 109 | return Err(syn::Error::new( 110 | segment.span(), 111 | "only support angle bracketed generic argument", 112 | )); 113 | }; 114 | 115 | if ident != ty_name { 116 | return Err(syn::Error::new( 117 | ident.span(), 118 | format!("only support `{}` type", ty_name), 119 | )); 120 | } 121 | 122 | let Some(arg) = args.first() else { 123 | return Err(syn::Error::new( 124 | segment.span(), 125 | format!( 126 | "not support `{}` type with empty generic arguments ", 127 | ty_name 128 | ), 129 | )); 130 | }; 131 | 132 | if args.len() > 1 { 133 | let msg = format!( 134 | "only support `{}` type with one generic argument", 135 | ty_name 136 | ); 137 | 138 | if let Some(e) = args 139 | .iter() 140 | .skip(1) 141 | .map(|arg| syn::Error::new(arg.span(), &msg)) 142 | .reduce(|mut a, b| { 143 | a.combine(b); 144 | a 145 | }) 146 | { 147 | return Err(e); 148 | } 149 | } 150 | 151 | if let GenericArgument::Type(ty) = arg { 152 | extract_ref_type(ty) 153 | } else { 154 | Err(syn::Error::new( 155 | arg.span(), 156 | "only support generic argument type", 157 | )) 158 | } 159 | } 160 | 161 | fn extract_option_type(ty: &Type) -> syn::Result<&Type> { 162 | extract_path_type(ty, "Option") 163 | } 164 | 165 | fn extract_vec_type(ty: &Type) -> syn::Result<&Type> { 166 | extract_path_type(ty, "Vec") 167 | } 168 | 169 | enum ResolveOneValue { 170 | Owned { 171 | resolve: Stmt, 172 | }, 173 | Ref { 174 | create_single: Stmt, 175 | get_single: Stmt, 176 | }, 177 | } 178 | 179 | struct ResolveOne { 180 | stmt: ResolveOneValue, 181 | variable: Ident, 182 | } 183 | 184 | fn generate_only_one_field_or_argument_resolve_stmt( 185 | attrs: &mut Vec, 186 | color: Color, 187 | index: usize, 188 | field_or_argument_ty: &Type, 189 | ) -> syn::Result { 190 | let FieldOrArgumentAttr { 191 | name, 192 | option, 193 | default, 194 | vec, 195 | ref_, 196 | } = match FieldOrArgumentAttr::remove_attributes(attrs) { 197 | Ok(Some(AttrsValue { value, .. })) => value, 198 | Ok(None) => FieldOrArgumentAttr::default(), 199 | Err(AttrsValue { value, .. }) => return Err(value), 200 | }; 201 | 202 | let ident = match ref_ { 203 | FlagOrValue::None => format_ident!("owned_{}", index), 204 | FlagOrValue::Flag { .. } | FlagOrValue::Value { .. } => format_ident!("ref_{}", index), 205 | }; 206 | 207 | if option { 208 | let ty = match ref_ { 209 | FlagOrValue::None => None, 210 | FlagOrValue::Flag { .. } => { 211 | let ty = extract_option_type(field_or_argument_ty)?; 212 | Some(quote!(#ty)) 213 | } 214 | FlagOrValue::Value { value: ty, .. } => Some(quote!(#ty)), 215 | }; 216 | 217 | return match ty { 218 | Some(ty) => { 219 | let create_single = match color { 220 | Color::Async => parse_quote! { 221 | cx.try_just_create_single_with_name_async::<#ty>(#name).await; 222 | }, 223 | Color::Sync => parse_quote! { 224 | cx.try_just_create_single_with_name::<#ty>(#name); 225 | }, 226 | }; 227 | 228 | let get_single = parse_quote! { 229 | let #ident = cx.get_single_option_with_name(#name); 230 | }; 231 | 232 | Ok(ResolveOne { 233 | stmt: ResolveOneValue::Ref { 234 | create_single, 235 | get_single, 236 | }, 237 | variable: ident, 238 | }) 239 | } 240 | None => { 241 | let resolve = match color { 242 | Color::Async => parse_quote! { 243 | let #ident = cx.resolve_option_with_name_async(#name).await; 244 | }, 245 | Color::Sync => parse_quote! { 246 | let #ident = cx.resolve_option_with_name(#name); 247 | }, 248 | }; 249 | 250 | Ok(ResolveOne { 251 | stmt: ResolveOneValue::Owned { resolve }, 252 | variable: ident, 253 | }) 254 | } 255 | }; 256 | } 257 | 258 | let default = match default { 259 | FlagOrValue::None => None, 260 | FlagOrValue::Flag { .. } => Some(parse_quote!(::core::default::Default::default())), 261 | FlagOrValue::Value { value: expr, .. } => Some(expr), 262 | }; 263 | 264 | if let Some(default) = default { 265 | let ty = match ref_ { 266 | FlagOrValue::None => None, 267 | FlagOrValue::Flag { .. } => { 268 | let ty = extract_ref_type(field_or_argument_ty)?; 269 | Some(quote!(#ty)) 270 | } 271 | FlagOrValue::Value { value: ty, .. } => Some(quote!(#ty)), 272 | }; 273 | 274 | return match ty { 275 | Some(ty) => { 276 | let create_single = match color { 277 | Color::Async => parse_quote! { 278 | cx.try_just_create_single_with_name_async::<#ty>(#name).await; 279 | }, 280 | Color::Sync => parse_quote! { 281 | cx.try_just_create_single_with_name::<#ty>(#name); 282 | }, 283 | }; 284 | 285 | let get_single = parse_quote! { 286 | let #ident = match cx.get_single_option_with_name(#name) { 287 | Some(value) => value, 288 | None => #default, 289 | }; 290 | }; 291 | 292 | Ok(ResolveOne { 293 | stmt: ResolveOneValue::Ref { 294 | create_single, 295 | get_single, 296 | }, 297 | variable: ident, 298 | }) 299 | } 300 | None => { 301 | let resolve = match color { 302 | Color::Async => parse_quote! { 303 | let #ident = match cx.resolve_option_with_name_async(#name).await { 304 | Some(value) => value, 305 | None => #default, 306 | }; 307 | }, 308 | Color::Sync => parse_quote! { 309 | let #ident = match cx.resolve_option_with_name(#name) { 310 | Some(value) => value, 311 | None => #default, 312 | }; 313 | }, 314 | }; 315 | 316 | Ok(ResolveOne { 317 | stmt: ResolveOneValue::Owned { resolve }, 318 | variable: ident, 319 | }) 320 | } 321 | }; 322 | } 323 | 324 | if vec { 325 | let ty = match ref_ { 326 | FlagOrValue::None => None, 327 | FlagOrValue::Flag { .. } => { 328 | let ty = extract_vec_type(field_or_argument_ty)?; 329 | Some(quote!(#ty)) 330 | } 331 | FlagOrValue::Value { value: ty, .. } => Some(quote!(#ty)), 332 | }; 333 | 334 | return match ty { 335 | Some(ty) => { 336 | let create_single = match color { 337 | Color::Async => parse_quote! { 338 | cx.try_just_create_singles_by_type_async::<#ty>().await; 339 | }, 340 | Color::Sync => parse_quote! { 341 | cx.try_just_create_singles_by_type::<#ty>(); 342 | }, 343 | }; 344 | 345 | let get_single = parse_quote! { 346 | let #ident = cx.get_singles_by_type(); 347 | }; 348 | 349 | Ok(ResolveOne { 350 | stmt: ResolveOneValue::Ref { 351 | create_single, 352 | get_single, 353 | }, 354 | variable: ident, 355 | }) 356 | } 357 | None => { 358 | let resolve = match color { 359 | Color::Async => parse_quote! { 360 | let #ident = cx.resolve_by_type_async().await; 361 | }, 362 | Color::Sync => parse_quote! { 363 | let #ident = cx.resolve_by_type(); 364 | }, 365 | }; 366 | 367 | Ok(ResolveOne { 368 | stmt: ResolveOneValue::Owned { resolve }, 369 | variable: ident, 370 | }) 371 | } 372 | }; 373 | } 374 | 375 | let ty = match ref_ { 376 | FlagOrValue::None => None, 377 | FlagOrValue::Flag { .. } => { 378 | let ty = extract_ref_type(field_or_argument_ty)?; 379 | Some(quote!(#ty)) 380 | } 381 | FlagOrValue::Value { value: ty, .. } => Some(quote!(#ty)), 382 | }; 383 | 384 | match ty { 385 | Some(ty) => { 386 | let create_single = match color { 387 | Color::Async => parse_quote! { 388 | cx.just_create_single_with_name_async::<#ty>(#name).await; 389 | }, 390 | Color::Sync => parse_quote! { 391 | cx.just_create_single_with_name::<#ty>(#name); 392 | }, 393 | }; 394 | 395 | let get_single = parse_quote! { 396 | let #ident = cx.get_single_with_name(#name); 397 | }; 398 | 399 | Ok(ResolveOne { 400 | stmt: ResolveOneValue::Ref { 401 | create_single, 402 | get_single, 403 | }, 404 | variable: ident, 405 | }) 406 | } 407 | None => { 408 | let resolve = match color { 409 | Color::Async => parse_quote! { 410 | let #ident = cx.resolve_with_name_async(#name).await; 411 | }, 412 | Color::Sync => parse_quote! { 413 | let #ident = cx.resolve_with_name(#name); 414 | }, 415 | }; 416 | 417 | Ok(ResolveOne { 418 | stmt: ResolveOneValue::Owned { resolve }, 419 | variable: ident, 420 | }) 421 | } 422 | } 423 | } 424 | 425 | pub(crate) struct ArgumentResolveStmts { 426 | pub(crate) ref_mut_cx_stmts: Vec, 427 | pub(crate) ref_cx_stmts: Vec, 428 | pub(crate) args: Vec, 429 | } 430 | 431 | pub(crate) fn generate_argument_resolve_methods( 432 | inputs: &mut Punctuated, 433 | color: Color, 434 | ) -> syn::Result { 435 | let capacity = inputs.len(); 436 | 437 | let mut ref_mut_cx_stmts = Vec::with_capacity(capacity); 438 | let mut ref_cx_stmts = Vec::with_capacity(capacity); 439 | let mut args = Vec::with_capacity(capacity); 440 | 441 | for (index, input) in inputs.iter_mut().enumerate() { 442 | match input { 443 | FnArg::Receiver(r) => { 444 | return Err(syn::Error::new(r.span(), "not support `self` receiver")) 445 | } 446 | FnArg::Typed(PatType { attrs, ty, .. }) => { 447 | let ResolveOne { stmt, variable } = 448 | generate_only_one_field_or_argument_resolve_stmt(attrs, color, index, ty)?; 449 | 450 | match stmt { 451 | ResolveOneValue::Owned { resolve } => ref_mut_cx_stmts.push(resolve), 452 | ResolveOneValue::Ref { 453 | create_single, 454 | get_single, 455 | } => { 456 | ref_mut_cx_stmts.push(create_single); 457 | ref_cx_stmts.push(get_single); 458 | } 459 | } 460 | 461 | args.push(variable); 462 | } 463 | } 464 | } 465 | 466 | Ok(ArgumentResolveStmts { 467 | ref_mut_cx_stmts, 468 | ref_cx_stmts, 469 | args, 470 | }) 471 | } 472 | 473 | #[cfg(feature = "auto-register")] 474 | pub(crate) enum ItemKind { 475 | Struct, 476 | Enum, 477 | Function, 478 | 479 | // impl block 480 | StructOrEnum, 481 | } 482 | 483 | #[cfg(feature = "auto-register")] 484 | impl ItemKind { 485 | pub(crate) fn as_str(&self) -> &'static str { 486 | match self { 487 | ItemKind::Struct => "struct", 488 | ItemKind::Enum => "enum", 489 | ItemKind::Function => "function", 490 | ItemKind::StructOrEnum => "struct or enum", 491 | } 492 | } 493 | } 494 | 495 | #[cfg(feature = "auto-register")] 496 | pub(crate) fn check_generics_when_enable_auto_register( 497 | auto_register: bool, 498 | generics: &syn::Generics, 499 | item_kind: ItemKind, 500 | scope: Scope, 501 | ) -> syn::Result<()> { 502 | if auto_register && !generics.params.is_empty() { 503 | return Err(syn::Error::new( 504 | generics.span(), 505 | format!( 506 | "not support auto register generics {}, \ 507 | please remove generics, or use `#[{:?}(auto_register = false)]` to disable auto register", 508 | item_kind.as_str(), 509 | scope 510 | ), 511 | )); 512 | } 513 | 514 | Ok(()) 515 | } 516 | 517 | pub(crate) struct FieldResolveStmts { 518 | pub(crate) ref_mut_cx_stmts: Vec, 519 | pub(crate) ref_cx_stmts: Vec, 520 | pub(crate) fields: ResolvedFields, 521 | } 522 | 523 | pub(crate) enum ResolvedFields { 524 | Unit, 525 | Named { 526 | field_names: Vec, 527 | field_values: Vec, 528 | }, 529 | Unnamed(Vec), 530 | } 531 | 532 | pub(crate) fn generate_field_resolve_stmts( 533 | fields: &mut Fields, 534 | color: Color, 535 | ) -> syn::Result { 536 | match fields { 537 | Fields::Unit => Ok(FieldResolveStmts { 538 | ref_mut_cx_stmts: Vec::new(), 539 | ref_cx_stmts: Vec::new(), 540 | fields: ResolvedFields::Unit, 541 | }), 542 | Fields::Named(FieldsNamed { named, .. }) => { 543 | let capacity = named.len(); 544 | 545 | let mut ref_mut_cx_stmts = Vec::with_capacity(capacity); 546 | let mut ref_cx_stmts = Vec::with_capacity(capacity); 547 | let mut field_values = Vec::with_capacity(capacity); 548 | 549 | let mut field_names = Vec::with_capacity(capacity); 550 | 551 | for ( 552 | index, 553 | Field { 554 | attrs, 555 | ident: field_name, 556 | ty, 557 | .. 558 | }, 559 | ) in named.into_iter().enumerate() 560 | { 561 | let ResolveOne { 562 | stmt, 563 | variable: field_value, 564 | } = generate_only_one_field_or_argument_resolve_stmt(attrs, color, index, ty)?; 565 | 566 | match stmt { 567 | ResolveOneValue::Owned { resolve } => ref_mut_cx_stmts.push(resolve), 568 | ResolveOneValue::Ref { 569 | create_single, 570 | get_single, 571 | } => { 572 | ref_mut_cx_stmts.push(create_single); 573 | ref_cx_stmts.push(get_single); 574 | } 575 | } 576 | 577 | field_values.push(field_value); 578 | field_names.push(field_name.clone().unwrap()); 579 | } 580 | 581 | Ok(FieldResolveStmts { 582 | ref_mut_cx_stmts, 583 | ref_cx_stmts, 584 | fields: ResolvedFields::Named { 585 | field_names, 586 | field_values, 587 | }, 588 | }) 589 | } 590 | Fields::Unnamed(FieldsUnnamed { unnamed, .. }) => { 591 | let capacity = unnamed.len(); 592 | 593 | let mut ref_mut_cx_stmts = Vec::with_capacity(capacity); 594 | let mut ref_cx_stmts = Vec::with_capacity(capacity); 595 | let mut field_values = Vec::with_capacity(capacity); 596 | 597 | for (index, Field { attrs, ty, .. }) in unnamed.into_iter().enumerate() { 598 | let ResolveOne { 599 | stmt, 600 | variable: field_value, 601 | } = generate_only_one_field_or_argument_resolve_stmt(attrs, color, index, ty)?; 602 | 603 | match stmt { 604 | ResolveOneValue::Owned { resolve } => ref_mut_cx_stmts.push(resolve), 605 | ResolveOneValue::Ref { 606 | create_single, 607 | get_single, 608 | } => { 609 | ref_mut_cx_stmts.push(create_single); 610 | ref_cx_stmts.push(get_single); 611 | } 612 | } 613 | 614 | field_values.push(field_value); 615 | } 616 | 617 | Ok(FieldResolveStmts { 618 | ref_mut_cx_stmts, 619 | ref_cx_stmts, 620 | fields: ResolvedFields::Unnamed(field_values), 621 | }) 622 | } 623 | } 624 | } 625 | -------------------------------------------------------------------------------- /rudi-macro/src/di_attr.rs: -------------------------------------------------------------------------------- 1 | use from_attr::FromAttr; 2 | use syn::{parse_quote, Path}; 3 | 4 | // #[di(rudi_path = path::to::rudi)] 5 | 6 | #[derive(FromAttr)] 7 | #[attribute(idents = [di])] 8 | pub(crate) struct DiAttr { 9 | #[attribute(default = default_rudi_path())] 10 | pub(crate) rudi_path: Path, 11 | } 12 | 13 | fn default_rudi_path() -> Path { 14 | parse_quote!(::rudi) 15 | } 16 | 17 | impl Default for DiAttr { 18 | fn default() -> Self { 19 | Self { 20 | rudi_path: default_rudi_path(), 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /rudi-macro/src/field_or_argument_attr.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{FlagOrValue, FromAttr}; 2 | use syn::{parse_quote, Expr, Type}; 3 | 4 | // #[di( 5 | // name = "..", 6 | // option, 7 | // default = 42, 8 | // vec, 9 | // ref = T 10 | // )] 11 | 12 | #[derive(FromAttr)] 13 | #[attribute(idents = [di])] 14 | pub(crate) struct FieldOrArgumentAttr { 15 | #[attribute(default = default_name(), conflicts = [vec])] 16 | pub(crate) name: Expr, 17 | 18 | #[attribute(conflicts = [default, vec])] 19 | pub(crate) option: bool, 20 | 21 | #[attribute(conflicts = [option, vec])] 22 | pub(crate) default: FlagOrValue, 23 | 24 | #[attribute(conflicts = [name, option, default])] 25 | pub(crate) vec: bool, 26 | 27 | #[attribute(rename = "ref")] 28 | pub(crate) ref_: FlagOrValue, 29 | } 30 | 31 | fn default_name() -> Expr { 32 | parse_quote!("") 33 | } 34 | 35 | impl Default for FieldOrArgumentAttr { 36 | fn default() -> Self { 37 | Self { 38 | name: default_name(), 39 | option: Default::default(), 40 | default: Default::default(), 41 | vec: Default::default(), 42 | ref_: Default::default(), 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /rudi-macro/src/impl_fn_or_enum_variant_attr.rs: -------------------------------------------------------------------------------- 1 | use from_attr::FromAttr; 2 | 3 | #[derive(FromAttr)] 4 | #[attribute(idents = [di])] 5 | pub(crate) struct ImplFnOrEnumVariantAttr; 6 | -------------------------------------------------------------------------------- /rudi-macro/src/item_enum_gen.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{AttrsValue, FromAttr, PathValue}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use rudi_core::{Color, Scope}; 5 | use syn::{spanned::Spanned, ItemEnum}; 6 | 7 | use crate::{ 8 | commons::{self, FieldResolveStmts, ResolvedFields}, 9 | di_attr::DiAttr, 10 | impl_fn_or_enum_variant_attr::ImplFnOrEnumVariantAttr, 11 | struct_or_function_attr::{ClosureOrPath, StructOrFunctionAttr}, 12 | }; 13 | 14 | pub(crate) fn generate( 15 | attr: StructOrFunctionAttr, 16 | mut item_enum: ItemEnum, 17 | scope: Scope, 18 | ) -> syn::Result { 19 | let DiAttr { rudi_path } = match DiAttr::remove_attributes(&mut item_enum.attrs) { 20 | Ok(Some(AttrsValue { value: attr, .. })) => attr, 21 | Ok(None) => DiAttr::default(), 22 | Err(AttrsValue { value: e, .. }) => return Err(e), 23 | }; 24 | 25 | if item_enum.variants.is_empty() { 26 | return Err(syn::Error::new(item_enum.span(), "not support empty enum")); 27 | } 28 | 29 | let StructOrFunctionAttr { 30 | name, 31 | eager_create, 32 | condition, 33 | binds, 34 | async_, 35 | #[cfg(feature = "auto-register")] 36 | auto_register, 37 | } = attr; 38 | 39 | #[cfg(feature = "auto-register")] 40 | commons::check_generics_when_enable_auto_register( 41 | auto_register, 42 | &item_enum.generics, 43 | commons::ItemKind::Enum, 44 | scope, 45 | )?; 46 | 47 | let color = match async_ { 48 | Some(PathValue { value: true, .. }) => Color::Async, 49 | _ => Color::Sync, 50 | }; 51 | 52 | let condition = condition 53 | .map(|ClosureOrPath(expr)| quote!(Some(#expr))) 54 | .unwrap_or_else(|| quote!(None)); 55 | 56 | let mut variant_spans = Vec::new(); 57 | 58 | let mut parse_errors = Vec::new(); 59 | let mut duplicate_errors = Vec::new(); 60 | let mut no_matched_variant_errors = Vec::new(); 61 | 62 | let matched = item_enum 63 | .variants 64 | .iter_mut() 65 | .filter_map(|variant| { 66 | variant_spans.push(variant.span()); 67 | 68 | match ImplFnOrEnumVariantAttr::remove_attributes(&mut variant.attrs) { 69 | Ok(None) => None, 70 | Ok(Some(AttrsValue { attrs, .. })) => Some((variant, attrs)), 71 | Err(AttrsValue { attrs, value: e }) => { 72 | parse_errors.push(e); 73 | Some((variant, attrs)) 74 | } 75 | } 76 | }) 77 | .reduce(|first, (_, attrs)| { 78 | attrs.into_iter().for_each(|attr| { 79 | let err = syn::Error::new(attr.span(), "duplicate `#[di]` attribute"); 80 | duplicate_errors.push(err); 81 | }); 82 | 83 | first 84 | }); 85 | 86 | if matched.is_none() { 87 | variant_spans.iter().for_each(|span| { 88 | no_matched_variant_errors.push(syn::Error::new( 89 | *span, 90 | "there must be a variant annotated by `#[di]`", 91 | )); 92 | }); 93 | } 94 | 95 | if let Some(e) = parse_errors 96 | .into_iter() 97 | .chain(duplicate_errors) 98 | .chain(no_matched_variant_errors) 99 | .reduce(|mut a, b| { 100 | a.combine(b); 101 | a 102 | }) 103 | { 104 | return Err(e); 105 | } 106 | 107 | let (variant, _) = matched.unwrap(); 108 | 109 | let FieldResolveStmts { 110 | ref_mut_cx_stmts, 111 | ref_cx_stmts, 112 | fields, 113 | } = commons::generate_field_resolve_stmts(&mut variant.fields, color)?; 114 | 115 | let create_provider = commons::generate_create_provider(scope, color); 116 | 117 | let enum_ident = &item_enum.ident; 118 | let variant_ident = &variant.ident; 119 | 120 | let (impl_generics, ty_generics, where_clause) = item_enum.generics.split_for_impl(); 121 | 122 | let instance = match fields { 123 | ResolvedFields::Unit => quote! { 124 | #variant_ident 125 | }, 126 | ResolvedFields::Named { 127 | field_names, 128 | field_values, 129 | } => { 130 | quote! { 131 | #variant_ident { 132 | #( 133 | #field_names: #field_values, 134 | )* 135 | } 136 | } 137 | } 138 | ResolvedFields::Unnamed(field_values) => { 139 | quote! { 140 | #variant_ident( 141 | #( 142 | #field_values, 143 | )* 144 | ) 145 | } 146 | } 147 | }; 148 | 149 | let constructor = match color { 150 | Color::Async => { 151 | quote! { 152 | #[allow(unused_variables)] 153 | |cx| ::std::boxed::Box::pin(async { 154 | #(#ref_mut_cx_stmts)* 155 | #(#ref_cx_stmts)* 156 | #enum_ident::#instance 157 | }) 158 | } 159 | } 160 | Color::Sync => { 161 | quote! { 162 | #[allow(unused_variables)] 163 | |cx| { 164 | #(#ref_mut_cx_stmts)* 165 | #(#ref_cx_stmts)* 166 | #enum_ident::#instance 167 | } 168 | } 169 | } 170 | }; 171 | 172 | #[cfg(not(feature = "auto-register"))] 173 | let auto_register = quote! {}; 174 | 175 | #[cfg(feature = "auto-register")] 176 | let auto_register = if auto_register { 177 | quote! { 178 | #rudi_path::register_provider!(<#enum_ident as #rudi_path::DefaultProvider>::provider()); 179 | } 180 | } else { 181 | quote! {} 182 | }; 183 | 184 | let expand = quote! { 185 | #item_enum 186 | 187 | impl #impl_generics #rudi_path::DefaultProvider for #enum_ident #ty_generics #where_clause { 188 | type Type = Self; 189 | 190 | fn provider() -> #rudi_path::Provider { 191 | <#rudi_path::Provider<_> as ::core::convert::From<_>>::from( 192 | #rudi_path::#create_provider(#constructor) 193 | .name(#name) 194 | .eager_create(#eager_create) 195 | .condition(#condition) 196 | #( 197 | .bind(#binds) 198 | )* 199 | ) 200 | } 201 | } 202 | 203 | #auto_register 204 | }; 205 | 206 | Ok(expand) 207 | } 208 | -------------------------------------------------------------------------------- /rudi-macro/src/item_fn_gen.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{AttrsValue, FromAttr, PathValue}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use rudi_core::{Color, Scope}; 5 | use syn::{GenericParam, ItemFn, ReturnType}; 6 | 7 | use crate::{ 8 | commons::{self, ArgumentResolveStmts}, 9 | di_attr::DiAttr, 10 | struct_or_function_attr::{ClosureOrPath, StructOrFunctionAttr}, 11 | }; 12 | 13 | // #[Singleton] 14 | // fn One(#[di(name = "hello")] i: i32) -> String { 15 | // i.to_string() 16 | // } 17 | 18 | pub(crate) fn generate( 19 | attr: StructOrFunctionAttr, 20 | mut item_fn: ItemFn, 21 | scope: Scope, 22 | ) -> syn::Result { 23 | let DiAttr { rudi_path } = match DiAttr::remove_attributes(&mut item_fn.attrs) { 24 | Ok(Some(AttrsValue { value: attr, .. })) => attr, 25 | Ok(None) => DiAttr::default(), 26 | Err(AttrsValue { value: e, .. }) => return Err(e), 27 | }; 28 | 29 | if let Some(PathValue { path, .. }) = attr.async_ { 30 | return Err(syn::Error::new( 31 | path, 32 | "`async` only support in struct and enum, please use async fn or sync fn instead", 33 | )); 34 | } 35 | 36 | let StructOrFunctionAttr { 37 | name, 38 | eager_create, 39 | condition, 40 | binds, 41 | async_: _, 42 | #[cfg(feature = "auto-register")] 43 | auto_register, 44 | } = attr; 45 | 46 | #[cfg(feature = "auto-register")] 47 | commons::check_generics_when_enable_auto_register( 48 | auto_register, 49 | &item_fn.sig.generics, 50 | commons::ItemKind::Function, 51 | scope, 52 | )?; 53 | 54 | let color = match item_fn.sig.asyncness { 55 | Some(_) => Color::Async, 56 | None => Color::Sync, 57 | }; 58 | 59 | let condition = condition 60 | .map(|ClosureOrPath(expr)| quote!(Some(#expr))) 61 | .unwrap_or_else(|| quote!(None)); 62 | 63 | let ArgumentResolveStmts { 64 | ref_mut_cx_stmts, 65 | ref_cx_stmts, 66 | args, 67 | } = commons::generate_argument_resolve_methods(&mut item_fn.sig.inputs, color)?; 68 | 69 | let create_provider = commons::generate_create_provider(scope, color); 70 | 71 | let (impl_generics, ty_generics, where_clause) = item_fn.sig.generics.split_for_impl(); 72 | 73 | let vis = &item_fn.vis; 74 | 75 | let docs = item_fn 76 | .attrs 77 | .iter() 78 | .filter(|attr| attr.path().is_ident("doc")); 79 | 80 | let ident = &item_fn.sig.ident; 81 | 82 | let return_type_ident = match &item_fn.sig.output { 83 | ReturnType::Default => quote! { 84 | () 85 | }, 86 | ReturnType::Type(_, ty) => quote! { 87 | #ty 88 | }, 89 | }; 90 | 91 | let struct_definition = if item_fn.sig.generics.params.is_empty() { 92 | quote! { 93 | #vis struct #ident; 94 | } 95 | } else { 96 | let members = item_fn 97 | .sig 98 | .generics 99 | .params 100 | .iter() 101 | .filter_map(|param| match param { 102 | GenericParam::Type(ty) => Some(ty), 103 | _ => None, 104 | }) 105 | .enumerate() 106 | .map(|(idx, ty)| { 107 | let ty_ident = &ty.ident; 108 | let ident = quote::format_ident!("_mark{}", idx); 109 | quote! { #ident: ::core::marker::PhantomData<#ty_ident> } 110 | }); 111 | 112 | quote! { 113 | #[derive(Default)] 114 | #vis struct #ident #ty_generics { #(#members),*} 115 | } 116 | }; 117 | 118 | let turbofish = ty_generics.as_turbofish(); 119 | let constructor = match color { 120 | Color::Async => { 121 | quote! { 122 | #[allow(unused_variables)] 123 | |cx| ::std::boxed::Box::pin(async { 124 | #(#ref_mut_cx_stmts)* 125 | #(#ref_cx_stmts)* 126 | #ident #turbofish (#(#args,)*).await 127 | }) 128 | } 129 | } 130 | Color::Sync => { 131 | quote! { 132 | #[allow(unused_variables)] 133 | |cx| { 134 | #(#ref_mut_cx_stmts)* 135 | #(#ref_cx_stmts)* 136 | #ident #turbofish (#(#args,)*) 137 | } 138 | } 139 | } 140 | }; 141 | 142 | #[cfg(not(feature = "auto-register"))] 143 | let auto_register = quote! {}; 144 | 145 | #[cfg(feature = "auto-register")] 146 | let auto_register = if auto_register { 147 | quote! { 148 | #rudi_path::register_provider!(<#ident as #rudi_path::DefaultProvider>::provider()); 149 | } 150 | } else { 151 | quote! {} 152 | }; 153 | 154 | let expand = quote! { 155 | #(#docs)* 156 | #[allow(non_camel_case_types)] 157 | #struct_definition 158 | 159 | impl #impl_generics #rudi_path::DefaultProvider for #ident #ty_generics #where_clause { 160 | type Type = #return_type_ident; 161 | 162 | fn provider() -> #rudi_path::Provider { 163 | #[allow(non_snake_case, clippy::too_many_arguments)] 164 | #item_fn 165 | 166 | <#rudi_path::Provider<_> as ::core::convert::From<_>>::from( 167 | #rudi_path::#create_provider(#constructor) 168 | .name(#name) 169 | .eager_create(#eager_create) 170 | .condition(#condition) 171 | #( 172 | .bind(#binds) 173 | )* 174 | ) 175 | } 176 | } 177 | 178 | #auto_register 179 | }; 180 | 181 | Ok(expand) 182 | } 183 | -------------------------------------------------------------------------------- /rudi-macro/src/item_impl_gen.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{AttrsValue, FromAttr, PathValue}; 2 | use proc_macro2::TokenStream; 3 | use quote::{quote, ToTokens}; 4 | use rudi_core::{Color, Scope}; 5 | use syn::{ 6 | parse_quote, spanned::Spanned, Generics, ImplItem, ImplItemFn, ItemImpl, Path, ReturnType, 7 | Type, TypePath, 8 | }; 9 | 10 | use crate::{ 11 | commons::{self, ArgumentResolveStmts}, 12 | di_attr::DiAttr, 13 | impl_fn_or_enum_variant_attr::ImplFnOrEnumVariantAttr, 14 | struct_or_function_attr::{ClosureOrPath, StructOrFunctionAttr}, 15 | }; 16 | 17 | // struct A { 18 | // a: i32, 19 | // } 20 | 21 | // #[Singleton] 22 | // impl A { 23 | // #[di] 24 | // fn new(#[di(name = "hello")] a:i32) -> Self { 25 | // Self { a } 26 | // } 27 | // } 28 | 29 | pub(crate) fn generate( 30 | attr: StructOrFunctionAttr, 31 | mut item_impl: ItemImpl, 32 | scope: Scope, 33 | ) -> syn::Result { 34 | let DiAttr { rudi_path } = match DiAttr::remove_attributes(&mut item_impl.attrs) { 35 | Ok(Some(AttrsValue { value: attr, .. })) => attr, 36 | Ok(None) => DiAttr::default(), 37 | Err(AttrsValue { value: e, .. }) => return Err(e), 38 | }; 39 | 40 | if let Some(PathValue { path, .. }) = attr.async_ { 41 | return Err(syn::Error::new( 42 | path, 43 | "`async` only support in struct and enum, please use async fn or sync fn instead", 44 | )); 45 | } 46 | 47 | let impl_span = item_impl.span(); 48 | 49 | let ItemImpl { 50 | generics, 51 | self_ty, 52 | items, 53 | trait_, 54 | .. 55 | } = &mut item_impl; 56 | 57 | let trait_ = trait_.as_mut().map(|(_, path, _)| path); 58 | 59 | let mut parse_errors = Vec::new(); 60 | let mut duplicate_errors = Vec::new(); 61 | let mut no_matched_fn_errors = Vec::new(); 62 | 63 | let matched = items 64 | .iter_mut() 65 | .filter_map(|impl_item| { 66 | let f = match impl_item { 67 | ImplItem::Fn(f) => f, 68 | _ => return None, 69 | }; 70 | 71 | match ImplFnOrEnumVariantAttr::remove_attributes(&mut f.attrs) { 72 | Ok(None) => None, 73 | Ok(Some(AttrsValue { attrs, .. })) => Some((f, attrs)), 74 | Err(AttrsValue { attrs, value: e }) => { 75 | parse_errors.push(e); 76 | Some((f, attrs)) 77 | } 78 | } 79 | }) 80 | .reduce(|first, (_, attrs)| { 81 | attrs.into_iter().for_each(|attr| { 82 | let err = syn::Error::new(attr.span(), "duplicate `#[di]` attribute"); 83 | duplicate_errors.push(err); 84 | }); 85 | 86 | first 87 | }); 88 | 89 | if matched.is_none() { 90 | no_matched_fn_errors.push(syn::Error::new( 91 | impl_span.span(), 92 | "there must be an associated function annotated by `#[di]`", 93 | )); 94 | } 95 | 96 | if let Some(e) = parse_errors 97 | .into_iter() 98 | .chain(duplicate_errors) 99 | .chain(no_matched_fn_errors) 100 | .reduce(|mut a, b| { 101 | a.combine(b); 102 | a 103 | }) 104 | { 105 | return Err(e); 106 | } 107 | 108 | let (f, _) = matched.unwrap(); 109 | 110 | let default_provider_impl = 111 | generate_default_provider_impl(f, &trait_, self_ty, generics, attr, scope, rudi_path)?; 112 | 113 | let expand = quote! { 114 | #item_impl 115 | 116 | #default_provider_impl 117 | }; 118 | 119 | Ok(expand) 120 | } 121 | 122 | fn generate_default_provider_impl<'a>( 123 | impl_item_fn: &'a mut ImplItemFn, 124 | trait_: &'a Option<&'a mut Path>, 125 | type_with_generics: &'a Type, 126 | generics: &'a Generics, 127 | attr: StructOrFunctionAttr, 128 | scope: Scope, 129 | rudi_path: Path, 130 | ) -> syn::Result { 131 | let StructOrFunctionAttr { 132 | name, 133 | eager_create, 134 | condition, 135 | binds, 136 | async_: _, 137 | #[cfg(feature = "auto-register")] 138 | auto_register, 139 | } = attr; 140 | 141 | #[cfg(feature = "auto-register")] 142 | commons::check_generics_when_enable_auto_register( 143 | auto_register, 144 | generics, 145 | commons::ItemKind::StructOrEnum, 146 | scope, 147 | )?; 148 | 149 | let return_type: Type = match &impl_item_fn.sig.output { 150 | ReturnType::Default => parse_quote!(()), 151 | ReturnType::Type(_, return_type) => *return_type.clone(), 152 | }; 153 | 154 | let return_type_is_named = &return_type == type_with_generics; 155 | 156 | let return_type_is_self = if let Type::Path(TypePath { 157 | qself: None, 158 | path: Path { 159 | leading_colon: None, 160 | segments, 161 | }, 162 | }) = &return_type 163 | { 164 | segments.len() == 1 && segments.first().unwrap().ident == "Self" 165 | } else { 166 | false 167 | }; 168 | 169 | if !return_type_is_named && !return_type_is_self { 170 | return Err(syn::Error::new( 171 | impl_item_fn.sig.span(), 172 | format!( 173 | "return type must be `{}` or `Self`", 174 | type_with_generics.into_token_stream() 175 | ), 176 | )); 177 | } 178 | 179 | let color = match impl_item_fn.sig.asyncness { 180 | Some(_) => Color::Async, 181 | None => Color::Sync, 182 | }; 183 | 184 | let condition = condition 185 | .map(|ClosureOrPath(expr)| quote!(Some(#expr))) 186 | .unwrap_or_else(|| quote!(None)); 187 | 188 | let ArgumentResolveStmts { 189 | ref_mut_cx_stmts, 190 | ref_cx_stmts, 191 | args, 192 | } = commons::generate_argument_resolve_methods(&mut impl_item_fn.sig.inputs, color)?; 193 | 194 | let create_provider = commons::generate_create_provider(scope, color); 195 | 196 | let (impl_generics, _, where_clause) = generics.split_for_impl(); 197 | 198 | let fn_ident = &impl_item_fn.sig.ident; 199 | 200 | let self_path = match trait_ { 201 | Some(trait_) => quote! { }, 202 | None => quote! { Self }, 203 | }; 204 | 205 | let constructor = match color { 206 | Color::Async => { 207 | quote! { 208 | #[allow(unused_variables)] 209 | |cx| ::std::boxed::Box::pin(async { 210 | #(#ref_mut_cx_stmts)* 211 | #(#ref_cx_stmts)* 212 | #self_path::#fn_ident(#(#args,)*).await 213 | }) 214 | } 215 | } 216 | Color::Sync => { 217 | quote! { 218 | #[allow(unused_variables)] 219 | |cx| { 220 | #(#ref_mut_cx_stmts)* 221 | #(#ref_cx_stmts)* 222 | #self_path::#fn_ident(#(#args,)*) 223 | } 224 | } 225 | } 226 | }; 227 | 228 | #[cfg(not(feature = "auto-register"))] 229 | let auto_register = quote! {}; 230 | 231 | #[cfg(feature = "auto-register")] 232 | let auto_register = if auto_register { 233 | quote! { 234 | #rudi_path::register_provider!(<#type_with_generics as #rudi_path::DefaultProvider>::provider()); 235 | } 236 | } else { 237 | quote! {} 238 | }; 239 | 240 | let expand = quote! { 241 | impl #impl_generics #rudi_path::DefaultProvider for #type_with_generics #where_clause { 242 | type Type = Self; 243 | 244 | fn provider() -> #rudi_path::Provider { 245 | <#rudi_path::Provider<_> as ::core::convert::From<_>>::from( 246 | #rudi_path::#create_provider(#constructor) 247 | .name(#name) 248 | .eager_create(#eager_create) 249 | .condition(#condition) 250 | #( 251 | .bind(#binds) 252 | )* 253 | ) 254 | } 255 | } 256 | 257 | #auto_register 258 | }; 259 | 260 | Ok(expand) 261 | } 262 | -------------------------------------------------------------------------------- /rudi-macro/src/item_struct_gen.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{AttrsValue, FromAttr, PathValue}; 2 | use proc_macro2::TokenStream; 3 | use quote::quote; 4 | use rudi_core::{Color, Scope}; 5 | use syn::ItemStruct; 6 | 7 | use crate::{ 8 | commons::{self, FieldResolveStmts, ResolvedFields}, 9 | di_attr::DiAttr, 10 | struct_or_function_attr::{ClosureOrPath, StructOrFunctionAttr}, 11 | }; 12 | 13 | pub(crate) fn generate( 14 | attr: StructOrFunctionAttr, 15 | mut item_struct: ItemStruct, 16 | scope: Scope, 17 | ) -> syn::Result { 18 | let DiAttr { rudi_path } = match DiAttr::remove_attributes(&mut item_struct.attrs) { 19 | Ok(Some(AttrsValue { value: attr, .. })) => attr, 20 | Ok(None) => DiAttr::default(), 21 | Err(AttrsValue { value: e, .. }) => return Err(e), 22 | }; 23 | 24 | let StructOrFunctionAttr { 25 | name, 26 | eager_create, 27 | condition, 28 | binds, 29 | async_, 30 | #[cfg(feature = "auto-register")] 31 | auto_register, 32 | } = attr; 33 | 34 | #[cfg(feature = "auto-register")] 35 | commons::check_generics_when_enable_auto_register( 36 | auto_register, 37 | &item_struct.generics, 38 | commons::ItemKind::Struct, 39 | scope, 40 | )?; 41 | 42 | let color = match async_ { 43 | Some(PathValue { value: true, .. }) => Color::Async, 44 | _ => Color::Sync, 45 | }; 46 | 47 | let condition = condition 48 | .map(|ClosureOrPath(expr)| quote!(Some(#expr))) 49 | .unwrap_or_else(|| quote!(None)); 50 | 51 | let FieldResolveStmts { 52 | ref_mut_cx_stmts, 53 | ref_cx_stmts, 54 | fields, 55 | } = commons::generate_field_resolve_stmts(&mut item_struct.fields, color)?; 56 | 57 | let create_provider = commons::generate_create_provider(scope, color); 58 | 59 | let struct_ident = &item_struct.ident; 60 | 61 | let (impl_generics, ty_generics, where_clause) = item_struct.generics.split_for_impl(); 62 | 63 | let instance = match fields { 64 | ResolvedFields::Unit => quote! { 65 | #struct_ident 66 | }, 67 | ResolvedFields::Named { 68 | field_names, 69 | field_values, 70 | } => { 71 | quote! { 72 | #struct_ident { 73 | #( 74 | #field_names: #field_values, 75 | )* 76 | } 77 | } 78 | } 79 | ResolvedFields::Unnamed(field_values) => { 80 | quote! { 81 | #struct_ident( 82 | #( 83 | #field_values, 84 | )* 85 | ) 86 | } 87 | } 88 | }; 89 | 90 | let constructor = match color { 91 | Color::Async => { 92 | quote! { 93 | #[allow(unused_variables)] 94 | |cx| ::std::boxed::Box::pin(async { 95 | #(#ref_mut_cx_stmts)* 96 | #(#ref_cx_stmts)* 97 | #instance 98 | }) 99 | } 100 | } 101 | Color::Sync => { 102 | quote! { 103 | #[allow(unused_variables)] 104 | |cx| { 105 | #(#ref_mut_cx_stmts)* 106 | #(#ref_cx_stmts)* 107 | #instance 108 | } 109 | } 110 | } 111 | }; 112 | 113 | #[cfg(not(feature = "auto-register"))] 114 | let auto_register = quote! {}; 115 | 116 | #[cfg(feature = "auto-register")] 117 | let auto_register = if auto_register { 118 | quote! { 119 | #rudi_path::register_provider!(<#struct_ident as #rudi_path::DefaultProvider>::provider()); 120 | } 121 | } else { 122 | quote! {} 123 | }; 124 | 125 | let expand = quote! { 126 | #item_struct 127 | 128 | impl #impl_generics #rudi_path::DefaultProvider for #struct_ident #ty_generics #where_clause { 129 | type Type = Self; 130 | 131 | fn provider() -> #rudi_path::Provider { 132 | <#rudi_path::Provider<_> as ::core::convert::From<_>>::from( 133 | #rudi_path::#create_provider(#constructor) 134 | .name(#name) 135 | .eager_create(#eager_create) 136 | .condition(#condition) 137 | #( 138 | .bind(#binds) 139 | )* 140 | ) 141 | } 142 | } 143 | 144 | #auto_register 145 | }; 146 | 147 | Ok(expand) 148 | } 149 | -------------------------------------------------------------------------------- /rudi-macro/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod commons; 2 | mod di_attr; 3 | mod field_or_argument_attr; 4 | mod impl_fn_or_enum_variant_attr; 5 | mod item_enum_gen; 6 | mod item_fn_gen; 7 | mod item_impl_gen; 8 | mod item_struct_gen; 9 | mod struct_or_function_attr; 10 | 11 | use from_attr::FromAttr; 12 | use proc_macro::TokenStream; 13 | use rudi_core::Scope; 14 | use syn::{parse_macro_input, spanned::Spanned, Item}; 15 | 16 | use crate::struct_or_function_attr::StructOrFunctionAttr; 17 | 18 | fn generate(attr: TokenStream, item: TokenStream, scope: Scope) -> TokenStream { 19 | let attr = match StructOrFunctionAttr::from_tokens(attr.into()) { 20 | Ok(attr) => attr, 21 | Err(err) => return err.to_compile_error().into(), 22 | }; 23 | 24 | let item = parse_macro_input!(item as Item); 25 | 26 | let result = match item { 27 | Item::Struct(item_struct) => item_struct_gen::generate(attr, item_struct, scope), 28 | Item::Enum(item_enum) => item_enum_gen::generate(attr, item_enum, scope), 29 | Item::Fn(item_fn) => item_fn_gen::generate(attr, item_fn, scope), 30 | Item::Impl(item_impl) => item_impl_gen::generate(attr, item_impl, scope), 31 | _ => Err(syn::Error::new( 32 | item.span(), 33 | "expected `struct` or `enum` or `function` or `impl block`", 34 | )), 35 | }; 36 | 37 | result.unwrap_or_else(|e| e.to_compile_error()).into() 38 | } 39 | 40 | /// Define a singleton provider. 41 | #[doc = ""] 42 | #[doc = include_str!("./docs/attribute_macro.md")] 43 | #[proc_macro_attribute] 44 | #[allow(non_snake_case)] 45 | pub fn Singleton(attr: TokenStream, item: TokenStream) -> TokenStream { 46 | generate(attr, item, Scope::Singleton) 47 | } 48 | 49 | /// Define a transient provider. 50 | #[doc = ""] 51 | #[doc = include_str!("./docs/attribute_macro.md")] 52 | #[proc_macro_attribute] 53 | #[allow(non_snake_case)] 54 | pub fn Transient(attr: TokenStream, item: TokenStream) -> TokenStream { 55 | generate(attr, item, Scope::Transient) 56 | } 57 | 58 | /// Define a single owner provider. 59 | #[doc = ""] 60 | #[doc = include_str!("./docs/attribute_macro.md")] 61 | #[proc_macro_attribute] 62 | #[allow(non_snake_case)] 63 | pub fn SingleOwner(attr: TokenStream, item: TokenStream) -> TokenStream { 64 | generate(attr, item, Scope::SingleOwner) 65 | } 66 | -------------------------------------------------------------------------------- /rudi-macro/src/struct_or_function_attr.rs: -------------------------------------------------------------------------------- 1 | use from_attr::{ConvertParsed, FromAttr, PathValue}; 2 | use syn::{parse_quote, spanned::Spanned, Expr, ExprPath}; 3 | 4 | #[derive(FromAttr)] 5 | #[attribute(idents = [di])] 6 | pub(crate) struct StructOrFunctionAttr { 7 | #[attribute(default = default_name())] 8 | pub(crate) name: Expr, 9 | 10 | pub(crate) eager_create: bool, 11 | 12 | pub(crate) condition: Option, 13 | 14 | pub(crate) binds: Vec, 15 | 16 | #[attribute(rename = "async")] 17 | pub(crate) async_: Option>, 18 | 19 | #[cfg(feature = "auto-register")] 20 | #[attribute(default = DEFAULT_AUTO_REGISTER)] 21 | pub(crate) auto_register: bool, 22 | } 23 | 24 | fn default_name() -> Expr { 25 | parse_quote!("") 26 | } 27 | 28 | #[cfg(feature = "auto-register")] 29 | const DEFAULT_AUTO_REGISTER: bool = true; 30 | 31 | pub(crate) struct ClosureOrPath(pub(crate) Expr); 32 | 33 | impl ConvertParsed for ClosureOrPath { 34 | type Type = Expr; 35 | 36 | fn convert(path_value: PathValue) -> syn::Result { 37 | let expr = path_value.value; 38 | 39 | match &expr { 40 | Expr::Closure(_) | Expr::Path(_) => Ok(Self(expr)), 41 | _ => Err(syn::Error::new( 42 | expr.span(), 43 | "the expr must be a closure or an expression path", 44 | )), 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /rudi/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "rudi" 3 | description = "Rudi - an out-of-the-box dependency injection framework for Rust." 4 | keywords = ["dependency-injection", "ioc", "di", "dependency"] 5 | version.workspace = true 6 | edition.workspace = true 7 | authors.workspace = true 8 | license.workspace = true 9 | homepage.workspace = true 10 | repository.workspace = true 11 | include.workspace = true 12 | readme.workspace = true 13 | 14 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 15 | 16 | [dependencies] 17 | rudi-core = { workspace = true } 18 | rudi-macro = { workspace = true, optional = true } 19 | inventory = { workspace = true, optional = true } 20 | tracing = { workspace = true, optional = true } 21 | 22 | [dev-dependencies] 23 | tokio = { workspace = true } 24 | 25 | [features] 26 | default = ["rudi-macro", "auto-register"] 27 | auto-register = ["dep:inventory", "rudi-macro?/auto-register"] 28 | 29 | [lints] 30 | workspace = true 31 | 32 | [package.metadata.docs.rs] 33 | all-features = true 34 | rustdoc-args = ["--cfg", "docsrs"] 35 | -------------------------------------------------------------------------------- /rudi/src/auto_register.rs: -------------------------------------------------------------------------------- 1 | #[doc(hidden)] 2 | pub use inventory::submit; 3 | 4 | use crate::{DynProvider, Module}; 5 | 6 | #[doc(hidden)] 7 | pub struct ProviderRegister { 8 | pub register: fn() -> DynProvider, 9 | } 10 | 11 | inventory::collect!(ProviderRegister); 12 | 13 | /// Returns an iterator over all auto-registered providers. 14 | /// 15 | /// [`AutoRegisterModule`] uses this function to collect all auto-registered [`DynProvider`]s. 16 | /// If you don't want to use `AutoRegisterModule`, you can use this function to customize your own module. 17 | /// 18 | /// # Example 19 | /// 20 | /// ```rust 21 | /// use rudi::{auto_registered_providers, modules, Context, DynProvider, Module, SingleOwner}; 22 | /// 23 | /// struct MyAutoRegisterModule; 24 | /// 25 | /// impl Module for MyAutoRegisterModule { 26 | /// fn eager_create() -> bool { 27 | /// true 28 | /// } 29 | /// 30 | /// fn providers() -> Vec { 31 | /// auto_registered_providers().collect() 32 | /// } 33 | /// } 34 | /// 35 | /// #[SingleOwner] 36 | /// struct A; 37 | /// 38 | /// # fn main() { 39 | /// let cx = Context::create(modules![MyAutoRegisterModule]); 40 | /// assert!(cx.get_single_option::
().is_some()); 41 | /// # } 42 | /// ``` 43 | pub fn auto_registered_providers() -> impl Iterator { 44 | inventory::iter:: 45 | .into_iter() 46 | .map(|register| (register.register)()) 47 | } 48 | 49 | /// A module that auto-registers all providers. 50 | /// 51 | /// This module is enabled by the `auto-register` feature. 52 | /// Because auto-registration relies on [`inventory`] crate, auto-registration 53 | /// is not available on platforms where `inventory` is not supported. 54 | /// 55 | /// # Example 56 | /// 57 | /// ```rust 58 | /// use rudi::{modules, AutoRegisterModule, Context, Singleton, Transient}; 59 | /// 60 | /// #[Singleton] 61 | /// #[derive(Clone)] 62 | /// struct A; 63 | /// 64 | /// #[Transient] 65 | /// struct B(A); 66 | /// 67 | /// # fn main() { 68 | /// let mut cx = Context::create(modules![AutoRegisterModule]); 69 | /// assert!(cx.resolve_option::().is_some()); 70 | /// # } 71 | /// ``` 72 | pub struct AutoRegisterModule; 73 | 74 | impl Module for AutoRegisterModule { 75 | fn providers() -> Vec { 76 | auto_registered_providers().collect() 77 | } 78 | } 79 | 80 | /// Register a `Provider` that will be collected by [`auto_registered_providers`]. 81 | /// 82 | /// If you have: 83 | /// - Enabled the `auto-register` feature (which is enabled by default). 84 | /// - Define [`Provider`](crate::Provider) using the [`#[Singleton]`](crate::Singleton), [`#[Transient]`](crate::Transient) or [`#[SingleOwner]`](crate::SingleOwner) macro. 85 | /// - [`#[Singleton]`](crate::Singleton), [`#[Transient]`](crate::Transient) or [`#[SingleOwner]`](crate::SingleOwner) does not use the `auto_register = false` attribute. 86 | /// 87 | /// Then you don't need to use this macro to register `Provider`. 88 | /// 89 | /// But if you use function define a [`Provider`](crate::Provider) and you want to use auto-registration, 90 | /// then you need to use this macro. 91 | /// 92 | /// # Example 93 | /// 94 | /// ```rust 95 | /// use rudi::{register_provider, singleton, Context, Provider}; 96 | /// 97 | /// fn foo() -> Provider<&'static str> { 98 | /// singleton(|_| "Hello").into() 99 | /// } 100 | /// 101 | /// register_provider!(foo()); 102 | /// 103 | /// fn main() { 104 | /// let mut cx = Context::auto_register(); 105 | /// assert!(cx.resolve_option::<&'static str>().is_some()); 106 | /// } 107 | /// ``` 108 | #[macro_export] 109 | macro_rules! register_provider { 110 | ($provider:expr) => { 111 | const _: () = { 112 | fn register() -> $crate::DynProvider { 113 | <$crate::DynProvider as ::core::convert::From<_>>::from($provider) 114 | } 115 | 116 | $crate::submit! { 117 | $crate::ProviderRegister { 118 | register 119 | } 120 | } 121 | }; 122 | }; 123 | } 124 | 125 | /// Generate a function to enable auto-registration. 126 | /// 127 | /// In Rust, it is possible to use [`inventory`] to accomplish something like 128 | /// auto-registration, but there is still a problem, and it exists in Rudi as well. 129 | /// 130 | /// Suppose you have two crates, one called `crate_1` and one called `crate_2`, 131 | /// and you define some auto-registration types in `crate_2`. 132 | /// 133 | /// If it is just a dependency on `crate_2` in `crate_1`'s `Cargo.toml`, then using 134 | /// [`auto_registered_providers`] in `crate_1` will not collect the types defined in `crate_2`, 135 | /// you have to use a function (or type, or constant) in `crate_1` that is defined in `crate_2` 136 | /// in order to enable auto-registration. 137 | /// 138 | /// So, there is this macro, which generates a function called `enable`, with no parameters 139 | /// and no return, just to be called by other crates to enable auto-registration. 140 | /// 141 | /// At the same time, you can also call the enable functions of other crates that the current 142 | /// crate depends on in this macro, so that when the enable function of the current crate is 143 | /// called, the enable functions of other crates will be called together. 144 | /// 145 | /// # Example 146 | /// 147 | /// ```rust ignore 148 | /// // lib1/src/lib.rs 149 | /// use rudi::{enable, Transient}; 150 | /// 151 | /// enable! {} 152 | /// 153 | /// #[Transient(name = "lib1")] 154 | /// fn Lib1() -> i32 { 155 | /// 5 156 | /// } 157 | /// 158 | /// // lib2/src/lib.rs 159 | /// use rudi::{enable, Transient}; 160 | /// 161 | /// enable! { 162 | /// lib1::enable(); 163 | /// } 164 | /// 165 | /// #[Transient(name = "lib2")] 166 | /// fn Lib2() -> i32 { 167 | /// 5 168 | /// } 169 | /// 170 | /// // bin/src/main.rs 171 | /// use rudi::Context; 172 | /// 173 | /// fn main() { 174 | /// lib2::enable(); 175 | /// 176 | /// let mut cx = Context::auto_register(); 177 | /// assert_eq!(cx.resolve_by_type::().into_iter().sum::(), 10); 178 | /// } 179 | /// ``` 180 | #[macro_export] 181 | macro_rules! enable { 182 | ($($body:tt)*) => { 183 | /// Enable auto-registration. 184 | pub fn enable() { 185 | $($body)* 186 | } 187 | }; 188 | } 189 | -------------------------------------------------------------------------------- /rudi/src/definition.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | borrow::Cow, 3 | cmp::Ordering, 4 | hash::{Hash, Hasher}, 5 | }; 6 | 7 | use crate::{Color, Scope, Type}; 8 | 9 | /// Represents a unique key for a provider. 10 | #[derive(Clone, Debug)] 11 | pub struct Key { 12 | /// The name of the provider. 13 | pub name: Cow<'static, str>, 14 | /// The type of the provider generic. 15 | pub ty: Type, 16 | } 17 | 18 | impl Key { 19 | pub(crate) fn new(name: Cow<'static, str>) -> Self { 20 | Self { 21 | name, 22 | ty: Type::new::(), 23 | } 24 | } 25 | } 26 | 27 | impl PartialEq for Key { 28 | fn eq(&self, other: &Self) -> bool { 29 | self.ty == other.ty && self.name == other.name 30 | } 31 | } 32 | 33 | impl Eq for Key {} 34 | 35 | impl PartialOrd for Key { 36 | fn partial_cmp(&self, other: &Self) -> Option { 37 | Some(self.cmp(other)) 38 | } 39 | } 40 | 41 | impl Ord for Key { 42 | fn cmp(&self, other: &Self) -> Ordering { 43 | match self.ty.cmp(&other.ty) { 44 | Ordering::Equal => {} 45 | ord => return ord, 46 | } 47 | self.name.cmp(&other.name) 48 | } 49 | } 50 | 51 | impl Hash for Key { 52 | fn hash(&self, state: &mut H) { 53 | self.ty.hash(state); 54 | self.name.hash(state); 55 | } 56 | } 57 | 58 | /// Represents a definition of a provider. 59 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] 60 | pub struct Definition { 61 | /// The unique key of the provider. 62 | pub key: Key, 63 | /// The origin type of the provider. 64 | /// 65 | /// When the following methods are called, current definition represents the 66 | /// return type of the method, and this field represents the parameter type of the method: 67 | /// - [`SingletonProvider::bind`](crate::SingletonProvider::bind) 68 | /// - [`TransientProvider::bind`](crate::TransientProvider::bind) 69 | /// - [`SingleOwnerProvider::bind`](crate::SingleOwnerProvider::bind) 70 | /// - [`SingletonAsyncProvider::bind`](crate::SingletonAsyncProvider::bind) 71 | /// - [`TransientAsyncProvider::bind`](crate::TransientAsyncProvider::bind) 72 | /// - [`SingleOwnerAsyncProvider::bind`](crate::SingleOwnerAsyncProvider::bind) 73 | pub origin: Option, 74 | /// The scope of the provider. 75 | pub scope: Scope, 76 | /// The color of the constructor. 77 | pub color: Option, 78 | /// Whether the provider is conditional. 79 | pub conditional: bool, 80 | } 81 | 82 | impl Definition { 83 | pub(crate) fn new( 84 | name: Cow<'static, str>, 85 | scope: Scope, 86 | color: Option, 87 | conditional: bool, 88 | ) -> Self { 89 | Self { 90 | key: Key::new::(name), 91 | origin: None, 92 | scope, 93 | color, 94 | conditional, 95 | } 96 | } 97 | 98 | pub(crate) fn bind(self) -> Definition { 99 | let Definition { 100 | key: Key { name, ty }, 101 | scope, 102 | color, 103 | conditional, 104 | origin: _origin, 105 | } = self; 106 | 107 | Self { 108 | key: Key::new::(name), 109 | origin: Some(ty), 110 | scope, 111 | color, 112 | conditional, 113 | } 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /rudi/src/docs/lib.md: -------------------------------------------------------------------------------- 1 | # Rudi 2 | 3 | Rudi - an out-of-the-box dependency injection framework for Rust. 4 | 5 | ## Quick Links 6 | 7 | Here are links to the most important sections of the docs: 8 | 9 | - [`Context`](crate::Context): The core of the entire dependency injection framework, responsible for managing all providers. 10 | - [`#[Singleton]`](crate::Singleton) / [`#[Transient]`](crate::Transient) / [`#[SingleOwner]`](crate::SingleOwner): Three attribute macros used to generate the implementation of [`DefaultProvider`](crate::DefaultProvider), thus registering providers. 11 | 12 | ## Feature Flags 13 | 14 | - `rudi-macro` (*Default*): Enables the `#[Singleton]`, `#[Transient]` and `#[SingleOwner]` attribute macros. 15 | - `auto-register` (*Default*): Enables automatic registration of types and functions. 16 | - `tracing`: Adds support for logging with [`tracing`](https://crates.io/crates/tracing). 17 | 18 | ## Example 19 | 20 | ```rust 21 | use rudi::{Context, Singleton, Transient}; 22 | 23 | // Register `fn(cx) -> A { A }` as the constructor for `A` 24 | #[derive(Debug)] 25 | #[Transient] 26 | struct A; 27 | 28 | #[derive(Debug)] 29 | struct B(A); 30 | 31 | // Register `fn(cx) -> B { B::new(cx.resolve::()) }` as the constructor for `B` 32 | #[Transient] 33 | impl B { 34 | #[di] 35 | fn new(a: A) -> B { 36 | B(a) 37 | } 38 | } 39 | 40 | // Register `fn(cx) -> C { C::B(cx.resolve::()) }` as the constructor for `C` 41 | #[allow(dead_code)] 42 | #[Transient] 43 | enum C { 44 | A(A), 45 | 46 | #[di] 47 | B(B), 48 | } 49 | 50 | // Register `fn(cx) -> () { Run(cx.resolve::(), cx.resolve::()) }` as the constructor for `()` 51 | #[Singleton] 52 | fn Run(b: B, c: C) { 53 | println!("{:?}", b); 54 | assert!(matches!(c, C::B(_))); 55 | } 56 | 57 | fn main() { 58 | // Automatically register all types and functions with the `#[Singleton]`, `#[Transient]` or `#[SingleOwner]` attribute. 59 | let mut cx = Context::auto_register(); 60 | 61 | // Get an instance of `()` from the `Context`, which will call the `Run` function. 62 | // This is equivalent to `cx.resolve::<()>();` 63 | cx.resolve() 64 | } 65 | ``` 66 | -------------------------------------------------------------------------------- /rudi/src/future.rs: -------------------------------------------------------------------------------- 1 | use std::{future::Future, pin::Pin}; 2 | 3 | /// An owned dynamically typed [`Future`] for use in cases where you can't 4 | /// statically type your result or need to add some indirection. 5 | pub type BoxFuture<'a, T> = Pin + 'a>>; 6 | 7 | impl FutureExt for T where T: Future {} 8 | 9 | /// An extension trait for `Future`s that provides a convenient adapter. 10 | pub trait FutureExt: Future { 11 | /// Wrap the future in a Box, pinning it. 12 | fn boxed<'a>(self) -> BoxFuture<'a, Self::Output> 13 | where 14 | Self: Sized + 'a, 15 | { 16 | Box::pin(self) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /rudi/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![doc = include_str!("./docs/lib.md")] 2 | #![cfg_attr(docsrs, feature(doc_cfg))] 3 | 4 | #[cfg_attr(docsrs, doc(cfg(feature = "auto-register")))] 5 | #[cfg(feature = "auto-register")] 6 | mod auto_register; 7 | mod context; 8 | mod definition; 9 | mod future; 10 | mod macros; 11 | mod module; 12 | mod provider; 13 | mod registry; 14 | mod single; 15 | mod ty; 16 | 17 | pub use rudi_core::*; 18 | #[cfg_attr(docsrs, doc(cfg(feature = "rudi-macro")))] 19 | #[cfg(feature = "rudi-macro")] 20 | pub use rudi_macro::*; 21 | 22 | #[cfg_attr(docsrs, doc(cfg(feature = "auto-register")))] 23 | #[cfg(feature = "auto-register")] 24 | pub use self::auto_register::*; 25 | pub(crate) use self::registry::*; 26 | pub use self::{context::*, definition::*, future::*, module::*, provider::*, single::*, ty::*}; 27 | -------------------------------------------------------------------------------- /rudi/src/macros.rs: -------------------------------------------------------------------------------- 1 | /// Convert a set of types that implement [`Module`] 2 | /// to a set of [`ResolveModule`] instances. 3 | /// 4 | /// # Example 5 | /// 6 | /// ```rust 7 | /// use rudi::{modules, DynProvider, Module, ResolveModule}; 8 | /// 9 | /// struct MyModule; 10 | /// 11 | /// impl Module for MyModule { 12 | /// fn providers() -> Vec { 13 | /// Vec::new() 14 | /// } 15 | /// } 16 | /// 17 | /// # fn main() { 18 | /// let _: Vec = modules![MyModule]; 19 | /// # } 20 | /// ``` 21 | /// 22 | /// [`Module`]: crate::Module 23 | /// [`ResolveModule`]: crate::ResolveModule 24 | #[macro_export] 25 | macro_rules! modules { 26 | () => { 27 | vec![] 28 | }; 29 | ($($module:ty),+ $(,)?) => { 30 | vec![$( 31 | $crate::ResolveModule::new::<$module>() 32 | ),+] 33 | }; 34 | } 35 | 36 | /// Convert a set of instances that implement `Into` 37 | /// to a set of [`DynProvider`] instances 38 | /// 39 | /// # Example 40 | /// 41 | /// ```rust 42 | /// use rudi::{providers, singleton, DynProvider}; 43 | /// 44 | /// # fn main() { 45 | /// let _: Vec = providers![singleton(|_| "Hello")]; 46 | /// # } 47 | /// ``` 48 | /// 49 | /// [`DynProvider`]: crate::DynProvider 50 | #[macro_export] 51 | macro_rules! providers { 52 | () => { 53 | vec![] 54 | }; 55 | ($($provider:expr),+ $(,)?) => { 56 | vec![$( 57 | <$crate::DynProvider as ::core::convert::From<_>>::from($provider) 58 | ),+] 59 | }; 60 | } 61 | 62 | /// Convert a set of types that implement [`DefaultProvider`] 63 | /// to a set of [`DynProvider`] instances 64 | /// 65 | /// # Example 66 | /// 67 | /// ```rust 68 | /// use rudi::{components, DynProvider, Transient}; 69 | /// 70 | /// #[Transient] 71 | /// struct A; 72 | /// 73 | /// # fn main() { 74 | /// let _: Vec = components![A]; 75 | /// # } 76 | /// ``` 77 | /// 78 | /// [`DefaultProvider`]: crate::DefaultProvider 79 | /// [`DynProvider`]: crate::DynProvider 80 | #[macro_export] 81 | macro_rules! components { 82 | () => { 83 | vec![] 84 | }; 85 | ($($component:ty),+ $(,)?) => { 86 | vec![$( 87 | <$crate::DynProvider as ::core::convert::From<_>>::from( 88 | <$component as $crate::DefaultProvider>::provider() 89 | ) 90 | ),+] 91 | }; 92 | } 93 | -------------------------------------------------------------------------------- /rudi/src/module.rs: -------------------------------------------------------------------------------- 1 | use crate::{DynProvider, Type}; 2 | 3 | /// Represents a module. 4 | /// 5 | /// # Example 6 | /// 7 | /// ```rust 8 | /// use rudi::{ 9 | /// modules, providers, singleton, transient, Context, DynProvider, Module, ResolveModule, 10 | /// }; 11 | /// 12 | /// struct Module1; 13 | /// 14 | /// impl Module for Module1 { 15 | /// fn eager_create() -> bool { 16 | /// true 17 | /// } 18 | /// 19 | /// fn providers() -> Vec { 20 | /// providers![singleton(|_| "Hello").name("1")] 21 | /// } 22 | /// } 23 | /// 24 | /// struct Module2; 25 | /// 26 | /// impl Module for Module2 { 27 | /// fn submodules() -> Option> { 28 | /// Some(modules![Module1]) 29 | /// } 30 | /// 31 | /// fn providers() -> Vec { 32 | /// providers![transient(|_| "World").name("2")] 33 | /// } 34 | /// } 35 | /// 36 | /// # fn main() { 37 | /// let mut cx = Context::create(modules![Module2]); 38 | /// let mut a = cx.resolve_by_type::<&'static str>(); 39 | /// a.sort(); 40 | /// assert!(format!("{:?}", a) == *r#"["Hello", "World"]"#); 41 | /// # } 42 | /// ``` 43 | pub trait Module { 44 | /// Whether the providers included in the module should be created eagerly, default is false. 45 | fn eager_create() -> bool { 46 | false 47 | } 48 | 49 | /// Included submodules, default is None. 50 | fn submodules() -> Option> { 51 | None 52 | } 53 | 54 | /// Included providers. 55 | fn providers() -> Vec; 56 | } 57 | 58 | /// A type representing a Module, converted from a type that implements [`Module`]. 59 | pub struct ResolveModule { 60 | ty: Type, 61 | eager_create: bool, 62 | submodules: Option>, 63 | providers: Vec, 64 | } 65 | 66 | impl ResolveModule { 67 | /// Create a [`ResolveModule`] from a type that implements [`Module`]. 68 | pub fn new() -> Self { 69 | Self { 70 | ty: Type::new::(), 71 | eager_create: T::eager_create(), 72 | submodules: T::submodules(), 73 | providers: T::providers(), 74 | } 75 | } 76 | 77 | /// Represents the type that is converted to a ResolveModule. 78 | pub fn ty(&self) -> Type { 79 | self.ty 80 | } 81 | 82 | /// Whether the providers included in the module should be created eagerly. 83 | pub fn eager_create(&self) -> bool { 84 | self.eager_create 85 | } 86 | 87 | pub(crate) fn submodules(&mut self) -> Option> { 88 | self.submodules.take() 89 | } 90 | 91 | pub(crate) fn providers(self) -> Vec { 92 | self.providers 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /rudi/src/registry.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use crate::{DynProvider, DynSingle, Key, Provider}; 4 | 5 | #[derive(Default)] 6 | pub(crate) struct SingleRegistry { 7 | registry: HashMap, 8 | } 9 | 10 | impl SingleRegistry { 11 | pub(crate) fn inner(&self) -> &HashMap { 12 | &self.registry 13 | } 14 | 15 | pub(crate) fn insert(&mut self, key: Key, single: DynSingle) { 16 | // There is no need to check the value of `allow_override` here, 17 | // because when inserting a provider and a single with the same key into the context, 18 | // the provider must be inserted first, followed by the single, 19 | // and the checking of `allow_override` has already been done when the provider is inserted. 20 | self.registry.insert(key, single); 21 | } 22 | 23 | pub(crate) fn get_owned(&self, key: &Key) -> Option { 24 | self.registry.get(key)?.as_single::()?.get_owned() 25 | } 26 | 27 | pub(crate) fn get_ref(&self, key: &Key) -> Option<&T> { 28 | Some(self.registry.get(key)?.as_single::()?.get_ref()) 29 | } 30 | 31 | pub(crate) fn contains(&self, key: &Key) -> bool { 32 | self.registry.contains_key(key) 33 | } 34 | 35 | pub(crate) fn remove(&mut self, key: &Key) -> Option { 36 | self.registry.remove(key) 37 | } 38 | } 39 | 40 | #[derive(Default)] 41 | pub(crate) struct ProviderRegistry { 42 | registry: HashMap, 43 | } 44 | 45 | impl ProviderRegistry { 46 | pub(crate) fn inner(&self) -> &HashMap { 47 | &self.registry 48 | } 49 | 50 | #[track_caller] 51 | pub(crate) fn insert(&mut self, provider: DynProvider, allow_override: bool) { 52 | let definition = provider.definition(); 53 | let key = provider.key().clone(); 54 | 55 | if !self.registry.contains_key(&key) { 56 | #[cfg(feature = "tracing")] 57 | tracing::debug!("(+) insert new: {:?}", definition); 58 | } else if allow_override { 59 | #[cfg(feature = "tracing")] 60 | tracing::warn!("(!) override by `key`: {:?}", definition); 61 | } else { 62 | panic!( 63 | "already existing a provider with the same `key`: {:?}", 64 | definition 65 | ); 66 | } 67 | 68 | self.registry.insert(key, provider); 69 | } 70 | 71 | pub(crate) fn get(&self, key: &Key) -> Option<&Provider> { 72 | self.registry.get(key)?.as_provider() 73 | } 74 | 75 | pub(crate) fn contains(&self, key: &Key) -> bool { 76 | self.registry.contains_key(key) 77 | } 78 | 79 | pub(crate) fn remove(&mut self, key: &Key) -> Option { 80 | self.registry.remove(key) 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /rudi/src/single.rs: -------------------------------------------------------------------------------- 1 | use std::any::Any; 2 | 3 | /// Represents a [`Singleton`](crate::Scope::Singleton) or [`SingleOwner`](crate::Scope::SingleOwner) instance. 4 | pub struct Single { 5 | instance: T, 6 | clone: Option T>, 7 | } 8 | 9 | impl Single { 10 | pub(crate) fn new(instance: T, clone: Option T>) -> Self { 11 | Self { instance, clone } 12 | } 13 | 14 | /// Returns the owned instance. 15 | pub fn get_owned(&self) -> Option { 16 | self.clone.map(|clone| clone(&self.instance)) 17 | } 18 | 19 | /// Returns a reference to the instance. 20 | pub fn get_ref(&self) -> &T { 21 | &self.instance 22 | } 23 | } 24 | 25 | /// Represents a [`Single`] that erased its type. 26 | pub struct DynSingle { 27 | origin: Box, 28 | } 29 | 30 | impl DynSingle { 31 | /// Returns a reference of the origin [`Single`]. 32 | pub fn as_single(&self) -> Option<&Single> { 33 | self.origin.downcast_ref::>() 34 | } 35 | } 36 | 37 | impl From> for DynSingle { 38 | fn from(value: Single) -> Self { 39 | Self { 40 | origin: Box::new(value), 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /rudi/src/ty.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any::{self, TypeId}, 3 | cmp::Ordering, 4 | hash::{Hash, Hasher}, 5 | }; 6 | 7 | /// Represents a type. 8 | #[derive(Clone, Copy, Debug)] 9 | pub struct Type { 10 | /// The name of the type. 11 | pub name: &'static str, 12 | /// The unique identifier of the type. 13 | pub id: TypeId, 14 | } 15 | 16 | impl Type { 17 | pub(crate) fn new() -> Type { 18 | Type { 19 | name: any::type_name::(), 20 | id: TypeId::of::(), 21 | } 22 | } 23 | } 24 | 25 | impl PartialEq for Type { 26 | fn eq(&self, other: &Self) -> bool { 27 | self.id == other.id 28 | } 29 | } 30 | 31 | impl Eq for Type {} 32 | 33 | impl PartialOrd for Type { 34 | fn partial_cmp(&self, other: &Self) -> Option { 35 | Some(self.cmp(other)) 36 | } 37 | } 38 | 39 | impl Ord for Type { 40 | fn cmp(&self, other: &Self) -> Ordering { 41 | self.id.cmp(&other.id) 42 | } 43 | } 44 | 45 | impl Hash for Type { 46 | fn hash(&self, state: &mut H) { 47 | self.id.hash(state); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /rudi/tests/components/mod.rs: -------------------------------------------------------------------------------- 1 | use std::{any::Any, rc::Rc}; 2 | 3 | use rudi::Singleton; 4 | 5 | #[derive(Clone, Debug)] 6 | #[Singleton] 7 | pub(crate) struct ComponentA; 8 | 9 | #[derive(Clone)] 10 | #[Singleton] 11 | pub(crate) struct ComponentB { 12 | #[allow(dead_code)] 13 | pub(crate) a: ComponentA, 14 | } 15 | 16 | pub(crate) trait Trait1 { 17 | #[allow(dead_code)] // false positive 18 | fn as_any(&self) -> &dyn Any; 19 | } 20 | 21 | pub(crate) trait Trait2 {} 22 | 23 | #[derive(Clone)] 24 | #[Singleton(binds = [Self::into_trait1, Self::into_trait2])] 25 | pub(crate) struct Component1; 26 | 27 | impl Component1 { 28 | pub(crate) fn into_trait1(self) -> Rc { 29 | Rc::new(self) 30 | } 31 | 32 | fn into_trait2(self) -> Rc { 33 | Rc::new(self) 34 | } 35 | } 36 | 37 | impl Trait1 for Component1 { 38 | fn as_any(&self) -> &dyn Any { 39 | self 40 | } 41 | } 42 | 43 | impl Trait2 for Component1 {} 44 | 45 | #[derive(Clone)] 46 | #[Singleton(binds = [Self::into_trait1])] 47 | pub(crate) struct Component2; 48 | 49 | impl Component2 { 50 | pub(crate) fn into_trait1(self) -> Rc { 51 | Rc::new(self) 52 | } 53 | } 54 | 55 | impl Trait1 for Component2 { 56 | fn as_any(&self) -> &dyn Any { 57 | self 58 | } 59 | } 60 | 61 | #[allow(dead_code)] // false positive 62 | #[derive(Clone)] 63 | pub(crate) struct Holder { 64 | pub(crate) id: usize, 65 | } 66 | -------------------------------------------------------------------------------- /rudi/tests/context_allow_override.rs: -------------------------------------------------------------------------------- 1 | use rudi::{components, modules, Context, DynProvider, Module, Transient}; 2 | 3 | #[test] 4 | fn allow_override_in_same_module() { 5 | #[Transient] 6 | struct A; 7 | 8 | struct MyModule; 9 | impl Module for MyModule { 10 | fn providers() -> Vec { 11 | components![A, A] 12 | } 13 | } 14 | 15 | let cx = Context::create(modules![MyModule]); 16 | assert_eq!(cx.provider_registry().len(), 1); 17 | } 18 | 19 | #[test] 20 | fn allow_override_in_defferent_module() { 21 | #[Transient] 22 | struct A; 23 | 24 | struct MyModule1; 25 | impl Module for MyModule1 { 26 | fn providers() -> Vec { 27 | components![A] 28 | } 29 | } 30 | 31 | struct MyModule2; 32 | impl Module for MyModule2 { 33 | fn providers() -> Vec { 34 | components![A] 35 | } 36 | } 37 | 38 | let cx = Context::create(modules![MyModule1, MyModule2]); 39 | assert_eq!(cx.provider_registry().len(), 1); 40 | } 41 | 42 | #[test] 43 | #[should_panic] 44 | fn disallow_override_in_same_module() { 45 | #[Transient] 46 | struct A; 47 | 48 | struct MyModule; 49 | impl Module for MyModule { 50 | fn providers() -> Vec { 51 | components![A, A] 52 | } 53 | } 54 | 55 | let cx = Context::options() 56 | .allow_override(false) 57 | .create(modules![MyModule]); 58 | assert_eq!(cx.provider_registry().len(), 1); 59 | } 60 | 61 | #[test] 62 | #[should_panic] 63 | fn disallow_override_in_defferent_module() { 64 | #[Transient] 65 | struct A; 66 | 67 | struct MyModule1; 68 | impl Module for MyModule1 { 69 | fn providers() -> Vec { 70 | components![A] 71 | } 72 | } 73 | 74 | struct MyModule2; 75 | impl Module for MyModule2 { 76 | fn providers() -> Vec { 77 | components![A] 78 | } 79 | } 80 | 81 | let cx = Context::options() 82 | .allow_override(false) 83 | .create(modules![MyModule1, MyModule2]); 84 | assert_eq!(cx.provider_registry().len(), 1); 85 | } 86 | -------------------------------------------------------------------------------- /rudi/tests/context_auto_register.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Context, Singleton, Transient}; 2 | 3 | #[test] 4 | fn auto_register() { 5 | #[Transient] 6 | struct A; 7 | 8 | #[Singleton] 9 | fn Number() -> i32 { 10 | 1 11 | } 12 | 13 | #[allow(dead_code)] 14 | struct B(i32); 15 | 16 | #[Transient] 17 | impl B { 18 | #[di] 19 | fn new(i: i32) -> B { 20 | B(i) 21 | } 22 | } 23 | 24 | let mut cx = Context::auto_register(); 25 | assert!(cx.resolve_option::().is_some()); 26 | assert!(cx.resolve_option::().is_some()); 27 | assert!(cx.resolve_option::().is_some()); 28 | } 29 | 30 | #[tokio::test] 31 | async fn auto_register_async() { 32 | #[Transient(async)] 33 | struct A; 34 | 35 | #[Singleton] 36 | async fn Number() -> i32 { 37 | 1 38 | } 39 | 40 | #[allow(dead_code)] 41 | struct B(i32); 42 | 43 | #[Transient] 44 | impl B { 45 | #[di] 46 | async fn new(i: i32) -> B { 47 | B(i) 48 | } 49 | } 50 | 51 | let mut cx = Context::auto_register_async().await; 52 | assert!(cx.resolve_option_async::().await.is_some()); 53 | assert!(cx.resolve_option_async::().await.is_some()); 54 | assert!(cx.resolve_option_async::().await.is_some()); 55 | } 56 | -------------------------------------------------------------------------------- /rudi/tests/context_call_sync_in_async.rs: -------------------------------------------------------------------------------- 1 | use rudi::{components, modules, Context, DynProvider, Module, Singleton, Transient}; 2 | 3 | #[tokio::test] 4 | async fn resolve_in_async_context() { 5 | #[Transient] 6 | struct A; 7 | 8 | #[derive(Clone)] 9 | struct B; 10 | 11 | #[Singleton] 12 | impl B { 13 | #[di] 14 | fn new() -> B { 15 | B 16 | } 17 | } 18 | 19 | #[Singleton] 20 | fn Number() -> i32 { 21 | 42 22 | } 23 | 24 | struct MyModule; 25 | 26 | impl Module for MyModule { 27 | fn providers() -> Vec { 28 | components![A, B, Number] 29 | } 30 | } 31 | 32 | let mut cx = Context::create(modules![MyModule]); 33 | 34 | assert!(cx.resolve_option_async::().await.is_some()); 35 | assert!(cx.resolve_option_async::().await.is_some()); 36 | assert!(cx.resolve_option_async::().await.is_some()); 37 | } 38 | 39 | #[tokio::test] 40 | async fn create_eager_instance_in_async_context() { 41 | #[Transient] 42 | struct A; 43 | 44 | #[derive(Clone)] 45 | struct B; 46 | 47 | #[Singleton] 48 | impl B { 49 | #[di] 50 | fn new() -> B { 51 | B 52 | } 53 | } 54 | 55 | #[Singleton] 56 | fn Number() -> i32 { 57 | 42 58 | } 59 | 60 | struct MyModule; 61 | 62 | impl Module for MyModule { 63 | fn providers() -> Vec { 64 | components![A, B, Number] 65 | } 66 | } 67 | 68 | let cx = Context::options() 69 | .eager_create(true) 70 | .create_async(modules![MyModule]) 71 | .await; 72 | 73 | assert_eq!(cx.provider_registry().len(), 3); 74 | assert_eq!(cx.single_registry().len(), 2); 75 | } 76 | -------------------------------------------------------------------------------- /rudi/tests/context_circular_dependency.rs: -------------------------------------------------------------------------------- 1 | use std::rc::Rc; 2 | 3 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 4 | 5 | #[test] 6 | #[should_panic] 7 | fn circular_dependency() { 8 | trait A {} 9 | 10 | trait B {} 11 | 12 | fn a(t: T) -> Rc { 13 | Rc::new(t) 14 | } 15 | 16 | fn b(t: T) -> Rc { 17 | Rc::new(t) 18 | } 19 | 20 | #[allow(dead_code)] 21 | #[derive(Clone)] 22 | #[Singleton(binds = [a])] 23 | struct AImpl(Rc); 24 | 25 | impl A for AImpl {} 26 | 27 | #[allow(dead_code)] 28 | #[derive(Clone)] 29 | #[Singleton(binds = [b])] 30 | struct BImpl(Rc); 31 | 32 | impl B for BImpl {} 33 | 34 | struct MyModule; 35 | 36 | impl Module for MyModule { 37 | fn providers() -> Vec { 38 | components![AImpl, BImpl] 39 | } 40 | } 41 | 42 | let mut cx = Context::create(modules![MyModule]); 43 | cx.resolve::(); 44 | } 45 | 46 | #[tokio::test] 47 | #[should_panic] 48 | async fn circular_dependency_async() { 49 | trait A {} 50 | 51 | trait B {} 52 | 53 | fn a(t: T) -> Rc { 54 | Rc::new(t) 55 | } 56 | 57 | fn b(t: T) -> Rc { 58 | Rc::new(t) 59 | } 60 | 61 | #[allow(dead_code)] 62 | #[derive(Clone)] 63 | #[Singleton(binds = [a], async)] 64 | struct AImpl(Rc); 65 | 66 | impl A for AImpl {} 67 | 68 | #[allow(dead_code)] 69 | #[derive(Clone)] 70 | #[Singleton(binds = [b], async)] 71 | struct BImpl(Rc); 72 | 73 | impl B for BImpl {} 74 | 75 | struct MyModule; 76 | 77 | impl Module for MyModule { 78 | fn providers() -> Vec { 79 | components![AImpl, BImpl] 80 | } 81 | } 82 | 83 | let mut cx = Context::create(modules![MyModule]); 84 | cx.resolve_async::().await; 85 | } 86 | -------------------------------------------------------------------------------- /rudi/tests/context_dynamic_modules.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::rc::Rc; 4 | 5 | use rudi::{ 6 | components, modules, providers, singleton, singleton_async, transient, transient_async, 7 | Context, DynProvider, FutureExt, Module, Scope, Singleton, Transient, 8 | }; 9 | 10 | use crate::components::{Component1, Holder, Trait1}; 11 | 12 | #[test] 13 | fn empty_module() { 14 | struct MyModule; 15 | impl Module for MyModule { 16 | fn providers() -> Vec { 17 | providers![] 18 | } 19 | } 20 | 21 | let mut cx = Context::create(modules![]); 22 | assert!(cx.provider_registry().is_empty()); 23 | assert!(cx.single_registry().is_empty()); 24 | 25 | cx.unload_modules(modules![]); 26 | assert!(cx.provider_registry().is_empty()); 27 | assert!(cx.single_registry().is_empty()); 28 | 29 | cx.load_modules(modules![MyModule]); 30 | assert!(cx.provider_registry().is_empty()); 31 | assert!(cx.single_registry().is_empty()); 32 | 33 | cx.unload_modules(modules![MyModule]); 34 | assert!(cx.provider_registry().is_empty()); 35 | assert!(cx.single_registry().is_empty()); 36 | } 37 | 38 | #[test] 39 | fn unload_singleton() { 40 | #[derive(Clone)] 41 | #[Singleton] 42 | struct A; 43 | 44 | struct MyModule; 45 | impl Module for MyModule { 46 | fn providers() -> Vec { 47 | components![A] 48 | } 49 | } 50 | 51 | let mut cx = Context::create(modules![MyModule]); 52 | 53 | let provider = cx.get_provider::(); 54 | assert!(provider.is_some()); 55 | assert!(provider.unwrap().definition().scope == Scope::Singleton); 56 | assert!(cx.resolve_option::().is_some()); 57 | 58 | cx.unload_modules(modules![MyModule]); 59 | 60 | assert!(cx.get_provider::().is_none()); 61 | assert!(cx.resolve_option::().is_none()); 62 | } 63 | 64 | #[test] 65 | fn unload_singleton_with_bind() { 66 | struct MyModule; 67 | impl Module for MyModule { 68 | fn providers() -> Vec { 69 | providers![singleton(|_| Component1).bind(Component1::into_trait1)] 70 | } 71 | } 72 | 73 | let mut cx = Context::create(modules![MyModule]); 74 | 75 | assert!(cx.get_provider::>().is_some()); 76 | 77 | let provider = cx.get_provider::(); 78 | assert!(provider.is_some()); 79 | assert!(provider.unwrap().definition().scope == Scope::Singleton); 80 | 81 | assert!(cx.resolve_option::().is_some()); 82 | assert!(cx.resolve_option::>().is_some()); 83 | 84 | cx.unload_modules(modules![MyModule]); 85 | 86 | assert!(cx.get_provider::().is_none()); 87 | 88 | assert!(cx.resolve_option::().is_none()); 89 | assert!(cx.resolve_option::>().is_none()); 90 | } 91 | 92 | #[test] 93 | fn unload_module() { 94 | #[Transient] 95 | struct A; 96 | 97 | #[Transient] 98 | struct B; 99 | 100 | struct Module1; 101 | impl Module for Module1 { 102 | fn providers() -> Vec { 103 | components![A] 104 | } 105 | } 106 | 107 | struct Module2; 108 | impl Module for Module2 { 109 | fn providers() -> Vec { 110 | components![B] 111 | } 112 | } 113 | 114 | let mut cx = Context::create(modules![Module1, Module2]); 115 | 116 | assert!(cx.get_provider::().is_some()); 117 | assert!(cx.get_provider::().is_some()); 118 | 119 | assert!(cx.resolve_option::().is_some()); 120 | assert!(cx.resolve_option::().is_some()); 121 | 122 | cx.unload_modules(modules![Module2]); 123 | 124 | assert!(cx.get_provider::().is_none()); 125 | assert!(cx.resolve_option::().is_none()); 126 | } 127 | 128 | #[test] 129 | fn unload_module_with_transient() { 130 | #[Transient] 131 | struct A; 132 | 133 | #[Transient] 134 | struct B(A); 135 | 136 | struct Module1; 137 | impl Module for Module1 { 138 | fn providers() -> Vec { 139 | components![A] 140 | } 141 | } 142 | 143 | struct Module2; 144 | impl Module for Module2 { 145 | fn providers() -> Vec { 146 | components![B] 147 | } 148 | } 149 | 150 | let mut cx = Context::create(modules![Module1, Module2]); 151 | 152 | assert!(cx.get_provider::().is_some()); 153 | assert!(cx.get_provider::().is_some()); 154 | 155 | assert!(cx.resolve_option::().is_some()); 156 | assert!(cx.resolve_option::().is_some()); 157 | 158 | cx.unload_modules(modules![Module2]); 159 | 160 | assert!(cx.get_provider::().is_none()); 161 | assert!(cx.resolve_option::().is_none()); 162 | } 163 | 164 | #[test] 165 | fn unload_module_with_override() { 166 | struct MyModule1; 167 | impl Module for MyModule1 { 168 | fn providers() -> Vec { 169 | providers![transient(|_| Holder { id: 42 })] 170 | } 171 | } 172 | 173 | struct MyModule2; 174 | impl Module for MyModule2 { 175 | fn providers() -> Vec { 176 | providers![transient(|_| Holder { id: 24 })] 177 | } 178 | } 179 | 180 | let mut cx = Context::create(modules![MyModule1, MyModule2]); 181 | 182 | assert!(cx.get_provider::().is_some()); 183 | assert_eq!(cx.resolve::().id, 24); 184 | 185 | cx.unload_modules(modules![MyModule2]); 186 | 187 | assert!(cx.get_provider::().is_none()); 188 | assert!(cx.resolve_option::().is_none()); 189 | } 190 | 191 | #[test] 192 | fn reload_module() { 193 | struct MyModule; 194 | impl Module for MyModule { 195 | fn providers() -> Vec { 196 | providers![singleton(|_| Holder { id: 42 })] 197 | } 198 | } 199 | 200 | let mut cx = Context::create(modules![MyModule]); 201 | 202 | assert!(cx.get_provider::().is_some()); 203 | assert_eq!(cx.resolve::().id, 42); 204 | 205 | cx.unload_modules(modules![MyModule]); 206 | cx.load_modules(modules![MyModule]); 207 | 208 | assert!(cx.get_provider::().is_some()); 209 | assert_eq!(cx.resolve::().id, 42); 210 | } 211 | 212 | #[tokio::test] 213 | async fn unload_singleton_async() { 214 | #[derive(Clone)] 215 | #[Singleton(async)] 216 | struct A; 217 | 218 | struct MyModule; 219 | impl Module for MyModule { 220 | fn providers() -> Vec { 221 | components![A] 222 | } 223 | } 224 | 225 | let mut cx = Context::create(modules![MyModule]); 226 | 227 | let provider = cx.get_provider::(); 228 | assert!(provider.is_some()); 229 | assert!(provider.unwrap().definition().scope == Scope::Singleton); 230 | assert!(cx.resolve_option_async::().await.is_some()); 231 | 232 | cx.unload_modules(modules![MyModule]); 233 | 234 | assert!(cx.get_provider::().is_none()); 235 | assert!(cx.resolve_option_async::().await.is_none()); 236 | } 237 | 238 | #[tokio::test] 239 | async fn unload_singleton_with_bind_async() { 240 | struct MyModule; 241 | impl Module for MyModule { 242 | fn providers() -> Vec { 243 | providers![ 244 | singleton_async(|_| async { Component1 }.boxed()).bind(Component1::into_trait1) 245 | ] 246 | } 247 | } 248 | 249 | let mut cx = Context::create(modules![MyModule]); 250 | 251 | assert!(cx.get_provider::>().is_some()); 252 | 253 | let provider = cx.get_provider::(); 254 | assert!(provider.is_some()); 255 | assert!(provider.unwrap().definition().scope == Scope::Singleton); 256 | 257 | assert!(cx.resolve_option_async::().await.is_some()); 258 | assert!(cx.resolve_option_async::>().await.is_some()); 259 | 260 | cx.unload_modules(modules![MyModule]); 261 | 262 | assert!(cx.get_provider::().is_none()); 263 | 264 | assert!(cx.resolve_option_async::().await.is_none()); 265 | assert!(cx.resolve_option_async::>().await.is_none()); 266 | } 267 | 268 | #[tokio::test] 269 | async fn unload_module_async() { 270 | #[Transient(async)] 271 | struct A; 272 | 273 | #[Transient(async)] 274 | struct B; 275 | 276 | struct Module1; 277 | impl Module for Module1 { 278 | fn providers() -> Vec { 279 | components![A] 280 | } 281 | } 282 | 283 | struct Module2; 284 | impl Module for Module2 { 285 | fn providers() -> Vec { 286 | components![B] 287 | } 288 | } 289 | 290 | let mut cx = Context::create(modules![Module1, Module2]); 291 | 292 | assert!(cx.get_provider::().is_some()); 293 | assert!(cx.get_provider::().is_some()); 294 | 295 | assert!(cx.resolve_option_async::().await.is_some()); 296 | assert!(cx.resolve_option_async::().await.is_some()); 297 | 298 | cx.unload_modules(modules![Module2]); 299 | 300 | assert!(cx.get_provider::().is_none()); 301 | assert!(cx.resolve_option_async::().await.is_none()); 302 | } 303 | 304 | #[tokio::test] 305 | async fn unload_module_with_transient_async() { 306 | #[Transient(async)] 307 | struct A; 308 | 309 | #[Transient(async)] 310 | struct B(A); 311 | 312 | struct Module1; 313 | impl Module for Module1 { 314 | fn providers() -> Vec { 315 | components![A] 316 | } 317 | } 318 | 319 | struct Module2; 320 | impl Module for Module2 { 321 | fn providers() -> Vec { 322 | components![B] 323 | } 324 | } 325 | 326 | let mut cx = Context::create(modules![Module1, Module2]); 327 | 328 | assert!(cx.get_provider::().is_some()); 329 | assert!(cx.get_provider::().is_some()); 330 | 331 | assert!(cx.resolve_option_async::().await.is_some()); 332 | assert!(cx.resolve_option_async::().await.is_some()); 333 | 334 | cx.unload_modules(modules![Module2]); 335 | 336 | assert!(cx.get_provider::().is_none()); 337 | assert!(cx.resolve_option_async::().await.is_none()); 338 | } 339 | 340 | #[tokio::test] 341 | async fn unload_module_with_override_async() { 342 | struct MyModule1; 343 | impl Module for MyModule1 { 344 | fn providers() -> Vec { 345 | providers![transient_async(|_| async { Holder { id: 42 } }.boxed())] 346 | } 347 | } 348 | 349 | struct MyModule2; 350 | impl Module for MyModule2 { 351 | fn providers() -> Vec { 352 | providers![transient_async(|_| async { Holder { id: 24 } }.boxed())] 353 | } 354 | } 355 | 356 | let mut cx = Context::create(modules![MyModule1, MyModule2]); 357 | 358 | assert!(cx.get_provider::().is_some()); 359 | assert_eq!(cx.resolve_async::().await.id, 24); 360 | 361 | cx.unload_modules(modules![MyModule2]); 362 | 363 | assert!(cx.get_provider::().is_none()); 364 | assert!(cx.resolve_option_async::().await.is_none()); 365 | } 366 | 367 | #[tokio::test] 368 | async fn reload_module_async() { 369 | struct MyModule; 370 | impl Module for MyModule { 371 | fn providers() -> Vec { 372 | providers![singleton_async(|_| async { Holder { id: 42 } }.boxed())] 373 | } 374 | } 375 | 376 | let mut cx = Context::create(modules![MyModule]); 377 | 378 | assert!(cx.get_provider::().is_some()); 379 | assert_eq!(cx.resolve_async::().await.id, 42); 380 | 381 | cx.unload_modules(modules![MyModule]); 382 | cx.load_modules(modules![MyModule]); 383 | 384 | assert!(cx.get_provider::().is_some()); 385 | assert_eq!(cx.resolve_async::().await.id, 42); 386 | } 387 | -------------------------------------------------------------------------------- /rudi/tests/context_standalone_variables.rs: -------------------------------------------------------------------------------- 1 | use rudi::{modules, Context, Scope}; 2 | 3 | #[test] 4 | fn standalone_variables() { 5 | macro_rules! single_test { 6 | ($method:ident, $variant:ident) => { 7 | let cx = Context::options() 8 | .$method(42i32) 9 | .$method(true) 10 | .$method("Hello world") 11 | .create(modules![]); 12 | 13 | assert_eq!(cx.single_registry().len(), 3); 14 | 15 | assert_eq!(cx.get_single::(), &42); 16 | assert!(*cx.get_single::()); 17 | assert_eq!(cx.get_single::<&str>(), &"Hello world"); 18 | 19 | assert_eq!(cx.single_registry().len(), 3); 20 | 21 | cx.provider_registry().iter().for_each(|(_, provider)| { 22 | assert!(provider.definition().scope == Scope::$variant); 23 | assert!(!provider.eager_create()); 24 | }); 25 | }; 26 | } 27 | 28 | { 29 | single_test!(singleton, Singleton); 30 | } 31 | 32 | { 33 | single_test!(single_owner, SingleOwner); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /rudi/tests/feat_eager_create_instance.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::cell::RefCell; 4 | 5 | use rudi::{ 6 | components, modules, Context, DefaultProvider, DynProvider, Module, Singleton, Transient, 7 | }; 8 | 9 | #[test] 10 | fn eager_create_context() { 11 | #[derive(Clone)] 12 | #[Singleton] 13 | struct A; 14 | 15 | struct MyModule; 16 | 17 | impl Module for MyModule { 18 | fn providers() -> Vec { 19 | components![A] 20 | } 21 | } 22 | 23 | let cx = Context::options() 24 | .eager_create(true) 25 | .create(modules![MyModule]); 26 | 27 | assert!(cx.single_registry().len() == 1); 28 | 29 | let provider = cx.get_provider::(); 30 | assert!(provider.is_some()); 31 | assert!(cx.contains_single::()) 32 | } 33 | 34 | #[test] 35 | fn eager_create_provider() { 36 | thread_local! { 37 | static CREATED: RefCell = const { RefCell::new(false) }; 38 | } 39 | 40 | #[derive(Clone)] 41 | struct A; 42 | 43 | #[Singleton(eager_create)] 44 | impl A { 45 | #[di] 46 | fn new() -> A { 47 | CREATED.set(true); 48 | 49 | A 50 | } 51 | } 52 | 53 | struct MyModule; 54 | impl Module for MyModule { 55 | fn providers() -> Vec { 56 | components![A] 57 | } 58 | } 59 | 60 | assert!(A::provider().eager_create()); 61 | 62 | let cx = Context::create(modules![MyModule]); 63 | 64 | assert!(cx.single_registry().len() == 1); 65 | assert!(CREATED.with_borrow(|created| *created)); 66 | } 67 | 68 | #[test] 69 | fn eager_create_module() { 70 | thread_local! { 71 | static COUNT: RefCell = const { RefCell::new(0) }; 72 | } 73 | 74 | #[derive(Clone)] 75 | struct A; 76 | 77 | #[Singleton(eager_create)] 78 | impl A { 79 | #[di] 80 | fn new() -> A { 81 | COUNT.with_borrow_mut(|c| { 82 | *c += 1; 83 | }); 84 | 85 | A 86 | } 87 | } 88 | 89 | struct MyModule; 90 | 91 | impl Module for MyModule { 92 | fn eager_create() -> bool { 93 | true 94 | } 95 | 96 | fn providers() -> Vec { 97 | components![A] 98 | } 99 | } 100 | 101 | assert!(A::provider().eager_create()); 102 | 103 | let mut cx = Context::create(modules![MyModule]); 104 | 105 | assert!(cx.single_registry().len() == 1); 106 | assert!(COUNT.with_borrow(|created| *created == 1)); 107 | 108 | cx.resolve::(); 109 | 110 | assert!(cx.single_registry().len() == 1); 111 | assert!(COUNT.with_borrow(|created| *created == 1)); 112 | } 113 | 114 | #[test] 115 | fn eager_create_module_twice() { 116 | thread_local! { 117 | static COUNT: RefCell = const { RefCell::new(0) }; 118 | } 119 | 120 | #[derive(Clone)] 121 | struct A; 122 | 123 | #[Singleton(eager_create)] 124 | impl A { 125 | #[di] 126 | fn new() -> A { 127 | COUNT.with_borrow_mut(|c| { 128 | *c += 1; 129 | }); 130 | 131 | A 132 | } 133 | } 134 | 135 | struct MyModule; 136 | 137 | impl Module for MyModule { 138 | fn providers() -> Vec { 139 | components![A] 140 | } 141 | } 142 | 143 | assert!(A::provider().eager_create()); 144 | 145 | let mut cx = Context::create(modules![MyModule]); 146 | 147 | assert!(COUNT.with_borrow(|created| *created == 1)); 148 | assert!(cx.single_registry().len() == 1); 149 | 150 | cx.flush(); 151 | 152 | assert!(COUNT.with_borrow(|created| *created == 1)); 153 | assert!(cx.single_registry().len() == 1); 154 | } 155 | 156 | #[test] 157 | fn eager_create_two_modules() { 158 | thread_local! { 159 | static COUNT: RefCell = const { RefCell::new(0) }; 160 | } 161 | 162 | #[derive(Clone)] 163 | struct A; 164 | 165 | #[Singleton(eager_create)] 166 | impl A { 167 | #[di] 168 | fn new() -> A { 169 | COUNT.with_borrow_mut(|c| { 170 | *c += 1; 171 | }); 172 | 173 | A 174 | } 175 | } 176 | 177 | #[derive(Clone)] 178 | struct B(A); 179 | 180 | #[Singleton(eager_create)] 181 | impl B { 182 | #[di] 183 | fn new(a: A) -> B { 184 | COUNT.with_borrow_mut(|c| { 185 | *c += 1; 186 | }); 187 | 188 | B(a) 189 | } 190 | } 191 | 192 | struct MyModule1; 193 | 194 | impl Module for MyModule1 { 195 | fn eager_create() -> bool { 196 | true 197 | } 198 | 199 | fn providers() -> Vec { 200 | components![A] 201 | } 202 | } 203 | 204 | struct MyModule2; 205 | 206 | impl Module for MyModule2 { 207 | fn eager_create() -> bool { 208 | true 209 | } 210 | 211 | fn providers() -> Vec { 212 | components![B] 213 | } 214 | } 215 | 216 | assert!(A::provider().eager_create()); 217 | assert!(B::provider().eager_create()); 218 | 219 | let mut cx = Context::create(modules![MyModule1, MyModule2]); 220 | 221 | assert!(COUNT.with_borrow(|created| *created == 2)); 222 | assert!(cx.single_registry().len() == 2); 223 | 224 | cx.resolve::(); 225 | cx.resolve::(); 226 | 227 | assert!(COUNT.with_borrow(|created| *created == 2)); 228 | assert!(cx.single_registry().len() == 2); 229 | } 230 | 231 | #[test] 232 | #[should_panic] 233 | fn create_eager_instances_async_in_sync_context() { 234 | #[Transient(async)] 235 | struct A; 236 | 237 | struct MyModule; 238 | impl Module for MyModule { 239 | fn providers() -> Vec { 240 | components![A] 241 | } 242 | } 243 | 244 | Context::options() 245 | .allow_only_single_eager_create(false) 246 | .eager_create(true) 247 | .create(modules![MyModule]); 248 | } 249 | 250 | #[test] 251 | fn only_singleton_or_all_scope_eager_create() { 252 | thread_local! { 253 | static COUNT: RefCell = const { RefCell::new(0) }; 254 | } 255 | 256 | #[derive(Clone)] 257 | struct A; 258 | 259 | #[Singleton] 260 | impl A { 261 | #[di] 262 | fn new() -> A { 263 | COUNT.with_borrow_mut(|c| { 264 | *c += 1; 265 | }); 266 | 267 | A 268 | } 269 | } 270 | 271 | struct B; 272 | 273 | #[Transient] 274 | impl B { 275 | #[di] 276 | fn new() -> B { 277 | COUNT.with_borrow_mut(|c| { 278 | *c += 1; 279 | }); 280 | 281 | B 282 | } 283 | } 284 | 285 | struct MyModule; 286 | 287 | impl Module for MyModule { 288 | fn providers() -> Vec { 289 | components![A, B] 290 | } 291 | } 292 | 293 | Context::options() 294 | .eager_create(true) 295 | .create(modules![MyModule]); 296 | assert!(COUNT.with_borrow(|created| *created == 1)); 297 | 298 | Context::options() 299 | .allow_only_single_eager_create(false) 300 | .eager_create(true) 301 | .create(modules![MyModule]); 302 | assert!(COUNT.with_borrow(|created| *created == 3)); 303 | } 304 | 305 | #[tokio::test] 306 | async fn eager_create_context_async() { 307 | #[derive(Clone)] 308 | #[Singleton(async)] 309 | struct A; 310 | 311 | struct MyModule; 312 | impl Module for MyModule { 313 | fn providers() -> Vec { 314 | components![A] 315 | } 316 | } 317 | 318 | let cx = Context::options() 319 | .eager_create(true) 320 | .create_async(modules![MyModule]) 321 | .await; 322 | 323 | assert!(cx.single_registry().len() == 1); 324 | 325 | let provider = cx.get_provider::(); 326 | assert!(provider.is_some()); 327 | assert!(cx.contains_single::()) 328 | } 329 | 330 | #[tokio::test] 331 | async fn eager_create_provider_async() { 332 | thread_local! { 333 | static CREATED: RefCell = const { RefCell::new(false) }; 334 | } 335 | 336 | #[derive(Clone)] 337 | struct A; 338 | 339 | #[Singleton(eager_create)] 340 | impl A { 341 | #[di] 342 | async fn new() -> A { 343 | CREATED.set(true); 344 | 345 | A 346 | } 347 | } 348 | 349 | struct MyModule; 350 | impl Module for MyModule { 351 | fn providers() -> Vec { 352 | components![A] 353 | } 354 | } 355 | 356 | assert!(A::provider().eager_create()); 357 | 358 | let cx = Context::create_async(modules![MyModule]).await; 359 | 360 | assert!(cx.single_registry().len() == 1); 361 | assert!(CREATED.with_borrow(|created| *created)); 362 | } 363 | 364 | #[tokio::test] 365 | async fn eager_create_module_async() { 366 | thread_local! { 367 | static COUNT: RefCell = const { RefCell::new(0) }; 368 | } 369 | 370 | #[derive(Clone)] 371 | struct A; 372 | 373 | #[Singleton(eager_create)] 374 | impl A { 375 | #[di] 376 | async fn new() -> A { 377 | COUNT.with_borrow_mut(|c| { 378 | *c += 1; 379 | }); 380 | 381 | A 382 | } 383 | } 384 | 385 | struct MyModule; 386 | 387 | impl Module for MyModule { 388 | fn eager_create() -> bool { 389 | true 390 | } 391 | 392 | fn providers() -> Vec { 393 | components![A] 394 | } 395 | } 396 | 397 | assert!(A::provider().eager_create()); 398 | 399 | let mut cx = Context::create_async(modules![MyModule]).await; 400 | 401 | assert!(cx.single_registry().len() == 1); 402 | assert!(COUNT.with_borrow(|created| *created == 1)); 403 | 404 | cx.resolve_async::().await; 405 | 406 | assert!(cx.single_registry().len() == 1); 407 | assert!(COUNT.with_borrow(|created| *created == 1)); 408 | } 409 | 410 | #[tokio::test] 411 | async fn eager_create_module_twice_async() { 412 | thread_local! { 413 | static COUNT: RefCell = const { RefCell::new(0) }; 414 | } 415 | 416 | #[derive(Clone)] 417 | struct A; 418 | 419 | #[Singleton(eager_create)] 420 | impl A { 421 | #[di] 422 | async fn new() -> A { 423 | COUNT.with_borrow_mut(|c| { 424 | *c += 1; 425 | }); 426 | 427 | A 428 | } 429 | } 430 | 431 | struct MyModule; 432 | 433 | impl Module for MyModule { 434 | fn providers() -> Vec { 435 | components![A] 436 | } 437 | } 438 | 439 | assert!(A::provider().eager_create()); 440 | 441 | let mut cx = Context::create_async(modules![MyModule]).await; 442 | 443 | assert!(COUNT.with_borrow(|created| *created == 1)); 444 | assert!(cx.single_registry().len() == 1); 445 | 446 | cx.flush_async().await; 447 | 448 | assert!(COUNT.with_borrow(|created| *created == 1)); 449 | assert!(cx.single_registry().len() == 1); 450 | } 451 | 452 | #[tokio::test] 453 | async fn eager_create_two_modules_async() { 454 | thread_local! { 455 | static COUNT: RefCell = const { RefCell::new(0) }; 456 | } 457 | 458 | #[derive(Clone)] 459 | struct A; 460 | 461 | #[Singleton(eager_create)] 462 | impl A { 463 | #[di] 464 | async fn new() -> A { 465 | COUNT.with_borrow_mut(|c| { 466 | *c += 1; 467 | }); 468 | 469 | A 470 | } 471 | } 472 | 473 | #[derive(Clone)] 474 | struct B(A); 475 | 476 | #[Singleton(eager_create)] 477 | impl B { 478 | #[di] 479 | async fn new(a: A) -> B { 480 | COUNT.with_borrow_mut(|c| { 481 | *c += 1; 482 | }); 483 | 484 | B(a) 485 | } 486 | } 487 | 488 | struct MyModule1; 489 | 490 | impl Module for MyModule1 { 491 | fn eager_create() -> bool { 492 | true 493 | } 494 | 495 | fn providers() -> Vec { 496 | components![A] 497 | } 498 | } 499 | 500 | struct MyModule2; 501 | 502 | impl Module for MyModule2 { 503 | fn eager_create() -> bool { 504 | true 505 | } 506 | 507 | fn providers() -> Vec { 508 | components![B] 509 | } 510 | } 511 | 512 | assert!(A::provider().eager_create()); 513 | assert!(B::provider().eager_create()); 514 | 515 | let mut cx = Context::create_async(modules![MyModule1, MyModule2]).await; 516 | 517 | assert!(COUNT.with_borrow(|created| *created == 2)); 518 | assert!(cx.single_registry().len() == 2); 519 | 520 | cx.resolve_async::().await; 521 | cx.resolve_async::().await; 522 | 523 | assert!(COUNT.with_borrow(|created| *created == 2)); 524 | assert!(cx.single_registry().len() == 2); 525 | } 526 | 527 | #[tokio::test] 528 | async fn create_eager_instances_sync_in_async_context() { 529 | #[Transient] 530 | struct A; 531 | 532 | struct MyModule; 533 | impl Module for MyModule { 534 | fn providers() -> Vec { 535 | components![A] 536 | } 537 | } 538 | 539 | Context::options() 540 | .allow_only_single_eager_create(false) 541 | .eager_create(true) 542 | .create_async(modules![MyModule]) 543 | .await; 544 | } 545 | 546 | #[tokio::test] 547 | async fn only_singleton_or_all_scope_eager_create_async() { 548 | thread_local! { 549 | static COUNT: RefCell = const { RefCell::new(0) }; 550 | } 551 | 552 | #[derive(Clone)] 553 | struct A; 554 | 555 | #[Singleton] 556 | impl A { 557 | #[di] 558 | async fn new() -> A { 559 | COUNT.with_borrow_mut(|c| { 560 | *c += 1; 561 | }); 562 | 563 | A 564 | } 565 | } 566 | 567 | struct B; 568 | 569 | #[Transient] 570 | impl B { 571 | #[di] 572 | async fn new() -> B { 573 | COUNT.with_borrow_mut(|c| { 574 | *c += 1; 575 | }); 576 | 577 | B 578 | } 579 | } 580 | 581 | struct MyModule; 582 | 583 | impl Module for MyModule { 584 | fn providers() -> Vec { 585 | components![A, B] 586 | } 587 | } 588 | 589 | Context::options() 590 | .eager_create(true) 591 | .create_async(modules![MyModule]) 592 | .await; 593 | assert!(COUNT.with_borrow(|created| *created == 1)); 594 | 595 | Context::options() 596 | .allow_only_single_eager_create(false) 597 | .eager_create(true) 598 | .create_async(modules![MyModule]) 599 | .await; 600 | assert!(COUNT.with_borrow(|created| *created == 3)); 601 | } 602 | -------------------------------------------------------------------------------- /rudi/tests/feat_generic.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | 3 | use rudi::{components, modules, Context, DynProvider, Module, Transient}; 4 | 5 | #[test] 6 | fn generic_provider() { 7 | #[derive(Default)] 8 | #[Transient(auto_register = false)] 9 | struct A(T); 10 | 11 | struct MyModule; 12 | impl Module for MyModule { 13 | fn providers() -> Vec { 14 | components![A] 15 | } 16 | } 17 | 18 | let cx = Context::create(modules![MyModule]); 19 | assert!(cx.get_provider::>().is_some()); 20 | } 21 | 22 | #[test] 23 | fn generic_module() { 24 | #[derive(Default)] 25 | #[Transient(auto_register = false)] 26 | struct A(T); 27 | 28 | struct MyModule(PhantomData); 29 | impl Module for MyModule { 30 | fn providers() -> Vec { 31 | components![A] 32 | } 33 | } 34 | 35 | let cx = Context::create(modules![MyModule::]); 36 | assert!(cx.get_provider::>().is_some()); 37 | } 38 | 39 | #[test] 40 | fn generic_provider_async() { 41 | #[derive(Default)] 42 | #[Transient(async, auto_register = false)] 43 | struct B(T); 44 | 45 | struct MyModule; 46 | impl Module for MyModule { 47 | fn providers() -> Vec { 48 | components![B] 49 | } 50 | } 51 | 52 | let cx = Context::create(modules![MyModule]); 53 | assert!(cx.get_provider::>().is_some()); 54 | } 55 | 56 | #[test] 57 | fn generic_module_async() { 58 | #[derive(Default)] 59 | #[Transient(async, auto_register = false)] 60 | struct B(T); 61 | 62 | struct MyModule(PhantomData); 63 | impl Module for MyModule { 64 | fn providers() -> Vec { 65 | components![B] 66 | } 67 | } 68 | 69 | let cx = Context::create(modules![MyModule::]); 70 | assert!(cx.get_provider::>().is_some()); 71 | } 72 | -------------------------------------------------------------------------------- /rudi/tests/feat_impl_trait_fn_as_constructor.rs: -------------------------------------------------------------------------------- 1 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 2 | 3 | #[test] 4 | fn test() { 5 | #[derive(Clone)] 6 | #[Singleton] 7 | struct Material; 8 | 9 | trait FromMaterial { 10 | fn from_material(m: &Material) -> Self; 11 | } 12 | 13 | #[derive(Clone)] 14 | struct Product; 15 | 16 | #[Singleton] 17 | impl FromMaterial for Product { 18 | #[di] 19 | fn from_material(#[di(ref)] _: &Material) -> Self { 20 | Product 21 | } 22 | } 23 | 24 | struct MyModule; 25 | 26 | impl Module for MyModule { 27 | fn providers() -> Vec { 28 | components![Material, Product] 29 | } 30 | } 31 | 32 | let mut cx = Context::create(modules![MyModule]); 33 | 34 | assert!(cx.resolve_option::().is_some()); 35 | } 36 | -------------------------------------------------------------------------------- /rudi/tests/feat_multiple_module.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::rc::Rc; 4 | 5 | use rudi::{ 6 | components, modules, providers, singleton, singleton_async, Context, DynProvider, FutureExt, 7 | Module, ResolveModule, Singleton, 8 | }; 9 | 10 | use crate::components::{ComponentA, ComponentB}; 11 | 12 | #[test] 13 | fn resolve_with_several_modules() { 14 | struct MyModule1; 15 | 16 | impl Module for MyModule1 { 17 | fn providers() -> Vec { 18 | providers![singleton(|_| Rc::new(ComponentA))] 19 | } 20 | } 21 | 22 | #[derive(Clone)] 23 | #[Singleton] 24 | struct Holder(Rc); 25 | 26 | struct MyModule2; 27 | impl Module for MyModule2 { 28 | fn providers() -> Vec { 29 | components![Holder] 30 | } 31 | } 32 | 33 | let mut cx = Context::create(modules![MyModule1, MyModule2]); 34 | let a = cx.resolve::>(); 35 | let b = cx.resolve::(); 36 | assert!(std::ptr::eq(&*a, &*b.0)); 37 | } 38 | 39 | #[test] 40 | fn single_module() { 41 | struct MyModule; 42 | 43 | impl Module for MyModule { 44 | fn providers() -> Vec { 45 | components![ComponentA] 46 | } 47 | } 48 | 49 | let cx = Context::create(modules![MyModule]); 50 | assert_eq!(cx.provider_registry().len(), 1); 51 | } 52 | 53 | #[test] 54 | fn multiple_module() { 55 | struct MyModule1; 56 | 57 | impl Module for MyModule1 { 58 | fn providers() -> Vec { 59 | components![ComponentA] 60 | } 61 | } 62 | 63 | struct MyModule2; 64 | 65 | impl Module for MyModule2 { 66 | fn providers() -> Vec { 67 | components![ComponentB] 68 | } 69 | } 70 | 71 | let cx = Context::create(modules![MyModule1, MyModule2]); 72 | assert_eq!(cx.provider_registry().len(), 2); 73 | } 74 | 75 | #[test] 76 | fn nested_module() { 77 | struct MyModule1; 78 | 79 | impl Module for MyModule1 { 80 | fn providers() -> Vec { 81 | components![ComponentA] 82 | } 83 | } 84 | 85 | struct MyModule2; 86 | 87 | impl Module for MyModule2 { 88 | fn submodules() -> Option> { 89 | Some(modules![MyModule1]) 90 | } 91 | 92 | fn providers() -> Vec { 93 | components![ComponentB] 94 | } 95 | } 96 | 97 | let cx = Context::create(modules![MyModule2]); 98 | assert_eq!(cx.provider_registry().len(), 2); 99 | } 100 | 101 | #[test] 102 | fn duplicate_nested_module() { 103 | struct DataModule; 104 | 105 | impl Module for DataModule { 106 | fn providers() -> Vec { 107 | components![ComponentA] 108 | } 109 | } 110 | 111 | struct DomainModule; 112 | 113 | impl Module for DomainModule { 114 | fn providers() -> Vec { 115 | components![ComponentB] 116 | } 117 | } 118 | 119 | struct FeatureModule1; 120 | 121 | impl Module for FeatureModule1 { 122 | fn submodules() -> Option> { 123 | Some(modules![DomainModule, DataModule]) 124 | } 125 | 126 | fn providers() -> Vec { 127 | components![] 128 | } 129 | } 130 | 131 | struct FeatureModule2; 132 | 133 | impl Module for FeatureModule2 { 134 | fn submodules() -> Option> { 135 | Some(modules![DomainModule, DataModule]) 136 | } 137 | 138 | fn providers() -> Vec { 139 | components![] 140 | } 141 | } 142 | 143 | let cx = Context::create(modules![FeatureModule1, FeatureModule2]); 144 | assert_eq!(cx.provider_registry().len(), 2); 145 | } 146 | 147 | #[tokio::test] 148 | async fn resolve_with_several_modules_async() { 149 | struct MyModule1; 150 | 151 | impl Module for MyModule1 { 152 | fn providers() -> Vec { 153 | providers![singleton_async(|_| async { Rc::new(ComponentA) }.boxed())] 154 | } 155 | } 156 | 157 | #[derive(Clone)] 158 | #[Singleton(async)] 159 | struct Holder(Rc); 160 | 161 | struct MyModule2; 162 | impl Module for MyModule2 { 163 | fn providers() -> Vec { 164 | components![Holder] 165 | } 166 | } 167 | 168 | let mut cx = Context::create(modules![MyModule1, MyModule2]); 169 | let a = cx.resolve_async::>().await; 170 | let b = cx.resolve_async::().await; 171 | assert!(std::ptr::eq(&*a, &*b.0)); 172 | } 173 | 174 | #[tokio::test] 175 | async fn single_module_async() { 176 | struct MyModule; 177 | 178 | impl Module for MyModule { 179 | fn providers() -> Vec { 180 | providers![singleton_async(|_| async { ComponentA }.boxed())] 181 | } 182 | } 183 | 184 | let cx = Context::create(modules![MyModule]); 185 | assert_eq!(cx.provider_registry().len(), 1); 186 | } 187 | 188 | #[tokio::test] 189 | async fn multiple_module_async() { 190 | struct MyModule1; 191 | 192 | impl Module for MyModule1 { 193 | fn providers() -> Vec { 194 | providers![singleton_async(|_| async { ComponentA }.boxed())] 195 | } 196 | } 197 | 198 | struct MyModule2; 199 | 200 | impl Module for MyModule2 { 201 | fn providers() -> Vec { 202 | providers![singleton_async(|cx| async { 203 | ComponentB { 204 | a: cx.resolve_async().await, 205 | } 206 | } 207 | .boxed())] 208 | } 209 | } 210 | 211 | let cx = Context::create(modules![MyModule1, MyModule2]); 212 | assert_eq!(cx.provider_registry().len(), 2); 213 | } 214 | 215 | #[tokio::test] 216 | async fn nested_module_async() { 217 | struct MyModule1; 218 | 219 | impl Module for MyModule1 { 220 | fn providers() -> Vec { 221 | providers![singleton_async(|_| async { ComponentA }.boxed())] 222 | } 223 | } 224 | 225 | struct MyModule2; 226 | 227 | impl Module for MyModule2 { 228 | fn submodules() -> Option> { 229 | Some(modules![MyModule1]) 230 | } 231 | 232 | fn providers() -> Vec { 233 | providers![singleton_async(|cx| async { 234 | ComponentB { 235 | a: cx.resolve_async().await, 236 | } 237 | } 238 | .boxed())] 239 | } 240 | } 241 | 242 | let cx = Context::create(modules![MyModule2]); 243 | assert_eq!(cx.provider_registry().len(), 2); 244 | } 245 | 246 | #[tokio::test] 247 | async fn duplicate_nested_module_async() { 248 | struct DataModule; 249 | 250 | impl Module for DataModule { 251 | fn providers() -> Vec { 252 | providers![singleton_async(|_| async { ComponentA }.boxed())] 253 | } 254 | } 255 | 256 | struct DomainModule; 257 | 258 | impl Module for DomainModule { 259 | fn providers() -> Vec { 260 | providers![singleton_async(|cx| async { 261 | ComponentB { 262 | a: cx.resolve_async().await, 263 | } 264 | } 265 | .boxed())] 266 | } 267 | } 268 | 269 | struct FeatureModule1; 270 | 271 | impl Module for FeatureModule1 { 272 | fn submodules() -> Option> { 273 | Some(modules![DomainModule, DataModule]) 274 | } 275 | 276 | fn providers() -> Vec { 277 | components![] 278 | } 279 | } 280 | 281 | struct FeatureModule2; 282 | 283 | impl Module for FeatureModule2 { 284 | fn submodules() -> Option> { 285 | Some(modules![DomainModule, DataModule]) 286 | } 287 | 288 | fn providers() -> Vec { 289 | components![] 290 | } 291 | } 292 | 293 | let cx = Context::create(modules![FeatureModule1, FeatureModule2]); 294 | assert_eq!(cx.provider_registry().len(), 2); 295 | } 296 | -------------------------------------------------------------------------------- /rudi/tests/feat_override_and_eager_create.rs: -------------------------------------------------------------------------------- 1 | use std::{cell::RefCell, rc::Rc}; 2 | 3 | use rudi::{components, modules, Context, DynProvider, Module, Singleton}; 4 | 5 | thread_local! { 6 | static COUNT: RefCell = const { RefCell::new(0) }; 7 | static NAME: RefCell<&'static str> = const { RefCell::new("") }; 8 | } 9 | 10 | trait Trait {} 11 | 12 | #[derive(Clone)] 13 | struct A; 14 | 15 | impl Trait for A {} 16 | 17 | #[Singleton(eager_create)] 18 | fn NewA() -> Rc { 19 | COUNT.with_borrow_mut(|c| { 20 | *c += 1; 21 | }); 22 | 23 | NAME.set("A"); 24 | 25 | Rc::new(A) 26 | } 27 | 28 | #[derive(Clone)] 29 | struct B; 30 | 31 | impl Trait for B {} 32 | 33 | #[Singleton(eager_create)] 34 | fn NewB() -> Rc { 35 | COUNT.with_borrow_mut(|c| { 36 | *c += 1; 37 | }); 38 | 39 | NAME.set("B"); 40 | 41 | Rc::new(B) 42 | } 43 | 44 | #[test] 45 | fn test() { 46 | struct MyModule1; 47 | 48 | impl Module for MyModule1 { 49 | fn providers() -> Vec { 50 | components![NewA] 51 | } 52 | } 53 | 54 | struct MyModule2; 55 | 56 | impl Module for MyModule2 { 57 | fn providers() -> Vec { 58 | components![NewB] 59 | } 60 | } 61 | 62 | Context::create(modules![MyModule1, MyModule2]); 63 | 64 | assert!(COUNT.with_borrow(|c| *c == 1)); 65 | assert!(NAME.with_borrow(|n| *n == "B")); 66 | } 67 | -------------------------------------------------------------------------------- /rudi/tests/macro_field_or_argument_attr.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Context, Singleton, Transient}; 2 | 3 | // name 4 | 5 | #[Singleton] 6 | fn One() -> i8 { 7 | 1 8 | } 9 | 10 | #[Singleton(name = "2")] 11 | fn Two() -> i8 { 12 | 2 13 | } 14 | 15 | #[Transient] 16 | struct A(i8); 17 | 18 | #[Transient] 19 | struct B(#[di(name = "2")] i8); 20 | 21 | // option 22 | 23 | #[Singleton] 24 | fn Three() -> Option { 25 | Some(3) 26 | } 27 | 28 | #[Singleton] 29 | fn Four() -> i16 { 30 | 4 31 | } 32 | 33 | #[Transient] 34 | struct C(Option); 35 | 36 | #[Transient] 37 | struct D(#[di(option)] Option); 38 | 39 | // default 40 | 41 | #[Transient] 42 | struct E(#[di(default)] i32); 43 | 44 | #[Transient] 45 | struct F(#[di(default = 42)] i32); 46 | 47 | // vec 48 | 49 | #[Singleton] 50 | fn Five() -> Vec { 51 | vec![5] 52 | } 53 | 54 | #[Singleton(eager_create)] 55 | fn Six() -> i64 { 56 | 6 57 | } 58 | 59 | #[Transient] 60 | struct G(Vec); 61 | 62 | #[Transient] 63 | struct H(#[di(vec)] Vec); 64 | 65 | #[Singleton] 66 | fn Run(a: A, b: B, c: C, d: D, e: E, f: F, g: G, h: H) { 67 | assert_eq!(a.0, 1); 68 | assert_eq!(b.0, 2); 69 | 70 | assert_eq!(c.0.unwrap(), 3); 71 | assert_eq!(d.0.unwrap(), 4); 72 | 73 | assert_eq!(e.0, 0); 74 | assert_eq!(f.0, 42); 75 | 76 | assert_eq!(g.0[0], 5); 77 | assert_eq!(h.0[0], 6); 78 | } 79 | 80 | #[Singleton(name = "ref")] 81 | fn Run2( 82 | #[di(ref)] one: &i8, 83 | #[di(ref, name = "2")] two: &i8, 84 | 85 | #[di(ref)] three: &Option, 86 | #[di(ref, option)] four: Option<&i16>, 87 | 88 | #[di(ref, default = &0)] zero: &i32, 89 | #[di(ref, default = &42)] forty_two: &i32, 90 | 91 | #[di(ref)] five: &Vec, 92 | #[di(ref, vec)] six: Vec<&i64>, 93 | ) { 94 | assert_eq!(one, &1); 95 | assert_eq!(two, &2); 96 | 97 | assert_eq!(three, &Some(3)); 98 | assert_eq!(four, Some(&4)); 99 | 100 | assert_eq!(zero, &0); 101 | assert_eq!(forty_two, &42); 102 | 103 | assert_eq!(five, &vec![5]); 104 | assert_eq!(six, vec![&6]); 105 | } 106 | 107 | mod alias { 108 | use rudi::Singleton; 109 | 110 | type OneAndTwo<'a> = &'a i8; 111 | 112 | type Three<'a> = &'a Option; 113 | type Four<'a> = Option<&'a i16>; 114 | 115 | type ZeroAndFortyTwo<'a> = &'a i32; 116 | 117 | type Five<'a> = &'a Vec; 118 | type Six<'a> = Vec<&'a i64>; 119 | 120 | #[Singleton(name = "ref alias")] 121 | fn Run3( 122 | #[di(ref = i8)] one: OneAndTwo<'_>, 123 | #[di(ref = i8, name = "2")] two: OneAndTwo<'_>, 124 | 125 | #[di(ref = Option)] three: Three<'_>, 126 | #[di(ref = i16, option)] four: Four<'_>, 127 | 128 | #[di(ref = i32, default = &0)] zero: ZeroAndFortyTwo<'_>, 129 | #[di(ref = i32, default = &42)] forty_two: ZeroAndFortyTwo<'_>, 130 | 131 | #[di(ref = Vec)] five: Five<'_>, 132 | #[di(ref = i64, vec)] six: Six<'_>, 133 | ) { 134 | assert_eq!(one, &1); 135 | assert_eq!(two, &2); 136 | 137 | assert_eq!(three, &Some(3)); 138 | assert_eq!(four, Some(&4)); 139 | 140 | assert_eq!(zero, &0); 141 | assert_eq!(forty_two, &42); 142 | 143 | assert_eq!(five, &vec![5]); 144 | assert_eq!(six, vec![&6]); 145 | } 146 | } 147 | 148 | #[test] 149 | fn field_or_argument_attr() { 150 | let mut cx = Context::auto_register(); 151 | cx.resolve::<()>(); 152 | cx.resolve_with_name::<()>("ref"); 153 | cx.resolve_with_name::<()>("ref alias"); 154 | } 155 | -------------------------------------------------------------------------------- /rudi/tests/macro_struct_or_function_attr.rs: -------------------------------------------------------------------------------- 1 | use std::{fmt::Debug, rc::Rc}; 2 | 3 | use rudi::{Context, Singleton, Transient}; 4 | 5 | // name 6 | 7 | #[Transient] 8 | fn One() -> i8 { 9 | 1 10 | } 11 | 12 | #[Transient(name = "2")] 13 | fn Two() -> i8 { 14 | 2 15 | } 16 | 17 | // eager_create 18 | 19 | #[Singleton(name = "3")] 20 | fn Three() -> i16 { 21 | 3 22 | } 23 | 24 | #[Singleton(name = "4", eager_create)] 25 | fn Four() -> i16 { 26 | 4 27 | } 28 | 29 | // condition 30 | 31 | fn _5_condition(cx: &Context) -> bool { 32 | !cx.contains_single_with_name::("5") 33 | } 34 | 35 | #[Singleton(name = "5", condition = _5_condition)] 36 | fn Five() -> i32 { 37 | 5 38 | } 39 | 40 | #[Singleton(name = "6", condition = |_cx| false)] 41 | fn Six() -> i32 { 42 | 6 43 | } 44 | 45 | // binds 46 | 47 | fn transform(t: T) -> Rc { 48 | Rc::new(t) 49 | } 50 | 51 | #[Singleton(name = "7")] 52 | fn Seven() -> i64 { 53 | 7 54 | } 55 | 56 | #[Singleton(name = "8", binds = [transform])] 57 | fn Eight() -> i64 { 58 | 8 59 | } 60 | 61 | // auto_register 62 | 63 | #[Singleton(name = "9")] 64 | fn Nine() -> i128 { 65 | 9 66 | } 67 | 68 | #[Singleton(name = "10", auto_register = false)] 69 | fn Ten() -> i128 { 70 | 10 71 | } 72 | 73 | // async 74 | 75 | #[Transient] 76 | struct A; 77 | 78 | #[Transient(async)] 79 | struct B; 80 | 81 | // rudi_path 82 | 83 | mod a { 84 | pub(crate) use rudi::*; 85 | } 86 | 87 | #[Transient] 88 | #[di(rudi_path = rudi)] 89 | struct C; 90 | 91 | #[Transient] 92 | #[di(rudi_path = a)] 93 | struct D; 94 | 95 | // `#[di]` used on `variant` of enum 96 | 97 | #[allow(dead_code)] 98 | #[derive(PartialEq, Eq, Debug)] 99 | #[Transient] 100 | enum EF { 101 | #[di] 102 | E, 103 | F, 104 | } 105 | 106 | #[tokio::test] 107 | async fn struct_or_function_attr() { 108 | let mut cx = Context::auto_register(); 109 | 110 | assert_eq!(cx.resolve::(), 1); 111 | assert_eq!(cx.resolve_with_name::("2"), 2); 112 | 113 | assert!(!cx.contains_single_with_name::("3")); 114 | assert!(cx.contains_single_with_name::("4")); 115 | 116 | assert!(cx.contains_provider_with_name::("5")); 117 | assert!(!cx.contains_provider_with_name::("6")); 118 | 119 | assert!(cx.resolve_option_with_name::>("7").is_none()); 120 | assert!(cx.resolve_option_with_name::>("8").is_some()); 121 | 122 | assert!(cx.get_provider_with_name::("9").is_some()); 123 | assert!(cx.get_provider_with_name::("10").is_none()); 124 | 125 | assert!(cx.resolve_option::().is_some()); 126 | assert!(cx.resolve_option_async::().await.is_some()); 127 | 128 | assert!(cx.resolve_option::().is_some()); 129 | assert!(cx.resolve_option::().is_some()); 130 | 131 | assert_eq!(cx.resolve::(), EF::E); 132 | } 133 | -------------------------------------------------------------------------------- /rudi/tests/module_resolve.rs: -------------------------------------------------------------------------------- 1 | use std::any::{self, TypeId}; 2 | 3 | use rudi::{DynProvider, Module, ResolveModule}; 4 | 5 | struct MyModule; 6 | 7 | impl Module for MyModule { 8 | fn providers() -> Vec { 9 | vec![] 10 | } 11 | } 12 | 13 | #[test] 14 | fn resolve_module() { 15 | let m = ResolveModule::new::(); 16 | assert!(m.ty().id == TypeId::of::()); 17 | assert!(m.ty().name == any::type_name::()); 18 | } 19 | -------------------------------------------------------------------------------- /rudi/tests/provider_binding.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::{any::TypeId, rc::Rc}; 4 | 5 | use rudi::{DefaultProvider, Definition, DynProvider}; 6 | 7 | use crate::components::{Component1, Trait1, Trait2}; 8 | 9 | #[test] 10 | fn binding_definitions() { 11 | let provider = Component1::provider(); 12 | test_binding_definitions(provider.binding_definitions()); 13 | test_binding_definitions(DynProvider::from(provider).binding_definitions()); 14 | } 15 | 16 | fn test_binding_definitions(definitions: Option<&Vec>) { 17 | assert!(definitions.is_some()); 18 | 19 | let definitions = definitions.unwrap(); 20 | assert_eq!(definitions.len(), 2); 21 | 22 | let trait1 = &definitions[0]; 23 | assert_eq!(trait1.key.ty.id, TypeId::of::>()); 24 | assert!(trait1.origin.is_some()); 25 | assert_eq!( 26 | trait1.origin.as_ref().unwrap().id, 27 | TypeId::of::() 28 | ); 29 | 30 | let trait2 = &definitions[1]; 31 | assert_eq!(trait2.key.ty.id, TypeId::of::>()); 32 | assert!(trait2.origin.is_some()); 33 | assert_eq!( 34 | trait2.origin.as_ref().unwrap().id, 35 | TypeId::of::() 36 | ); 37 | } 38 | -------------------------------------------------------------------------------- /rudi/tests/provider_override.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::{any::TypeId, rc::Rc}; 4 | 5 | use rudi::{ 6 | modules, providers, singleton, singleton_async, Context, DynProvider, FutureExt, Module, 7 | }; 8 | 9 | use crate::components::{Component1, Component2, Trait1}; 10 | 11 | #[test] 12 | fn allow_override_by_type() { 13 | struct Module1; 14 | impl Module for Module1 { 15 | fn providers() -> Vec { 16 | providers![singleton(|_| Component2).bind(Component2::into_trait1)] 17 | } 18 | } 19 | 20 | struct Module2; 21 | impl Module for Module2 { 22 | fn providers() -> Vec { 23 | providers![singleton(|_| Component1).bind(Component1::into_trait1)] 24 | } 25 | } 26 | 27 | let mut cx = Context::create(modules![Module1, Module2]); 28 | // Component1, Component2, Rc 29 | assert_eq!(cx.provider_registry().len(), 3); 30 | 31 | let instance = cx.resolve::>(); 32 | assert!(instance.as_any().is::()); 33 | 34 | let provider = cx.get_provider::>(); 35 | assert!(provider.is_some()); 36 | assert!( 37 | provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 38 | ); 39 | } 40 | 41 | #[test] 42 | fn allow_override_by_name() { 43 | struct Module1; 44 | impl Module for Module1 { 45 | fn providers() -> Vec { 46 | providers![singleton(|_| Component2) 47 | .name("hello") 48 | .bind(Component2::into_trait1)] 49 | } 50 | } 51 | 52 | struct Module2; 53 | impl Module for Module2 { 54 | fn providers() -> Vec { 55 | providers![singleton(|_| Component1) 56 | .name("hello") 57 | .bind(Component1::into_trait1)] 58 | } 59 | } 60 | 61 | let mut cx = Context::create(modules![Module1, Module2]); 62 | // Component1, Component2, Rc 63 | assert_eq!(cx.provider_registry().len(), 3); 64 | 65 | let instance = cx.resolve_with_name::>("hello"); 66 | assert!(instance.as_any().is::()); 67 | let provider = cx.get_provider_with_name::>("hello"); 68 | assert!(provider.is_some()); 69 | assert!( 70 | provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 71 | ); 72 | } 73 | 74 | #[tokio::test] 75 | async fn allow_override_by_type_async() { 76 | struct Module1; 77 | impl Module for Module1 { 78 | fn providers() -> Vec { 79 | providers![ 80 | singleton_async(|_| async { Component2 }.boxed()).bind(Component2::into_trait1) 81 | ] 82 | } 83 | } 84 | 85 | struct Module2; 86 | impl Module for Module2 { 87 | fn providers() -> Vec { 88 | providers![ 89 | singleton_async(|_| async { Component1 }.boxed()).bind(Component1::into_trait1) 90 | ] 91 | } 92 | } 93 | 94 | let mut cx = Context::create(modules![Module1, Module2]); 95 | // Component1, Component2, Rc 96 | assert_eq!(cx.provider_registry().len(), 3); 97 | 98 | let instance = cx.resolve_async::>().await; 99 | assert!(instance.as_any().is::()); 100 | 101 | let provider = cx.get_provider::>(); 102 | assert!(provider.is_some()); 103 | assert!( 104 | provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 105 | ); 106 | } 107 | 108 | #[tokio::test] 109 | async fn allow_override_by_name_async() { 110 | struct Module1; 111 | impl Module for Module1 { 112 | fn providers() -> Vec { 113 | providers![singleton_async(|_| async { Component2 }.boxed()) 114 | .name("hello") 115 | .bind(Component2::into_trait1)] 116 | } 117 | } 118 | 119 | struct Module2; 120 | impl Module for Module2 { 121 | fn providers() -> Vec { 122 | providers![singleton_async(|_| async { Component1 }.boxed()) 123 | .name("hello") 124 | .bind(Component1::into_trait1)] 125 | } 126 | } 127 | 128 | let mut cx = Context::create(modules![Module1, Module2]); 129 | // Component1, Component2, Rc 130 | assert_eq!(cx.provider_registry().len(), 3); 131 | 132 | let instance = cx.resolve_with_name_async::>("hello").await; 133 | assert!(instance.as_any().is::()); 134 | let provider = cx.get_provider_with_name::>("hello"); 135 | assert!(provider.is_some()); 136 | assert!( 137 | provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 138 | ); 139 | } 140 | -------------------------------------------------------------------------------- /rudi/tests/provider_resolve.rs: -------------------------------------------------------------------------------- 1 | mod components; 2 | 3 | use std::{any::TypeId, rc::Rc}; 4 | 5 | use rudi::{ 6 | components, modules, providers, singleton, singleton_async, transient, transient_async, 7 | Context, DynProvider, FutureExt, Module, Transient, 8 | }; 9 | 10 | use crate::components::{Component1, Component2, ComponentA, Trait1}; 11 | 12 | #[test] 13 | fn resolve_singleton() { 14 | struct MyModule; 15 | impl Module for MyModule { 16 | fn providers() -> Vec { 17 | providers![singleton(|_| Rc::new(ComponentA))] 18 | } 19 | } 20 | 21 | let mut cx = Context::create(modules![MyModule]); 22 | 23 | let a = cx.resolve::>(); 24 | let b = cx.resolve::>(); 25 | 26 | assert!(std::ptr::eq(&*a, &*b)); 27 | } 28 | 29 | #[test] 30 | fn resolve_transient() { 31 | struct MyModule; 32 | impl Module for MyModule { 33 | fn providers() -> Vec { 34 | providers![transient(|_| Rc::new(ComponentA))] 35 | } 36 | } 37 | 38 | let mut cx = Context::create(modules![MyModule]); 39 | 40 | let a = cx.resolve::>(); 41 | let b = cx.resolve::>(); 42 | 43 | assert!(!std::ptr::eq(&*a, &*b)); 44 | } 45 | 46 | #[test] 47 | fn resolve_singleton_by_name() { 48 | struct MyModule; 49 | impl Module for MyModule { 50 | fn providers() -> Vec { 51 | let a = Rc::new(ComponentA); 52 | providers![ 53 | singleton({ 54 | let a = a.clone(); 55 | move |_| a.clone() 56 | }) 57 | .name("A"), 58 | singleton(move |_| a.clone()).name("B"), 59 | ] 60 | } 61 | } 62 | 63 | let mut cx = Context::create(modules![MyModule]); 64 | 65 | let a = cx.resolve_with_name::>("A"); 66 | let b = cx.resolve_with_name::>("B"); 67 | 68 | assert!(std::ptr::eq(&*a, &*b)); 69 | } 70 | 71 | #[test] 72 | fn resolve_transient_by_name() { 73 | struct MyModule; 74 | impl Module for MyModule { 75 | fn providers() -> Vec { 76 | let a = Rc::new(ComponentA); 77 | providers![ 78 | transient({ 79 | let a = a.clone(); 80 | move |_| a.clone() 81 | }) 82 | .name("A"), 83 | transient(move |_| a.clone()).name("B"), 84 | ] 85 | } 86 | } 87 | 88 | let mut cx = Context::create(modules![MyModule]); 89 | 90 | let a = cx.resolve_with_name::>("A"); 91 | let b = cx.resolve_with_name::>("B"); 92 | 93 | assert!(std::ptr::eq(&*a, &*b)); 94 | } 95 | 96 | #[test] 97 | fn resolve_trait_object() { 98 | struct MyModule; 99 | impl Module for MyModule { 100 | fn providers() -> Vec { 101 | providers![ 102 | singleton(|_| Component1) 103 | .name("1") 104 | .bind(Component1::into_trait1), 105 | singleton(|_| Component2) 106 | .name("2") 107 | .bind(Component2::into_trait1) 108 | ] 109 | } 110 | } 111 | 112 | let cx = Context::create(modules![MyModule]); 113 | 114 | let providers = cx.get_providers_by_type::>(); 115 | 116 | assert!(providers.len() == 2); 117 | 118 | assert!(providers.iter().any(|provider| { 119 | provider.definition().origin.as_ref().unwrap().id == TypeId::of::() 120 | })); 121 | 122 | assert!(providers.iter().any(|provider| { 123 | provider.definition().origin.as_ref().unwrap().id == TypeId::of::() 124 | })); 125 | } 126 | 127 | #[test] 128 | fn resolve_default_name_and_custom_name() { 129 | struct MyModule; 130 | impl Module for MyModule { 131 | fn providers() -> Vec { 132 | providers![ 133 | singleton(|_| Component1).bind(Component1::into_trait1), 134 | singleton(|_| Component2) 135 | .name("2") 136 | .bind(Component2::into_trait1), 137 | ] 138 | } 139 | } 140 | 141 | let mut cx = Context::create(modules![MyModule]); 142 | 143 | let a = cx.resolve::>(); 144 | let b = cx.resolve_with_name::>("2"); 145 | 146 | let a_provider = cx.get_provider::>(); 147 | let b_provider = cx.get_provider_with_name::>("2"); 148 | 149 | assert!(a_provider.is_some()); 150 | assert!( 151 | a_provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 152 | ); 153 | 154 | assert!(b_provider.is_some()); 155 | assert!( 156 | b_provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 157 | ); 158 | 159 | assert!(a.as_any().is::()); 160 | assert!(b.as_any().is::()); 161 | } 162 | 163 | #[test] 164 | #[should_panic] 165 | fn resolve_async_instance_in_sync_context() { 166 | #[Transient(async)] 167 | struct A; 168 | 169 | struct MyModule; 170 | impl Module for MyModule { 171 | fn providers() -> Vec { 172 | components![A] 173 | } 174 | } 175 | 176 | let mut cx = Context::create(modules![MyModule]); 177 | 178 | cx.resolve::(); 179 | } 180 | 181 | #[test] 182 | fn resolve_instances_by_type() { 183 | struct MyModule; 184 | impl Module for MyModule { 185 | fn providers() -> Vec { 186 | providers![ 187 | transient(|_| ComponentA).name("A"), 188 | transient(|_| ComponentA).name("B"), 189 | ] 190 | } 191 | } 192 | 193 | let mut cx = Context::create(modules![MyModule]); 194 | 195 | assert!(cx.resolve_by_type::().len() == 2); 196 | } 197 | 198 | #[tokio::test] 199 | async fn resolve_singleton_async() { 200 | struct MyModule; 201 | impl Module for MyModule { 202 | fn providers() -> Vec { 203 | providers![singleton_async(|_| async { Rc::new(ComponentA) }.boxed())] 204 | } 205 | } 206 | 207 | let mut cx = Context::create(modules![MyModule]); 208 | 209 | let a = cx.resolve_async::>().await; 210 | let b = cx.resolve_async::>().await; 211 | 212 | assert!(std::ptr::eq(&*a, &*b)); 213 | } 214 | 215 | #[tokio::test] 216 | async fn resolve_transient_async() { 217 | struct MyModule; 218 | impl Module for MyModule { 219 | fn providers() -> Vec { 220 | providers![transient_async(|_| async { Rc::new(ComponentA) }.boxed())] 221 | } 222 | } 223 | 224 | let mut cx = Context::create(modules![MyModule]); 225 | 226 | let a = cx.resolve_async::>().await; 227 | let b = cx.resolve_async::>().await; 228 | 229 | assert!(!std::ptr::eq(&*a, &*b)); 230 | } 231 | 232 | #[tokio::test] 233 | async fn resolve_singleton_by_name_async() { 234 | thread_local! { 235 | static A: Rc = Rc::new(ComponentA); 236 | } 237 | 238 | struct MyModule; 239 | impl Module for MyModule { 240 | fn providers() -> Vec { 241 | providers![ 242 | singleton_async(move |_| async { A.with(|a| a.clone()) }.boxed()).name("A"), 243 | singleton_async(move |_| async { A.with(|a| a.clone()) }.boxed()).name("B"), 244 | ] 245 | } 246 | } 247 | 248 | let mut cx = Context::create(modules![MyModule]); 249 | 250 | let a = cx.resolve_with_name_async::>("A").await; 251 | let b = cx.resolve_with_name_async::>("B").await; 252 | 253 | assert!(std::ptr::eq(&*a, &*b)); 254 | } 255 | 256 | #[tokio::test] 257 | async fn resolve_transient_by_name_async() { 258 | thread_local! { 259 | static A: Rc = Rc::new(ComponentA); 260 | } 261 | 262 | struct MyModule; 263 | impl Module for MyModule { 264 | fn providers() -> Vec { 265 | providers![ 266 | transient_async(move |_| async { A.with(|a| a.clone()) }.boxed()).name("A"), 267 | transient_async(move |_| async { A.with(|a| a.clone()) }.boxed()).name("B"), 268 | ] 269 | } 270 | } 271 | 272 | let mut cx = Context::create(modules![MyModule]); 273 | 274 | let a = cx.resolve_with_name_async::>("A").await; 275 | let b = cx.resolve_with_name_async::>("B").await; 276 | 277 | assert!(std::ptr::eq(&*a, &*b)); 278 | } 279 | 280 | #[tokio::test] 281 | async fn resolve_trait_object_async() { 282 | struct MyModule; 283 | impl Module for MyModule { 284 | fn providers() -> Vec { 285 | providers![ 286 | singleton_async(|_| async { Component1 }.boxed()) 287 | .name("1") 288 | .bind(Component1::into_trait1), 289 | singleton_async(|_| async { Component2 }.boxed()) 290 | .name("2") 291 | .bind(Component2::into_trait1) 292 | ] 293 | } 294 | } 295 | 296 | let cx = Context::create(modules![MyModule]); 297 | 298 | let providers = cx.get_providers_by_type::>(); 299 | 300 | assert!(providers.len() == 2); 301 | 302 | assert!(providers.iter().any(|provider| { 303 | provider.definition().origin.as_ref().unwrap().id == TypeId::of::() 304 | })); 305 | 306 | assert!(providers.iter().any(|provider| { 307 | provider.definition().origin.as_ref().unwrap().id == TypeId::of::() 308 | })); 309 | } 310 | 311 | #[tokio::test] 312 | async fn resolve_default_name_and_custom_name_async() { 313 | struct MyModule; 314 | impl Module for MyModule { 315 | fn providers() -> Vec { 316 | providers![ 317 | singleton_async(|_| async { Component1 }.boxed()).bind(Component1::into_trait1), 318 | singleton_async(|_| async { Component2 }.boxed()) 319 | .name("2") 320 | .bind(Component2::into_trait1), 321 | ] 322 | } 323 | } 324 | 325 | let mut cx = Context::create(modules![MyModule]); 326 | 327 | let a = cx.resolve_async::>().await; 328 | let b = cx.resolve_with_name_async::>("2").await; 329 | 330 | let a_provider = cx.get_provider::>(); 331 | let b_provider = cx.get_provider_with_name::>("2"); 332 | 333 | assert!(a_provider.is_some()); 334 | assert!( 335 | a_provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 336 | ); 337 | 338 | assert!(b_provider.is_some()); 339 | assert!( 340 | b_provider.unwrap().definition().origin.as_ref().unwrap().id == TypeId::of::() 341 | ); 342 | 343 | assert!(a.as_any().is::()); 344 | assert!(b.as_any().is::()); 345 | } 346 | 347 | #[tokio::test] 348 | async fn resolve_sync_instance_in_async_context() { 349 | #[Transient] 350 | struct A; 351 | 352 | struct MyModule; 353 | impl Module for MyModule { 354 | fn providers() -> Vec { 355 | components![A] 356 | } 357 | } 358 | 359 | let mut cx = Context::create(modules![MyModule]); 360 | 361 | cx.resolve_async::().await; 362 | } 363 | 364 | #[tokio::test] 365 | async fn resolve_instances_by_type_async() { 366 | struct MyModule; 367 | impl Module for MyModule { 368 | fn providers() -> Vec { 369 | providers![ 370 | transient_async(|_| async { ComponentA }.boxed()).name("A"), 371 | transient_async(|_| async { ComponentA }.boxed()).name("B"), 372 | ] 373 | } 374 | } 375 | 376 | let mut cx = Context::create(modules![MyModule]); 377 | 378 | assert!(cx.resolve_by_type_async::().await.len() == 2); 379 | } 380 | -------------------------------------------------------------------------------- /rudi/tests/scope_behaviour.rs: -------------------------------------------------------------------------------- 1 | use rudi::{Context, SingleOwner, Singleton, Transient}; 2 | 3 | #[Transient(name = "one")] 4 | fn One() -> i32 { 5 | 1 6 | } 7 | 8 | #[Singleton(name = "two")] 9 | fn Two() -> i32 { 10 | 2 11 | } 12 | 13 | #[SingleOwner(name = "three")] 14 | fn Three() -> i32 { 15 | 3 16 | } 17 | 18 | #[test] 19 | fn transient_owned() { 20 | let mut cx = Context::auto_register(); 21 | assert_eq!(cx.resolve_with_name::("one"), 1); 22 | } 23 | 24 | #[test] 25 | #[should_panic] 26 | fn transient_ref() { 27 | let mut cx = Context::auto_register(); 28 | assert!(cx.try_just_create_single_with_name::("one")); 29 | assert_eq!(cx.get_single_with_name::("one"), &1); 30 | } 31 | 32 | #[test] 33 | fn singleton_owned() { 34 | let mut cx = Context::auto_register(); 35 | assert_eq!(cx.resolve_with_name::("two"), 2); 36 | } 37 | 38 | #[test] 39 | fn singleton_ref() { 40 | let mut cx = Context::auto_register(); 41 | assert!(cx.try_just_create_single_with_name::("two")); 42 | assert_eq!(cx.get_single_with_name::("two"), &2); 43 | } 44 | 45 | #[test] 46 | #[should_panic] 47 | fn single_owner_owned() { 48 | let mut cx = Context::auto_register(); 49 | assert_eq!(cx.resolve_with_name::("three"), 3); 50 | } 51 | 52 | #[test] 53 | fn single_owner_ref() { 54 | let mut cx = Context::auto_register(); 55 | assert!(cx.try_just_create_single_with_name::("three")); 56 | assert_eq!(cx.get_single_with_name::("three"), &3); 57 | } 58 | --------------------------------------------------------------------------------