├── .github └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── macros ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT └── src │ ├── args.rs │ ├── gen_caster.rs │ ├── item_impl.rs │ ├── item_type.rs │ └── lib.rs ├── src ├── cast.rs ├── cast │ ├── cast_arc.rs │ ├── cast_box.rs │ ├── cast_mut.rs │ ├── cast_rc.rs │ └── cast_ref.rs ├── hasher.rs └── lib.rs └── tests ├── castable_to.rs ├── on-enum.rs ├── on-struct.rs ├── on-trait-impl-assoc-type1.rs ├── on-trait-impl-assoc-type2.rs ├── on-trait-impl-assoc-type3.rs ├── on-trait-impl.rs ├── on-type-multi-traits.rs ├── run.rs └── ui ├── duplicate-flags.rs ├── duplicate-flags.stderr ├── on-generic-type.rs ├── on-generic-type.stderr ├── on-type-impl.rs ├── on-type-impl.stderr ├── unknown-flag.rs └── unknown-flag.stderr /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: ci 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | test: 11 | name: Build and run tests 12 | strategy: 13 | matrix: 14 | rust-version: [stable, 1.40.0] 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v2 18 | - name: Install Rust 19 | uses: actions-rs/toolchain@v1 20 | with: 21 | toolchain: ${{ matrix.rust-version }} 22 | profile: minimal 23 | override: true 24 | - name: Build 25 | run: cargo build --verbose 26 | - name: Run tests 27 | run: cargo test --verbose 28 | check-style: 29 | name: Check source code style 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v2 33 | - name: Install Rust 34 | uses: actions-rs/toolchain@v1 35 | with: 36 | toolchain: stable 37 | override: true 38 | - name: Check formatting 39 | run: cargo fmt -- --check 40 | - name: Run linter 41 | run: cargo clippy --all --all-targets 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Cargo lock in subs 2 | **/Cargo.lock 3 | 4 | # Generated by Cargo 5 | **/target/ 6 | 7 | **/*.rs.bk 8 | **/*.iml 9 | .idea/ 10 | .vscode/ 11 | /db/ 12 | /snapshot/ 13 | /log/ 14 | /keys/ 15 | /test/log/ 16 | 17 | # macOS 18 | .DS_store 19 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intertrait" 3 | version = "0.2.2" 4 | authors = ["CodeChain Team "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Allow for inter-trait casting" 7 | edition = "2018" 8 | repository = "https://github.com/CodeChain-io/intertrait" 9 | documentation = "https://docs.rs/intertrait" 10 | readme = "README.md" 11 | categories = ["rust-patterns"] 12 | keywords = ["trait", "cast", "any"] 13 | include = ["src/**/*", "Cargo.toml", "LICENSE-*", "README.md"] 14 | 15 | [dependencies] 16 | once_cell = "1.4" 17 | linkme = "0.2" 18 | intertrait-macros = { version = "=0.2.2", path = "macros" } 19 | 20 | [dev-dependencies] 21 | trybuild = "1.0" 22 | doc-comment = "0.3" 23 | 24 | [workspace] 25 | members = ["macros"] 26 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intertrait 2 | 3 | ![Build Status](https://github.com/CodeChain-io/intertrait/workflows/ci/badge.svg) 4 | [![Latest Version](https://img.shields.io/crates/v/intertrait.svg)](https://crates.io/crates/intertrait) 5 | [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://docs.rs/intertrait) 6 | 7 | This library provides direct casting among trait objects implemented by a type. 8 | 9 | In Rust, a trait object for a sub-trait of [`std::any::Any`] can be downcast to a concrete type at runtime 10 | if the type is known. But no direct casting between two trait objects (i.e. without involving the concrete type 11 | of the backing value) is possible (even no coercion from a trait object for a trait to that for its super-trait yet). 12 | 13 | With this crate, any trait object for a sub-trait of [`CastFrom`] can be cast directly to a trait object 14 | for another trait implemented by the underlying type if the target traits are registered beforehand 15 | with the macros provided by this crate. 16 | 17 | # Dependencies 18 | Add the following two dependencies to your `Cargo.toml`: 19 | 20 | ```toml 21 | [dependencies] 22 | intertrait = "0.2" 23 | linkme = "0.2" 24 | ``` 25 | 26 | The `linkme` dependency is required due to the use of `linkme` macro in the output of `intertrait` macros. 27 | 28 | # Usage 29 | 30 | ```rust 31 | use intertrait::*; 32 | use intertrait::cast::*; 33 | 34 | struct Data; 35 | 36 | trait Source: CastFrom {} 37 | 38 | trait Greet { 39 | fn greet(&self); 40 | } 41 | 42 | #[cast_to] 43 | impl Greet for Data { 44 | fn greet(&self) { 45 | println!("Hello"); 46 | } 47 | } 48 | 49 | impl Source for Data {} 50 | 51 | fn main() { 52 | let data = Data; 53 | let source: &dyn Source = &data; 54 | let greet = source.cast::(); 55 | greet.unwrap().greet(); 56 | } 57 | ``` 58 | 59 | Target traits must be explicitly designated beforehand. There are three ways of doing it: 60 | 61 | ### `#[cast_to]` to `impl` item 62 | The trait implemented is designated as a target trait. 63 | 64 | ```rust 65 | use intertrait::*; 66 | 67 | struct Data; 68 | trait Greet { fn greet(&self); } 69 | 70 | #[cast_to] 71 | impl Greet for Data { 72 | fn greet(&self) { 73 | println!("Hello"); 74 | } 75 | } 76 | ``` 77 | 78 | ### `#[cast_to(Trait)]` to type definition 79 | For the type, the traits specified as arguments to the `#[cast_to(...)]` attribute are designated as target traits. 80 | 81 | ```rust 82 | use intertrait::*; 83 | 84 | trait Greet { fn greet(&self); } 85 | 86 | impl Greet for Data { 87 | fn greet(&self) { 88 | println!("Hello"); 89 | } 90 | } 91 | 92 | #[cast_to(Greet, std::fmt::Debug)] 93 | #[derive(std::fmt::Debug)] 94 | struct Data; 95 | ``` 96 | 97 | ### `castable_to!(Type => Trait1, Trait2)` 98 | For the type, the traits following `:` are designated as target traits. 99 | 100 | ```rust 101 | use intertrait::*; 102 | 103 | #[derive(std::fmt::Debug)] 104 | struct Data; 105 | trait Greet { fn greet(&self); } 106 | impl Greet for Data { 107 | fn greet(&self) { 108 | println!("Hello"); 109 | } 110 | } 111 | // Only in an item position due to the current limitation in the stable Rust. 112 | // https://github.com/rust-lang/rust/pull/68717 113 | castable_to!(Data => Greet, std::fmt::Debug); 114 | 115 | fn main() {} 116 | ``` 117 | 118 | ## `Arc` Support 119 | `std::sync::Arc` is unique in that it implements `downcast` method only on `dyn Any + Send + Sync + 'static'. 120 | To use with `Arc`, the following steps should be taken: 121 | 122 | * Mark source traits with [`CastFromSync`] instead of [`CastFrom`] 123 | * Add `[sync]` flag to `#[cast_to]` and `castable_to!` as follows: 124 | ```ignore 125 | #[cast_to([sync])] 126 | #[cast_to([sync] Trait1, Trait2)] 127 | castable_to!(Type => [sync] Trait, Trait2); 128 | ``` 129 | 130 | # How it works 131 | First of all, [`CastFrom`] trait makes it possible to retrieve an object of [`std::any::Any`] 132 | from an object for a sub-trait of [`CastFrom`]. 133 | 134 | And the macros provided by `intertrait` generates trampoline functions for downcasting a trait object 135 | for [`std::any::Any`] back to its concrete type and then creating a trait object for the target trait from it. 136 | 137 | Those trampoline functions are aggregated into a global registry 138 | using [`linkme`](https://github.com/dtolnay/linkme/) crate, which involves no (generally discouraged) 139 | life-before-main trick. The registry is keyed with a pair of [`TypeId`]s, which are those of the concrete type 140 | backing a trait object for a sub-trait of [`CastFrom`] and the target trait (the actual implementation 141 | is a bit different here, but conceptually so). 142 | 143 | In the course, it doesn't rely on any unstable Rust implementation details such as the layout of trait objects 144 | that may be changed in the future. 145 | 146 | # Credits 147 | `intertrait` has taken much of its core ideas from the great [`traitcast`](https://github.com/bch29/traitcast) crate. 148 | 149 | # License 150 | Licensed under either of 151 | 152 | * Apache License, Version 2.0 153 | ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) 154 | * MIT license 155 | ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) 156 | 157 | at your option. 158 | 159 | ## Contribution 160 | Unless you explicitly state otherwise, any contribution intentionally submitted 161 | for inclusion in the work by you, as defined in the Apache-2.0 license, shall be 162 | dual licensed as above, without any additional terms or conditions. 163 | 164 | [`std::any::Any`]: https://doc.rust-lang.org/std/any/trait.Any.html 165 | [`TypeId`]: https://doc.rust-lang.org/std/any/struct.TypeId.html 166 | [`CastFrom`]: https://docs.rs/intertrait/*/intertrait/trait.CastFrom.html 167 | [`CastFromSync`]: https://docs.rs/intertrait/*/intertrait/trait.CastFromSync.html -------------------------------------------------------------------------------- /macros/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "intertrait-macros" 3 | description = "Macros for intertrait crate, which allows for direct casting between trait objects" 4 | version = "0.2.2" 5 | authors = ["CodeChain Team "] 6 | license = "MIT OR Apache-2.0" 7 | edition = "2018" 8 | repository = "https://github.com/CodeChain-io/intertrait" 9 | include = ["src/**/*", "Cargo.toml", "LICENSE-*"] 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1.0" 16 | syn = { version = "1.0", features = ["full"] } 17 | quote = "1.0" 18 | uuid = { version = "0.8", features = ["v4"] } 19 | 20 | [dev-dependencies] 21 | intertrait = { version = "=0.2.2", path = ".." } 22 | linkme = "0.2" 23 | -------------------------------------------------------------------------------- /macros/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 | -------------------------------------------------------------------------------- /macros/LICENSE-MIT: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any person obtaining a copy 2 | of this software and associated documentation files (the "Software"), to deal 3 | in the Software without restriction, including without limitation the rights 4 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 5 | copies of the Software, and to permit persons to whom the Software is 6 | furnished to do so, subject to the following conditions: 7 | 8 | The above copyright notice and this permission notice shall be included in all 9 | copies or substantial portions of the Software. 10 | 11 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 12 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 13 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 14 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 15 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 16 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 17 | SOFTWARE. 18 | -------------------------------------------------------------------------------- /macros/src/args.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use syn::bracketed; 4 | use syn::parse::{Parse, ParseStream, Result}; 5 | use syn::punctuated::Punctuated; 6 | use syn::{Error, Ident, Path, Token, Type}; 7 | 8 | #[derive(Hash, PartialEq, Eq)] 9 | pub enum Flag { 10 | Sync, 11 | } 12 | 13 | impl Flag { 14 | fn from(ident: &Ident) -> Result { 15 | match ident.to_string().as_str() { 16 | "sync" => Ok(Flag::Sync), 17 | unknown => { 18 | let msg = format!("Unknown flag: {}", unknown); 19 | Err(Error::new_spanned(ident, msg)) 20 | } 21 | } 22 | } 23 | } 24 | 25 | pub struct Targets { 26 | pub flags: HashSet, 27 | pub paths: Vec, 28 | } 29 | 30 | impl Parse for Targets { 31 | fn parse(input: ParseStream) -> Result { 32 | let mut flags = HashSet::new(); 33 | let mut paths = Vec::new(); 34 | 35 | if input.is_empty() { 36 | return Ok(Targets { flags, paths }); 37 | } 38 | 39 | if input.peek(syn::token::Bracket) { 40 | let content; 41 | bracketed!(content in input); 42 | for ident in Punctuated::::parse_terminated(&content)? { 43 | if !flags.insert(Flag::from(&ident)?) { 44 | let msg = format!("Duplicated flag: {}", ident); 45 | return Err(Error::new_spanned(ident, msg)); 46 | } 47 | } 48 | } 49 | 50 | if input.is_empty() { 51 | return Ok(Targets { flags, paths }); 52 | } 53 | 54 | paths = Punctuated::::parse_terminated(input)? 55 | .into_iter() 56 | .collect(); 57 | 58 | Ok(Targets { flags, paths }) 59 | } 60 | } 61 | 62 | pub struct Casts { 63 | pub ty: Type, 64 | pub targets: Targets, 65 | } 66 | 67 | impl Parse for Casts { 68 | fn parse(input: ParseStream) -> Result { 69 | let ty: Type = input.parse()?; 70 | input.parse::]>()?; 71 | 72 | Ok(Casts { 73 | ty, 74 | targets: input.parse()?, 75 | }) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /macros/src/gen_caster.rs: -------------------------------------------------------------------------------- 1 | use std::str::from_utf8_unchecked; 2 | 3 | use proc_macro2::TokenStream; 4 | use uuid::adapter::Simple; 5 | use uuid::Uuid; 6 | 7 | use quote::format_ident; 8 | use quote::quote; 9 | use quote::ToTokens; 10 | 11 | pub fn generate_caster(ty: &impl ToTokens, trait_: &impl ToTokens, sync: bool) -> TokenStream { 12 | let mut fn_buf = [0u8; FN_BUF_LEN]; 13 | let fn_ident = format_ident!("{}", new_fn_name(&mut fn_buf)); 14 | let new_caster = if sync { 15 | quote! { 16 | ::intertrait::Caster::::new_sync( 17 | |from| from.downcast_ref::<#ty>().unwrap(), 18 | |from| from.downcast_mut::<#ty>().unwrap(), 19 | |from| from.downcast::<#ty>().unwrap(), 20 | |from| from.downcast::<#ty>().unwrap(), 21 | |from| from.downcast::<#ty>().unwrap() 22 | ) 23 | } 24 | } else { 25 | quote! { 26 | ::intertrait::Caster::::new( 27 | |from| from.downcast_ref::<#ty>().unwrap(), 28 | |from| from.downcast_mut::<#ty>().unwrap(), 29 | |from| from.downcast::<#ty>().unwrap(), 30 | |from| from.downcast::<#ty>().unwrap(), 31 | ) 32 | } 33 | }; 34 | 35 | quote! { 36 | #[::linkme::distributed_slice(::intertrait::CASTERS)] 37 | fn #fn_ident() -> (::std::any::TypeId, ::intertrait::BoxedCaster) { 38 | (::std::any::TypeId::of::<#ty>(), Box::new(#new_caster)) 39 | } 40 | } 41 | } 42 | 43 | const FN_PREFIX: &[u8] = b"__"; 44 | const FN_BUF_LEN: usize = FN_PREFIX.len() + Simple::LENGTH; 45 | 46 | fn new_fn_name(buf: &mut [u8]) -> &str { 47 | buf[..FN_PREFIX.len()].copy_from_slice(FN_PREFIX); 48 | Uuid::new_v4() 49 | .to_simple() 50 | .encode_lower(&mut buf[FN_PREFIX.len()..]); 51 | unsafe { from_utf8_unchecked(&buf[..FN_BUF_LEN]) } 52 | } 53 | -------------------------------------------------------------------------------- /macros/src/item_impl.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use proc_macro2::TokenStream; 4 | use quote::{quote, quote_spanned, ToTokens}; 5 | use syn::punctuated::Punctuated; 6 | use syn::spanned::Spanned; 7 | use syn::Token; 8 | use syn::{ 9 | AngleBracketedGenericArguments, Binding, GenericArgument, ImplItem, ItemImpl, Path, 10 | PathArguments, 11 | }; 12 | use PathArguments::AngleBracketed; 13 | 14 | use crate::args::Flag; 15 | use crate::gen_caster::generate_caster; 16 | 17 | pub fn process(flags: &HashSet, input: ItemImpl) -> TokenStream { 18 | let ItemImpl { 19 | ref self_ty, 20 | ref trait_, 21 | ref items, 22 | .. 23 | } = input; 24 | 25 | let generated = match trait_ { 26 | None => quote_spanned! { 27 | self_ty.span() => compile_error!("#[cast_to] should only be on an impl of a trait"); 28 | }, 29 | Some(trait_) => match trait_ { 30 | (Some(bang), _, _) => quote_spanned! { 31 | bang.span() => compile_error!("#[cast_to] is not for !Trait impl"); 32 | }, 33 | (None, path, _) => { 34 | let path = fully_bound_trait(path, items); 35 | generate_caster(self_ty, &path, flags.contains(&Flag::Sync)) 36 | } 37 | }, 38 | }; 39 | 40 | quote! { 41 | #input 42 | #generated 43 | } 44 | } 45 | 46 | fn fully_bound_trait(path: &Path, items: &[ImplItem]) -> impl ToTokens { 47 | let bindings = items 48 | .iter() 49 | .filter_map(|item| { 50 | if let ImplItem::Type(assoc_ty) = item { 51 | Some(GenericArgument::Binding(Binding { 52 | ident: assoc_ty.ident.to_owned(), 53 | eq_token: Default::default(), 54 | ty: assoc_ty.ty.to_owned(), 55 | })) 56 | } else { 57 | None 58 | } 59 | }) 60 | .collect::>(); 61 | 62 | let mut path = path.clone(); 63 | 64 | if bindings.is_empty() { 65 | return path; 66 | } 67 | 68 | if let Some(last) = path.segments.last_mut() { 69 | match &mut last.arguments { 70 | PathArguments::None => { 71 | last.arguments = AngleBracketed(AngleBracketedGenericArguments { 72 | colon2_token: None, 73 | lt_token: Default::default(), 74 | args: bindings, 75 | gt_token: Default::default(), 76 | }) 77 | } 78 | AngleBracketed(args) => args.args.extend(bindings), 79 | _ => {} 80 | } 81 | } 82 | path 83 | } 84 | -------------------------------------------------------------------------------- /macros/src/item_type.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashSet; 2 | 3 | use proc_macro2::TokenStream; 4 | use syn::spanned::Spanned; 5 | use syn::{DeriveInput, Path}; 6 | 7 | use quote::{quote, quote_spanned}; 8 | 9 | use crate::args::Flag; 10 | use crate::gen_caster::generate_caster; 11 | 12 | pub fn process(flags: &HashSet, paths: Vec, input: DeriveInput) -> TokenStream { 13 | let DeriveInput { 14 | ref ident, 15 | ref generics, 16 | .. 17 | } = input; 18 | let generated = if generics.lt_token.is_some() { 19 | quote_spanned! { 20 | generics.span() => compile_error!("#[cast_to(..)] can't be used on a generic type definition"); 21 | } 22 | } else { 23 | paths 24 | .into_iter() 25 | .flat_map(|t| generate_caster(ident, &t, flags.contains(&Flag::Sync))) 26 | .collect() 27 | }; 28 | quote! { 29 | #input 30 | #generated 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /macros/src/lib.rs: -------------------------------------------------------------------------------- 1 | extern crate proc_macro; 2 | 3 | use proc_macro::TokenStream; 4 | 5 | use syn::{parse, parse_macro_input, DeriveInput, ItemImpl}; 6 | 7 | use args::{Casts, Flag, Targets}; 8 | use gen_caster::generate_caster; 9 | 10 | mod args; 11 | mod gen_caster; 12 | mod item_impl; 13 | mod item_type; 14 | 15 | /// Attached on an `impl` item or type definition, registers traits as targets for casting. 16 | /// 17 | /// If on an `impl` item, no argument is allowed. But on a type definition, the target traits 18 | /// must be listed explicitly. 19 | /// 20 | /// Add `[sync]` before the list of traits if the underlying type is `Sync + Send` and you 21 | /// need `std::sync::Arc`. 22 | /// 23 | /// # Examples 24 | /// ## On a trait impl 25 | /// ``` 26 | /// use intertrait::*; 27 | /// 28 | /// struct Data; 29 | /// 30 | /// trait Greet { 31 | /// fn greet(&self); 32 | /// } 33 | /// 34 | /// // Greet can be cast into from any sub-trait of CastFrom implemented by Data. 35 | /// #[cast_to] 36 | /// impl Greet for Data { 37 | /// fn greet(&self) { 38 | /// println!("Hello"); 39 | /// } 40 | /// } 41 | /// ``` 42 | /// 43 | /// ## On a type definition 44 | /// Use when a target trait is derived or implemented in an external crate. 45 | /// ``` 46 | /// use intertrait::*; 47 | /// 48 | /// // Debug can be cast into from any sub-trait of CastFrom implemented by Data 49 | /// #[cast_to(std::fmt::Debug)] 50 | /// #[derive(std::fmt::Debug)] 51 | /// struct Data; 52 | /// ``` 53 | /// 54 | /// ## For Arc 55 | /// Use when the underlying type is `Sync + Send` and you want to use `Arc`. 56 | /// ``` 57 | /// use intertrait::*; 58 | /// 59 | /// // Debug can be cast into from any sub-trait of CastFrom implemented by Data 60 | /// #[cast_to([sync] std::fmt::Debug)] 61 | /// #[derive(std::fmt::Debug)] 62 | /// struct Data; 63 | /// ``` 64 | #[proc_macro_attribute] 65 | pub fn cast_to(args: TokenStream, input: TokenStream) -> TokenStream { 66 | match parse::(args) { 67 | Ok(Targets { flags, paths }) => { 68 | if paths.is_empty() { 69 | item_impl::process(&flags, parse_macro_input!(input as ItemImpl)) 70 | } else { 71 | item_type::process(&flags, paths, parse_macro_input!(input as DeriveInput)) 72 | } 73 | } 74 | Err(err) => vec![err.to_compile_error(), input.into()] 75 | .into_iter() 76 | .collect(), 77 | } 78 | .into() 79 | } 80 | 81 | /// Declares target traits for casting implemented by a type. 82 | /// 83 | /// This macro is for registering both a concrete type and its traits to be targets for casting. 84 | /// Useful when the type definition and the trait implementations are in an external crate. 85 | /// 86 | /// **Note**: this macro cannot be used in an expression or statement prior to Rust 1.45.0, 87 | /// due to [a previous limitation](https://github.com/rust-lang/rust/pull/68717). 88 | /// If you want to use it in an expression or statement, use Rust 1.45.0 or later. 89 | /// 90 | /// # Examples 91 | /// ``` 92 | /// use intertrait::*; 93 | /// 94 | /// #[derive(std::fmt::Debug)] 95 | /// enum Data { 96 | /// A, B, C 97 | /// } 98 | /// trait Greet { 99 | /// fn greet(&self); 100 | /// } 101 | /// impl Greet for Data { 102 | /// fn greet(&self) { 103 | /// println!("Hello"); 104 | /// } 105 | /// } 106 | /// 107 | /// castable_to! { Data => std::fmt::Debug, Greet } 108 | /// 109 | /// # fn main() {} 110 | /// ``` 111 | /// 112 | /// When the type is `Sync + Send` and is used with `Arc`: 113 | /// ``` 114 | /// use intertrait::*; 115 | /// 116 | /// #[derive(std::fmt::Debug)] 117 | /// enum Data { 118 | /// A, B, C 119 | /// } 120 | /// trait Greet { 121 | /// fn greet(&self); 122 | /// } 123 | /// impl Greet for Data { 124 | /// fn greet(&self) { 125 | /// println!("Hello"); 126 | /// } 127 | /// } 128 | /// castable_to! { Data => [sync] std::fmt::Debug, Greet } 129 | /// 130 | /// # fn main() {} 131 | /// ``` 132 | #[proc_macro] 133 | pub fn castable_to(input: TokenStream) -> TokenStream { 134 | let Casts { 135 | ty, 136 | targets: Targets { flags, paths }, 137 | } = parse_macro_input!(input); 138 | 139 | paths 140 | .iter() 141 | .map(|t| generate_caster(&ty, t, flags.contains(&Flag::Sync))) 142 | .collect::() 143 | .into() 144 | } 145 | -------------------------------------------------------------------------------- /src/cast.rs: -------------------------------------------------------------------------------- 1 | //! `cast` module contains traits to provide `cast` method for various references 2 | //! and smart pointers. 3 | //! 4 | //! In source files requiring casts, import all of the traits as follows: 5 | //! 6 | //! ```ignore 7 | //! use intertrait::cast::*; 8 | //! ``` 9 | //! 10 | //! Since there exists single trait for each receiver type, the same `cast` method is overloaded. 11 | mod cast_arc; 12 | mod cast_box; 13 | mod cast_mut; 14 | mod cast_rc; 15 | mod cast_ref; 16 | 17 | pub use cast_arc::*; 18 | pub use cast_box::*; 19 | pub use cast_mut::*; 20 | pub use cast_rc::*; 21 | pub use cast_ref::*; 22 | -------------------------------------------------------------------------------- /src/cast/cast_arc.rs: -------------------------------------------------------------------------------- 1 | use crate::{caster, CastFromSync}; 2 | use std::sync::Arc; 3 | 4 | /// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting 5 | /// of a trait object for it behind an `Rc` to a trait object for another trait 6 | /// implemented by the underlying value. 7 | /// 8 | /// # Examples 9 | /// ``` 10 | /// # use std::sync::Arc; 11 | /// # use intertrait::*; 12 | /// use intertrait::cast::*; 13 | /// 14 | /// # #[cast_to([sync] Greet)] 15 | /// # struct Data; 16 | /// # trait Source: CastFrom {} 17 | /// # trait Greet { 18 | /// # fn greet(&self); 19 | /// # } 20 | /// # impl Greet for Data { 21 | /// # fn greet(&self) { 22 | /// # println!("Hello"); 23 | /// # } 24 | /// # } 25 | /// impl Source for Data {} 26 | /// let data = Data; 27 | /// let source = Arc::new(data); 28 | /// let greet = source.cast::(); 29 | /// greet.unwrap_or_else(|_| panic!("must not happen")).greet(); 30 | /// ``` 31 | pub trait CastArc { 32 | /// Casts an `Arc` for this trait into that for type `T`. 33 | fn cast(self: Arc) -> Result, Arc>; 34 | } 35 | 36 | /// A blanket implementation of `CastArc` for traits extending `CastFrom`, `Sync`, and `Send`. 37 | impl CastArc for S { 38 | fn cast(self: Arc) -> Result, Arc> { 39 | match caster::((*self).type_id()) { 40 | Some(caster) => Ok((caster.cast_arc)(self.arc_any())), 41 | None => Err(self), 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cast/cast_box.rs: -------------------------------------------------------------------------------- 1 | use crate::{caster, CastFrom}; 2 | 3 | /// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting 4 | /// of a trait object for it behind a `Box` to a trait object for another trait 5 | /// implemented by the underlying value. 6 | /// 7 | /// # Examples 8 | /// ``` 9 | /// # use intertrait::*; 10 | /// use intertrait::cast::*; 11 | /// 12 | /// # #[cast_to(Greet)] 13 | /// # struct Data; 14 | /// # trait Source: CastFrom {} 15 | /// # trait Greet { 16 | /// # fn greet(&self); 17 | /// # } 18 | /// # impl Greet for Data { 19 | /// # fn greet(&self) { 20 | /// # println!("Hello"); 21 | /// # } 22 | /// # } 23 | /// impl Source for Data {} 24 | /// let data = Box::new(Data); 25 | /// let source: Box = data; 26 | /// let greet = source.cast::(); 27 | /// greet.unwrap_or_else(|_| panic!("casting failed")).greet(); 28 | /// ``` 29 | pub trait CastBox { 30 | /// Casts a box to this trait into that of type `T`. If fails, returns the receiver. 31 | fn cast(self: Box) -> Result, Box>; 32 | } 33 | 34 | /// A blanket implementation of `CastBox` for traits extending `CastFrom`. 35 | impl CastBox for S { 36 | fn cast(self: Box) -> Result, Box> { 37 | match caster::((*self).type_id()) { 38 | Some(caster) => Ok((caster.cast_box)(self.box_any())), 39 | None => Err(self), 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/cast/cast_mut.rs: -------------------------------------------------------------------------------- 1 | use crate::{caster, CastFrom}; 2 | 3 | /// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting 4 | /// of a trait object for it behind an mutable reference to a trait object for another trait 5 | /// implemented by the underlying value. 6 | /// 7 | /// # Examples 8 | /// ``` 9 | /// # use intertrait::*; 10 | /// use intertrait::cast::*; 11 | /// 12 | /// # #[cast_to(Greet)] 13 | /// # struct Data; 14 | /// # trait Source: CastFrom {} 15 | /// # trait Greet { 16 | /// # fn greet(&self); 17 | /// # } 18 | /// # impl Greet for Data { 19 | /// # fn greet(&self) { 20 | /// # println!("Hello"); 21 | /// # } 22 | /// # } 23 | /// impl Source for Data {} 24 | /// let mut data = Data; 25 | /// let source: &mut dyn Source = &mut data; 26 | /// let greet = source.cast::(); 27 | /// greet.unwrap().greet(); 28 | /// ``` 29 | pub trait CastMut { 30 | /// Casts a mutable reference to this trait into that of type `T`. 31 | fn cast(&mut self) -> Option<&mut T>; 32 | } 33 | 34 | /// A blanket implementation of `CastMut` for traits extending `CastFrom`. 35 | impl CastMut for S { 36 | fn cast(&mut self) -> Option<&mut T> { 37 | let any = self.mut_any(); 38 | let caster = caster::((*any).type_id())?; 39 | (caster.cast_mut)(any).into() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/cast/cast_rc.rs: -------------------------------------------------------------------------------- 1 | use crate::{caster, CastFrom}; 2 | use std::rc::Rc; 3 | 4 | /// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting 5 | /// of a trait object for it behind an `Rc` to a trait object for another trait 6 | /// implemented by the underlying value. 7 | /// 8 | /// # Examples 9 | /// ``` 10 | /// # use std::rc::Rc; 11 | /// # use intertrait::*; 12 | /// use intertrait::cast::*; 13 | /// 14 | /// # #[cast_to(Greet)] 15 | /// # struct Data; 16 | /// # trait Source: CastFrom {} 17 | /// # trait Greet { 18 | /// # fn greet(&self); 19 | /// # } 20 | /// # impl Greet for Data { 21 | /// # fn greet(&self) { 22 | /// # println!("Hello"); 23 | /// # } 24 | /// # } 25 | /// impl Source for Data {} 26 | /// let data = Data; 27 | /// let source = Rc::new(data); 28 | /// let greet = source.cast::(); 29 | /// greet.unwrap_or_else(|_| panic!("must not happen")).greet(); 30 | /// ``` 31 | pub trait CastRc { 32 | /// Casts an `Rc` for this trait into that for type `T`. 33 | fn cast(self: Rc) -> Result, Rc>; 34 | } 35 | 36 | /// A blanket implementation of `CastRc` for traits extending `CastFrom`. 37 | impl CastRc for S { 38 | fn cast(self: Rc) -> Result, Rc> { 39 | match caster::((*self).type_id()) { 40 | Some(caster) => Ok((caster.cast_rc)(self.rc_any())), 41 | None => Err(self), 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/cast/cast_ref.rs: -------------------------------------------------------------------------------- 1 | use std::any::TypeId; 2 | 3 | use crate::{caster, CastFrom, Caster, CASTER_MAP}; 4 | 5 | /// A trait that is blanket-implemented for traits extending `CastFrom` to allow for casting 6 | /// of a trait object for it behind an immutable reference to a trait object for another trait 7 | /// implemented by the underlying value. 8 | /// 9 | /// # Examples 10 | /// ## Casting an immutable reference 11 | /// ``` 12 | /// # use intertrait::*; 13 | /// use intertrait::cast::*; 14 | /// 15 | /// # #[cast_to(Greet)] 16 | /// # struct Data; 17 | /// # trait Source: CastFrom {} 18 | /// # trait Greet { 19 | /// # fn greet(&self); 20 | /// # } 21 | /// # impl Greet for Data { 22 | /// # fn greet(&self) { 23 | /// # println!("Hello"); 24 | /// # } 25 | /// # } 26 | /// impl Source for Data {} 27 | /// let data = Data; 28 | /// let source: &dyn Source = &data; 29 | /// let greet = source.cast::(); 30 | /// greet.unwrap().greet(); 31 | /// ``` 32 | /// 33 | /// ## Testing if a cast is possible 34 | /// ``` 35 | /// # use intertrait::*; 36 | /// use intertrait::cast::*; 37 | /// 38 | /// # #[cast_to(Greet)] 39 | /// # struct Data; 40 | /// # trait Source: CastFrom {} 41 | /// # trait Greet { 42 | /// # fn greet(&self); 43 | /// # } 44 | /// # impl Greet for Data { 45 | /// # fn greet(&self) { 46 | /// # println!("Hello"); 47 | /// # } 48 | /// # } 49 | /// impl Source for Data {} 50 | /// let data = Data; 51 | /// let source: &dyn Source = &data; 52 | /// assert!(source.impls::()); 53 | /// assert!(!source.impls::()); 54 | /// ``` 55 | pub trait CastRef { 56 | /// Casts a reference to this trait into that of type `T`. 57 | fn cast(&self) -> Option<&T>; 58 | 59 | /// Tests if this trait object can be cast into `T`. 60 | fn impls(&self) -> bool; 61 | } 62 | 63 | /// A blanket implementation of `CastRef` for traits extending `CastFrom`. 64 | impl CastRef for S { 65 | fn cast(&self) -> Option<&T> { 66 | let any = self.ref_any(); 67 | let caster = caster::(any.type_id())?; 68 | (caster.cast_ref)(any).into() 69 | } 70 | 71 | fn impls(&self) -> bool { 72 | CASTER_MAP.contains_key(&(self.type_id(), TypeId::of::>())) 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /src/hasher.rs: -------------------------------------------------------------------------------- 1 | use std::convert::TryInto; 2 | use std::hash::{BuildHasherDefault, Hasher}; 3 | use std::mem::size_of; 4 | 5 | /// A simple `Hasher` implementation tuned for performance. 6 | #[derive(Default)] 7 | pub struct FastHasher(u64); 8 | 9 | /// A `BuildHasher` for `FastHasher`. 10 | pub type BuildFastHasher = BuildHasherDefault; 11 | 12 | impl Hasher for FastHasher { 13 | fn finish(&self) -> u64 { 14 | self.0 15 | } 16 | 17 | fn write(&mut self, bytes: &[u8]) { 18 | let mut bytes = bytes; 19 | while bytes.len() > size_of::() { 20 | let (u64_bytes, remaining) = bytes.split_at(size_of::()); 21 | self.0 ^= u64::from_ne_bytes(u64_bytes.try_into().unwrap()); 22 | bytes = remaining 23 | } 24 | self.0 ^= bytes 25 | .iter() 26 | .fold(0u64, |result, b| (result << 8) | *b as u64); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! A library providing direct casting among trait objects implemented by a type. 2 | //! 3 | //! In Rust, an object of a sub-trait of [`Any`] can be downcast to a concrete type 4 | //! at runtime if the type is known. But no direct casting between two trait objects 5 | //! (i.e. without involving the concrete type of the backing value) is possible 6 | //! (even no coercion from a trait object to that of its super-trait yet). 7 | //! 8 | //! With this crate, any trait object with [`CastFrom`] as its super-trait can be cast directly 9 | //! to another trait object implemented by the underlying type if the target traits are 10 | //! registered beforehand with the macros provided by this crate. 11 | //! 12 | //! # Usage 13 | //! ``` 14 | //! use intertrait::*; 15 | //! use intertrait::cast::*; 16 | //! 17 | //! struct Data; 18 | //! 19 | //! trait Source: CastFrom {} 20 | //! 21 | //! trait Greet { 22 | //! fn greet(&self); 23 | //! } 24 | //! 25 | //! #[cast_to] 26 | //! impl Greet for Data { 27 | //! fn greet(&self) { 28 | //! println!("Hello"); 29 | //! } 30 | //! } 31 | //! 32 | //! impl Source for Data {} 33 | //! 34 | //! let data = Data; 35 | //! let source: &dyn Source = &data; 36 | //! let greet = source.cast::(); 37 | //! greet.unwrap().greet(); 38 | //! ``` 39 | //! 40 | //! Target traits must be explicitly designated beforehand. There are three ways to do it: 41 | //! 42 | //! * [`#[cast_to]`][cast_to] to `impl` item 43 | //! * [`#[cast_to(Trait)]`][cast_to] to type definition 44 | //! * [`castable_to!(Type => Trait1, Trait2)`][castable_to] 45 | //! 46 | //! If the underlying type involved is `Sync + Send` and you want to use it with [`Arc`], 47 | //! use [`CastFromSync`] in place of [`CastFrom`] and add `[sync]` flag before the list 48 | //! of traits in the macros. Refer to the documents for each of macros for details. 49 | //! 50 | //! For casting, refer to traits defined in [`cast`] module. 51 | //! 52 | //! [cast_to]: ./attr.cast_to.html 53 | //! [castable_to]: ./macro.castable_to.html 54 | //! [`CastFrom`]: ./trait.CastFrom.html 55 | //! [`CastFromSync`]: ./trait.CastFromSync.html 56 | //! [`cast`]: ./cast/index.html 57 | //! [`Any`]: https://doc.rust-lang.org/std/any/trait.Any.html 58 | //! [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html 59 | use std::any::{Any, TypeId}; 60 | use std::collections::HashMap; 61 | use std::rc::Rc; 62 | use std::sync::Arc; 63 | 64 | use linkme::distributed_slice; 65 | use once_cell::sync::Lazy; 66 | 67 | pub use intertrait_macros::*; 68 | 69 | use crate::hasher::BuildFastHasher; 70 | 71 | pub mod cast; 72 | mod hasher; 73 | 74 | #[doc(hidden)] 75 | pub type BoxedCaster = Box; 76 | 77 | #[cfg(doctest)] 78 | doc_comment::doctest!("../README.md"); 79 | 80 | /// A distributed slice gathering constructor functions for [`Caster`]s. 81 | /// 82 | /// A constructor function returns `TypeId` of a concrete type involved in the casting 83 | /// and a `Box` of a trait object backed by a [`Caster`]. 84 | /// 85 | /// [`Caster`]: ./struct.Caster.html 86 | #[doc(hidden)] 87 | #[distributed_slice] 88 | pub static CASTERS: [fn() -> (TypeId, BoxedCaster)] = [..]; 89 | 90 | /// A `HashMap` mapping `TypeId` of a [`Caster`] to an instance of it. 91 | /// 92 | /// [`Caster`]: ./struct.Caster.html 93 | static CASTER_MAP: Lazy> = 94 | Lazy::new(|| { 95 | CASTERS 96 | .iter() 97 | .map(|f| { 98 | let (type_id, caster) = f(); 99 | ((type_id, (*caster).type_id()), caster) 100 | }) 101 | .collect() 102 | }); 103 | 104 | fn cast_arc_panic(_: Arc) -> Arc { 105 | panic!("Prepend [sync] to the list of target traits for Sync + Send types") 106 | } 107 | 108 | /// A `Caster` knows how to cast a reference to or `Box` of a trait object for `Any` 109 | /// to a trait object of trait `T`. Each `Caster` instance is specific to a concrete type. 110 | /// That is, it knows how to cast to single specific trait implemented by single specific type. 111 | /// 112 | /// An implementation of a trait for a concrete type doesn't need to manually provide 113 | /// a `Caster`. Instead attach `#[cast_to]` to the `impl` block. 114 | #[doc(hidden)] 115 | pub struct Caster { 116 | /// Casts an immutable reference to a trait object for `Any` to a reference 117 | /// to a trait object for trait `T`. 118 | pub cast_ref: fn(from: &dyn Any) -> &T, 119 | 120 | /// Casts a mutable reference to a trait object for `Any` to a mutable reference 121 | /// to a trait object for trait `T`. 122 | pub cast_mut: fn(from: &mut dyn Any) -> &mut T, 123 | 124 | /// Casts a `Box` holding a trait object for `Any` to another `Box` holding a trait object 125 | /// for trait `T`. 126 | pub cast_box: fn(from: Box) -> Box, 127 | 128 | /// Casts an `Rc` holding a trait object for `Any` to another `Rc` holding a trait object 129 | /// for trait `T`. 130 | pub cast_rc: fn(from: Rc) -> Rc, 131 | 132 | /// Casts an `Arc` holding a trait object for `Any + Sync + Send + 'static` 133 | /// to another `Arc` holding a trait object for trait `T`. 134 | pub cast_arc: fn(from: Arc) -> Arc, 135 | } 136 | 137 | impl Caster { 138 | pub fn new( 139 | cast_ref: fn(from: &dyn Any) -> &T, 140 | cast_mut: fn(from: &mut dyn Any) -> &mut T, 141 | cast_box: fn(from: Box) -> Box, 142 | cast_rc: fn(from: Rc) -> Rc, 143 | ) -> Caster { 144 | Caster:: { 145 | cast_ref, 146 | cast_mut, 147 | cast_box, 148 | cast_rc, 149 | cast_arc: cast_arc_panic, 150 | } 151 | } 152 | 153 | pub fn new_sync( 154 | cast_ref: fn(from: &dyn Any) -> &T, 155 | cast_mut: fn(from: &mut dyn Any) -> &mut T, 156 | cast_box: fn(from: Box) -> Box, 157 | cast_rc: fn(from: Rc) -> Rc, 158 | cast_arc: fn(from: Arc) -> Arc, 159 | ) -> Caster { 160 | Caster:: { 161 | cast_ref, 162 | cast_mut, 163 | cast_box, 164 | cast_rc, 165 | cast_arc, 166 | } 167 | } 168 | } 169 | 170 | /// Returns a `Caster` from a concrete type `S` to a trait `T` implemented by it. 171 | fn caster(type_id: TypeId) -> Option<&'static Caster> { 172 | CASTER_MAP 173 | .get(&(type_id, TypeId::of::>())) 174 | .and_then(|caster| caster.downcast_ref::>()) 175 | } 176 | 177 | /// `CastFrom` must be extended by a trait that wants to allow for casting into another trait. 178 | /// 179 | /// It is used for obtaining a trait object for [`Any`] from a trait object for its sub-trait, 180 | /// and blanket implemented for all `Sized + Any + 'static` types. 181 | /// 182 | /// # Examples 183 | /// ```ignore 184 | /// trait Source: CastFrom { 185 | /// ... 186 | /// } 187 | /// ``` 188 | pub trait CastFrom: Any + 'static { 189 | /// Returns a immutable reference to `Any`, which is backed by the type implementing this trait. 190 | fn ref_any(&self) -> &dyn Any; 191 | 192 | /// Returns a mutable reference to `Any`, which is backed by the type implementing this trait. 193 | fn mut_any(&mut self) -> &mut dyn Any; 194 | 195 | /// Returns a `Box` of `Any`, which is backed by the type implementing this trait. 196 | fn box_any(self: Box) -> Box; 197 | 198 | /// Returns an `Rc` of `Any`, which is backed by the type implementing this trait. 199 | fn rc_any(self: Rc) -> Rc; 200 | } 201 | 202 | /// `CastFromSync` must be extended by a trait that is `Any + Sync + Send + 'static` 203 | /// and wants to allow for casting into another trait behind references and smart pointers 204 | /// especially including `Arc`. 205 | /// 206 | /// It is used for obtaining a trait object for [`Any + Sync + Send + 'static`] from an object 207 | /// for its sub-trait, and blanket implemented for all `Sized + Sync + Send + 'static` types. 208 | /// 209 | /// # Examples 210 | /// ```ignore 211 | /// trait Source: CastFromSync { 212 | /// ... 213 | /// } 214 | /// ``` 215 | pub trait CastFromSync: CastFrom + Sync + Send + 'static { 216 | fn arc_any(self: Arc) -> Arc; 217 | } 218 | 219 | impl CastFrom for T { 220 | fn ref_any(&self) -> &dyn Any { 221 | self 222 | } 223 | 224 | fn mut_any(&mut self) -> &mut dyn Any { 225 | self 226 | } 227 | 228 | fn box_any(self: Box) -> Box { 229 | self 230 | } 231 | 232 | fn rc_any(self: Rc) -> Rc { 233 | self 234 | } 235 | } 236 | 237 | impl CastFrom for dyn Any + 'static { 238 | fn ref_any(&self) -> &dyn Any { 239 | self 240 | } 241 | 242 | fn mut_any(&mut self) -> &mut dyn Any { 243 | self 244 | } 245 | 246 | fn box_any(self: Box) -> Box { 247 | self 248 | } 249 | 250 | fn rc_any(self: Rc) -> Rc { 251 | self 252 | } 253 | } 254 | 255 | impl CastFromSync for T { 256 | fn arc_any(self: Arc) -> Arc { 257 | self 258 | } 259 | } 260 | 261 | impl CastFrom for dyn Any + Sync + Send + 'static { 262 | fn ref_any(&self) -> &dyn Any { 263 | self 264 | } 265 | 266 | fn mut_any(&mut self) -> &mut dyn Any { 267 | self 268 | } 269 | 270 | fn box_any(self: Box) -> Box { 271 | self 272 | } 273 | 274 | fn rc_any(self: Rc) -> Rc { 275 | self 276 | } 277 | } 278 | 279 | impl CastFromSync for dyn Any + Sync + Send + 'static { 280 | fn arc_any(self: Arc) -> Arc { 281 | self 282 | } 283 | } 284 | 285 | #[cfg(test)] 286 | mod tests { 287 | use std::any::{Any, TypeId}; 288 | use std::fmt::{Debug, Display}; 289 | 290 | use linkme::distributed_slice; 291 | 292 | use crate::{BoxedCaster, CastFromSync}; 293 | 294 | use super::cast::*; 295 | use super::*; 296 | 297 | #[distributed_slice(super::CASTERS)] 298 | static TEST_CASTER: fn() -> (TypeId, BoxedCaster) = create_test_caster; 299 | 300 | #[derive(Debug)] 301 | struct TestStruct; 302 | 303 | trait SourceTrait: CastFromSync {} 304 | 305 | impl SourceTrait for TestStruct {} 306 | 307 | fn create_test_caster() -> (TypeId, BoxedCaster) { 308 | let type_id = TypeId::of::(); 309 | let caster = Box::new(Caster:: { 310 | cast_ref: |from| from.downcast_ref::().unwrap(), 311 | cast_mut: |from| from.downcast_mut::().unwrap(), 312 | cast_box: |from| from.downcast::().unwrap(), 313 | cast_rc: |from| from.downcast::().unwrap(), 314 | cast_arc: |from| from.downcast::().unwrap(), 315 | }); 316 | (type_id, caster) 317 | } 318 | 319 | #[test] 320 | fn cast_ref() { 321 | let ts = TestStruct; 322 | let st: &dyn SourceTrait = &ts; 323 | let debug = st.cast::(); 324 | assert!(debug.is_some()); 325 | } 326 | 327 | #[test] 328 | fn cast_mut() { 329 | let mut ts = TestStruct; 330 | let st: &mut dyn SourceTrait = &mut ts; 331 | let debug = st.cast::(); 332 | assert!(debug.is_some()); 333 | } 334 | 335 | #[test] 336 | fn cast_box() { 337 | let ts = Box::new(TestStruct); 338 | let st: Box = ts; 339 | let debug = st.cast::(); 340 | assert!(debug.is_ok()); 341 | } 342 | 343 | #[test] 344 | fn cast_rc() { 345 | let ts = Rc::new(TestStruct); 346 | let st: Rc = ts; 347 | let debug = st.cast::(); 348 | assert!(debug.is_ok()); 349 | } 350 | 351 | #[test] 352 | fn cast_arc() { 353 | let ts = Arc::new(TestStruct); 354 | let st: Arc = ts; 355 | let debug = st.cast::(); 356 | assert!(debug.is_ok()); 357 | } 358 | 359 | #[test] 360 | fn cast_ref_wrong() { 361 | let ts = TestStruct; 362 | let st: &dyn SourceTrait = &ts; 363 | let display = st.cast::(); 364 | assert!(display.is_none()); 365 | } 366 | 367 | #[test] 368 | fn cast_mut_wrong() { 369 | let mut ts = TestStruct; 370 | let st: &mut dyn SourceTrait = &mut ts; 371 | let display = st.cast::(); 372 | assert!(display.is_none()); 373 | } 374 | 375 | #[test] 376 | fn cast_box_wrong() { 377 | let ts = Box::new(TestStruct); 378 | let st: Box = ts; 379 | let display = st.cast::(); 380 | assert!(display.is_err()); 381 | } 382 | 383 | #[test] 384 | fn cast_rc_wrong() { 385 | let ts = Rc::new(TestStruct); 386 | let st: Rc = ts; 387 | let display = st.cast::(); 388 | assert!(display.is_err()); 389 | } 390 | 391 | #[test] 392 | fn cast_arc_wrong() { 393 | let ts = Arc::new(TestStruct); 394 | let st: Arc = ts; 395 | let display = st.cast::(); 396 | assert!(display.is_err()); 397 | } 398 | 399 | #[test] 400 | fn cast_ref_from_any() { 401 | let ts = TestStruct; 402 | let st: &dyn Any = &ts; 403 | let debug = st.cast::(); 404 | assert!(debug.is_some()); 405 | } 406 | 407 | #[test] 408 | fn cast_mut_from_any() { 409 | let mut ts = TestStruct; 410 | let st: &mut dyn Any = &mut ts; 411 | let debug = st.cast::(); 412 | assert!(debug.is_some()); 413 | } 414 | 415 | #[test] 416 | fn cast_box_from_any() { 417 | let ts = Box::new(TestStruct); 418 | let st: Box = ts; 419 | let debug = st.cast::(); 420 | assert!(debug.is_ok()); 421 | } 422 | 423 | #[test] 424 | fn cast_rc_from_any() { 425 | let ts = Rc::new(TestStruct); 426 | let st: Rc = ts; 427 | let debug = st.cast::(); 428 | assert!(debug.is_ok()); 429 | } 430 | 431 | #[test] 432 | fn cast_arc_from_any() { 433 | let ts = Arc::new(TestStruct); 434 | let st: Arc = ts; 435 | let debug = st.cast::(); 436 | assert!(debug.is_ok()); 437 | } 438 | 439 | #[test] 440 | fn impls_ref() { 441 | let ts = TestStruct; 442 | let st: &dyn SourceTrait = &ts; 443 | assert!(st.impls::()); 444 | } 445 | 446 | #[test] 447 | fn impls_mut() { 448 | let mut ts = TestStruct; 449 | let st: &mut dyn SourceTrait = &mut ts; 450 | assert!((*st).impls::()); 451 | } 452 | 453 | #[test] 454 | fn impls_box() { 455 | let ts = Box::new(TestStruct); 456 | let st: Box = ts; 457 | assert!((*st).impls::()); 458 | } 459 | 460 | #[test] 461 | fn impls_rc() { 462 | let ts = Rc::new(TestStruct); 463 | let st: Rc = ts; 464 | assert!((*st).impls::()); 465 | } 466 | 467 | #[test] 468 | fn impls_arc() { 469 | let ts = Arc::new(TestStruct); 470 | let st: Arc = ts; 471 | assert!((*st).impls::()); 472 | } 473 | 474 | #[test] 475 | fn impls_not_ref() { 476 | let ts = TestStruct; 477 | let st: &dyn SourceTrait = &ts; 478 | assert!(!st.impls::()); 479 | } 480 | 481 | #[test] 482 | fn impls_not_mut() { 483 | let mut ts = TestStruct; 484 | let st: &mut dyn Any = &mut ts; 485 | assert!(!(*st).impls::()); 486 | } 487 | 488 | #[test] 489 | fn impls_not_box() { 490 | let ts = Box::new(TestStruct); 491 | let st: Box = ts; 492 | assert!(!st.impls::()); 493 | } 494 | 495 | #[test] 496 | fn impls_not_rc() { 497 | let ts = Rc::new(TestStruct); 498 | let st: Rc = ts; 499 | assert!(!(*st).impls::()); 500 | } 501 | 502 | #[test] 503 | fn impls_not_arc() { 504 | let ts = Arc::new(TestStruct); 505 | let st: Arc = ts; 506 | assert!(!(*st).impls::()); 507 | } 508 | } 509 | -------------------------------------------------------------------------------- /tests/castable_to.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | 4 | struct Data; 5 | 6 | trait Source: CastFrom {} 7 | 8 | trait Greet { 9 | fn greet(&self); 10 | } 11 | 12 | impl Greet for Data { 13 | fn greet(&self) { 14 | println!("Hello"); 15 | } 16 | } 17 | 18 | trait Greet1 { 19 | fn greet1(&self); 20 | } 21 | 22 | impl Greet1 for Data { 23 | fn greet1(&self) { 24 | println!("Hello1"); 25 | } 26 | } 27 | 28 | trait Greet2 { 29 | fn greet2(&self); 30 | } 31 | 32 | impl Greet2 for Data { 33 | fn greet2(&self) { 34 | println!("Hello2"); 35 | } 36 | } 37 | 38 | impl Source for Data {} 39 | 40 | castable_to! { Data => crate::Greet, Greet1, Greet2 } 41 | 42 | #[test] 43 | fn test_multi_traits_on_struct() { 44 | let data = Data; 45 | let source: &dyn Source = &data; 46 | 47 | let greet = source.cast::(); 48 | greet.unwrap().greet(); 49 | 50 | let greet1 = source.cast::(); 51 | greet1.unwrap().greet1(); 52 | 53 | let greet2 = source.cast::(); 54 | greet2.unwrap().greet2(); 55 | } 56 | -------------------------------------------------------------------------------- /tests/on-enum.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | 4 | #[cast_to(Greet)] 5 | #[allow(dead_code)] 6 | enum Data { 7 | Var1, 8 | Var2(u32), 9 | } 10 | 11 | trait Source: CastFrom {} 12 | 13 | trait Greet { 14 | fn greet(&self); 15 | } 16 | 17 | impl Greet for Data { 18 | fn greet(&self) { 19 | println!("Hello"); 20 | } 21 | } 22 | 23 | impl Source for Data {} 24 | 25 | #[test] 26 | fn test_cast_to_on_enum() { 27 | let data = Data::Var2(1); 28 | let source: &dyn Source = &data; 29 | let greet = source.cast::(); 30 | greet.unwrap().greet(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/on-struct.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | 4 | #[cast_to(Greet)] 5 | struct Data; 6 | 7 | trait Source: CastFrom {} 8 | 9 | trait Greet { 10 | fn greet(&self); 11 | } 12 | 13 | impl Greet for Data { 14 | fn greet(&self) { 15 | println!("Hello"); 16 | } 17 | } 18 | 19 | impl Source for Data {} 20 | 21 | #[test] 22 | fn test_cast_to_on_struct() { 23 | let data = Data; 24 | let source: &dyn Source = &data; 25 | let greet = source.cast::(); 26 | greet.unwrap().greet(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/on-trait-impl-assoc-type1.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use intertrait::cast::*; 4 | use intertrait::*; 5 | 6 | struct I32Data(i32); 7 | 8 | trait Source: CastFrom {} 9 | 10 | trait Producer { 11 | type Output: Debug; 12 | 13 | fn produce(&self) -> Self::Output; 14 | } 15 | 16 | #[cast_to] 17 | impl Producer for I32Data { 18 | type Output = i32; 19 | 20 | fn produce(&self) -> Self::Output { 21 | self.0 22 | } 23 | } 24 | 25 | impl Source for I32Data {} 26 | 27 | #[test] 28 | fn test_cast_to_on_trait_impl_with_assoc_type1() { 29 | let data = I32Data(100); 30 | let source: &dyn Source = &data; 31 | let producer = source.cast::>(); 32 | assert_eq!(producer.unwrap().produce(), data.0); 33 | } 34 | -------------------------------------------------------------------------------- /tests/on-trait-impl-assoc-type2.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use intertrait::cast::*; 4 | use intertrait::*; 5 | 6 | struct Data; 7 | 8 | trait Source: CastFrom {} 9 | 10 | trait Concat { 11 | type I1: Debug; 12 | type I2: Debug; 13 | 14 | fn concat(&self, a: Self::I1, b: Self::I2) -> String; 15 | } 16 | 17 | #[cast_to] 18 | impl Concat for Data { 19 | type I1 = i32; 20 | type I2 = &'static str; 21 | 22 | fn concat(&self, a: Self::I1, b: Self::I2) -> String { 23 | format!("Data: {} - {}", a, b) 24 | } 25 | } 26 | 27 | impl Source for Data {} 28 | 29 | #[test] 30 | fn test_cast_to_on_trait_impl_with_assoc_type2() { 31 | let data = Data; 32 | let source: &dyn Source = &data; 33 | let concat = source.cast::>(); 34 | assert_eq!(concat.unwrap().concat(101, "hello"), "Data: 101 - hello"); 35 | } 36 | -------------------------------------------------------------------------------- /tests/on-trait-impl-assoc-type3.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::Debug; 2 | 3 | use intertrait::cast::*; 4 | use intertrait::*; 5 | 6 | struct Data; 7 | 8 | trait Source: CastFrom {} 9 | 10 | trait Concat { 11 | type I1: Debug; 12 | type I2: Debug; 13 | 14 | fn concat(&self, prefix: T, a: Self::I1, b: Self::I2) -> String; 15 | } 16 | 17 | #[cast_to] 18 | impl Concat for Data { 19 | type I1 = i32; 20 | type I2 = &'static str; 21 | 22 | fn concat(&self, prefix: String, a: Self::I1, b: Self::I2) -> String { 23 | format!("{}: {} - {}", prefix, a, b) 24 | } 25 | } 26 | 27 | impl Source for Data {} 28 | 29 | #[test] 30 | fn test_cast_to_on_trait_impl_with_assoc_type3() { 31 | let data = Data; 32 | let source: &dyn Source = &data; 33 | let concat = source.cast::>(); 34 | assert_eq!( 35 | concat.unwrap().concat("Data".to_owned(), 101, "hello"), 36 | "Data: 101 - hello" 37 | ); 38 | } 39 | -------------------------------------------------------------------------------- /tests/on-trait-impl.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | 4 | struct Data; 5 | 6 | trait Source: CastFrom {} 7 | 8 | trait Greet { 9 | fn greet(&self); 10 | } 11 | 12 | #[cast_to] 13 | impl Greet for Data { 14 | fn greet(&self) { 15 | println!("Hello"); 16 | } 17 | } 18 | 19 | impl Source for Data {} 20 | 21 | #[test] 22 | fn test_cast_to_on_trait_impl() { 23 | let data = Data; 24 | let source: &dyn Source = &data; 25 | let greet = source.cast::(); 26 | greet.unwrap().greet(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/on-type-multi-traits.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | 4 | #[cast_to(Greet, Greet1, Greet2)] 5 | struct Data; 6 | 7 | trait Source: CastFrom {} 8 | 9 | trait Greet { 10 | fn greet(&self); 11 | } 12 | 13 | impl Greet for Data { 14 | fn greet(&self) { 15 | println!("Hello"); 16 | } 17 | } 18 | 19 | trait Greet1 { 20 | fn greet1(&self); 21 | } 22 | 23 | impl Greet1 for Data { 24 | fn greet1(&self) { 25 | println!("Hello1"); 26 | } 27 | } 28 | 29 | trait Greet2 { 30 | fn greet2(&self); 31 | } 32 | 33 | impl Greet2 for Data { 34 | fn greet2(&self) { 35 | println!("Hello2"); 36 | } 37 | } 38 | 39 | impl Source for Data {} 40 | 41 | #[test] 42 | fn test_multi_traits_on_struct() { 43 | let data = Data; 44 | let source: &dyn Source = &data; 45 | 46 | let greet = source.cast::(); 47 | greet.unwrap().greet(); 48 | 49 | let greet1 = source.cast::(); 50 | greet1.unwrap().greet1(); 51 | 52 | let greet2 = source.cast::(); 53 | greet2.unwrap().greet2(); 54 | } 55 | -------------------------------------------------------------------------------- /tests/run.rs: -------------------------------------------------------------------------------- 1 | #[test] 2 | fn tests() { 3 | let t = trybuild::TestCases::new(); 4 | t.compile_fail("tests/ui/*.rs"); 5 | } 6 | -------------------------------------------------------------------------------- /tests/ui/duplicate-flags.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | use std::sync::Arc; 4 | 5 | #[cast_to([sync, sync] Greet)] 6 | struct Data; 7 | 8 | trait Source: CastFromSync {} 9 | 10 | trait Greet { 11 | fn greet(&self); 12 | } 13 | 14 | impl Greet for Data { 15 | fn greet(&self) { 16 | println!("Hello"); 17 | } 18 | } 19 | 20 | impl Source for Data {} 21 | 22 | fn main() { 23 | let data = Arc::new(Data); 24 | let source: Arc = data; 25 | let greet = source.cast::(); 26 | greet.unwrap_or_else(|_| panic!("can't happen")).greet(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/ui/duplicate-flags.stderr: -------------------------------------------------------------------------------- 1 | error: Duplicated flag: sync 2 | --> $DIR/duplicate-flags.rs:5:18 3 | | 4 | 5 | #[cast_to([sync, sync] Greet)] 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/on-generic-type.rs: -------------------------------------------------------------------------------- 1 | use intertrait::*; 2 | use intertrait::cast::*; 3 | use std::marker::PhantomData; 4 | 5 | #[cast_to(Greet)] 6 | struct Data { 7 | phantom: PhantomData, 8 | } 9 | 10 | trait Source: CastFrom {} 11 | 12 | trait Greet { 13 | fn greet(&self); 14 | } 15 | 16 | impl Greet for Data { 17 | fn greet(&self) { 18 | println!("Hello"); 19 | } 20 | } 21 | 22 | impl Source for Data {} 23 | 24 | fn main() { 25 | let data = Data:: { 26 | phantom: PhantomData, 27 | }; 28 | let source: &dyn Source = &data; 29 | let greet = source.cast::(); 30 | greet.unwrap().greet(); 31 | } 32 | -------------------------------------------------------------------------------- /tests/ui/on-generic-type.stderr: -------------------------------------------------------------------------------- 1 | error: #[cast_to(..)] can't be used on a generic type definition 2 | --> $DIR/on-generic-type.rs:6:12 3 | | 4 | 6 | struct Data { 5 | | ^ 6 | -------------------------------------------------------------------------------- /tests/ui/on-type-impl.rs: -------------------------------------------------------------------------------- 1 | use intertrait::*; 2 | 3 | struct Data; 4 | 5 | #[cast_to] 6 | impl Data { 7 | fn hello() { 8 | println!("hello!"); 9 | } 10 | } 11 | 12 | fn main() { 13 | let _ = Data; 14 | } 15 | -------------------------------------------------------------------------------- /tests/ui/on-type-impl.stderr: -------------------------------------------------------------------------------- 1 | error: #[cast_to] should only be on an impl of a trait 2 | --> $DIR/on-type-impl.rs:6:6 3 | | 4 | 6 | impl Data { 5 | | ^^^^ 6 | -------------------------------------------------------------------------------- /tests/ui/unknown-flag.rs: -------------------------------------------------------------------------------- 1 | use intertrait::cast::*; 2 | use intertrait::*; 3 | use std::sync::Arc; 4 | 5 | #[cast_to([sync, send] Greet)] 6 | struct Data; 7 | 8 | trait Source: CastFromSync {} 9 | 10 | trait Greet { 11 | fn greet(&self); 12 | } 13 | 14 | impl Greet for Data { 15 | fn greet(&self) { 16 | println!("Hello"); 17 | } 18 | } 19 | 20 | impl Source for Data {} 21 | 22 | fn main() { 23 | let data = Arc::new(Data); 24 | let source: Arc = data; 25 | let greet = source.cast::(); 26 | greet.unwrap_or_else(|_| panic!("can't happen")).greet(); 27 | } 28 | -------------------------------------------------------------------------------- /tests/ui/unknown-flag.stderr: -------------------------------------------------------------------------------- 1 | error: Unknown flag: send 2 | --> $DIR/unknown-flag.rs:5:18 3 | | 4 | 5 | #[cast_to([sync, send] Greet)] 5 | | ^^^^ 6 | --------------------------------------------------------------------------------