├── .github └── workflows │ ├── lint.yml │ └── test.yml ├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md └── ambassador ├── Cargo.toml ├── README.md ├── src ├── delegate_shared.rs ├── delegate_to_methods.rs ├── derive.rs ├── lib.rs ├── register.rs └── util.rs └── tests ├── compile-fail ├── delegatable_trait.rs ├── delegate_self_fail.rs ├── delegate_to_method.rs ├── delegate_to_method_where.rs ├── delegate_to_missing_method.rs ├── enum_associated_constant.rs ├── enum_associated_types.rs ├── extra_args_in_delegate_attr.rs ├── extra_items_in_remote_methods_impl.rs ├── generic_trait_bad_where.rs ├── missing_delegate_attribute.rs ├── muti_field_struct.rs ├── single_field_struct.rs └── taxonomy_fail.rs ├── compiletest.rs └── run-pass ├── associated_types.rs ├── async.rs ├── auto_where_clause.rs ├── cfg.rs ├── const_generic.rs ├── delegate_remote.rs ├── delegate_self.rs ├── delegate_to_method_g_and_at.rs ├── delegate_to_methods.rs ├── delegate_to_methods_dyn_works.rs ├── delegate_to_methods_in_trait_impl.rs ├── delegate_to_remote_methods.rs ├── delegate_to_remote_methods_on_remote_type.rs ├── delegate_trait_remote_display.rs ├── derive_and_trait_in_modules.rs ├── derive_in_module.rs ├── double_generics.rs ├── enum_associated_constant.rs ├── enum_associated_types.rs ├── gat.rs ├── generic_enum.rs ├── generic_method.rs ├── generic_struct.rs ├── generic_trait_any.rs ├── generic_trait_complex.rs ├── generic_trait_lifetime.rs ├── generic_trait_single_type.rs ├── generic_tuple_struct.rs ├── inhibit_where_clause.rs ├── late_bound_lifetime.rs ├── method_mut_ref_self.rs ├── method_ref_self.rs ├── method_self.rs ├── method_single_arg.rs ├── muti-feature.rs ├── single_trait_single_method.rs ├── single_variant.rs ├── struct_field_target.rs ├── struct_single_field.rs ├── struct_single_field_target.rs ├── taxonomy.rs ├── tuple_struct_single_field.rs ├── tuple_struct_target.rs ├── two_enums.rs ├── where_clause.rs └── where_clause_split.rs /.github/workflows/lint.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Lint 4 | jobs: 5 | fmt: 6 | name: Rustfmt 7 | runs-on: ubuntu-latest 8 | strategy: 9 | matrix: 10 | rust: 11 | - 1.53.0 12 | steps: 13 | - uses: actions/checkout@v1 14 | - uses: actions-rs/toolchain@v1 15 | with: 16 | profile: minimal 17 | toolchain: nightly 18 | override: false 19 | - uses: actions-rs/toolchain@v1 20 | with: 21 | profile: minimal 22 | toolchain: ${{ matrix.rust }} 23 | override: true 24 | - run: rustup component add rustfmt 25 | - run: cargo +nightly update -Z minimal-versions 26 | - uses: actions-rs/cargo@v1 27 | with: 28 | command: fmt 29 | args: --all -- --check 30 | 31 | clippy: 32 | name: Clippy 33 | runs-on: ubuntu-latest 34 | strategy: 35 | matrix: 36 | rust: 37 | - 1.53.0 38 | steps: 39 | - uses: actions/checkout@v1 40 | - uses: actions-rs/toolchain@v1 41 | with: 42 | profile: minimal 43 | toolchain: nightly 44 | override: false 45 | - uses: actions-rs/toolchain@v1 46 | with: 47 | profile: minimal 48 | toolchain: ${{ matrix.rust }} 49 | override: true 50 | - run: rustup component add clippy 51 | - run: cargo +nightly update -Z minimal-versions 52 | - uses: actions-rs/cargo@v1 53 | with: 54 | command: clippy 55 | args: -- -D warnings 56 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | on: [push, pull_request] 2 | 3 | name: Check & Test 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | rust: 12 | - 1.53.0 13 | steps: 14 | - uses: actions/checkout@v1 15 | - uses: actions-rs/toolchain@v1 16 | with: 17 | profile: minimal 18 | toolchain: nightly 19 | override: false 20 | - uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: ${{ matrix.rust }} 24 | override: true 25 | - run: cargo +nightly update -Z minimal-versions 26 | - uses: actions-rs/cargo@v1 27 | with: 28 | command: check 29 | 30 | test: 31 | name: Test Suite 32 | runs-on: ubuntu-latest 33 | strategy: 34 | matrix: 35 | rust: 36 | - 1.79.0 37 | # It's useful to test applications of these macros to newer features so using 1.53 for tests doesn't work 38 | # This needs to be locked to a specific version of Rust since the exact error messages may change 39 | # When switching this Rust version make sure to also update tests to use newer error messages if applicable 40 | steps: 41 | - uses: actions/checkout@v1 42 | - uses: actions-rs/toolchain@v1 43 | with: 44 | profile: minimal 45 | toolchain: ${{ matrix.rust }} 46 | override: true 47 | - uses: actions-rs/cargo@v1 48 | with: 49 | command: test 50 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "ambassador", 5 | ] 6 | -------------------------------------------------------------------------------- /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 | MIT License 2 | 3 | Copyright (c) 2019 The ambassador contributors. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ambassador - Delegate trait implementations via procedural macros 2 | 3 | 4 | 5 | Crates.io version 7 | 8 | 9 | 10 | docs.rs docs 12 | 13 | 14 |
15 | 16 | 17 | Delegating the implementation of traits to enum variants or fields of a struct normally requires a lot of boilerplate code. Ambassador is an attempt to eliminate that boilerplate by deriving the delegating trait implementation via procedural macros. 18 | 19 | **The minimum supported Rust version is 1.53.0.** 20 | 21 | ## Installation 22 | 23 | ``` 24 | cargo add ambassador 25 | ``` 26 | 27 | ## General Usage 28 | 29 | More examples are also available https://github.com/hobofan/ambassador/tree/master/ambassador/tests/run-pass 30 | 31 | ### `#[delegatable_trait]` 32 | 33 | First we need to make our trait available for delegation by adding a `#[delegatable_trait]` attribute to it (this also makes your trait delegatable in other crates): 34 | 35 | ```rust 36 | use ambassador::delegatable_trait; 37 | 38 | #[delegatable_trait] // <------- 39 | pub trait Shout { 40 | fn shout(&self, input: &str) -> String; 41 | } 42 | ``` 43 | 44 | ### `#[derive(Delegate)]` & `#[delegate(Trait)]` 45 | 46 | Now we can delegate the implementation of our trait to a struct field/enum variants by adding `#[derive(Delegate)]` and its associated attribute `#[delegate(Trait)]` to it: 47 | 48 | ```rust 49 | use ambassador::Delegate; 50 | 51 | pub struct Cat; 52 | 53 | impl Shout for Cat { 54 | fn shout(&self, input: &str) -> String { 55 | format!("{} - meow!", input) 56 | } 57 | } 58 | 59 | #[derive(Delegate)] // <------- 60 | #[delegate(Shout)] // <-------- Delegate implementation of Shout to struct field 61 | pub struct WrappedCat(Cat); 62 | ``` 63 | 64 | #### `#[delegate(..., target = "foo")]` - `target` key 65 | 66 | For structs with multiple fields, the field that should act as delegation target can be specified via the `target` key: 67 | 68 | ```rust 69 | #[derive(Delegate)] 70 | #[delegate(Shout, target = "foo")] // <-------- Delegate implementation of Shout to struct field .foo 71 | pub struct WrappedCats { 72 | foo: Cat, 73 | bar: Cat, 74 | } 75 | ``` 76 | 77 | This also works for tuple structs with multiple fields, by using their index as a target key: 78 | 79 | ```rust 80 | #[derive(Delegate)] 81 | #[delegate(Shout, target = "1")] // <-------- Delegate implementation of Shout to second field of type Dog 82 | pub struct WrappedAnimals(Cat, Dog); 83 | ``` 84 | 85 | #### `#[delegate(..., target = "self")]` - `target="self"` 86 | Types that implement all the methods of a trait without implementing the trait itself, 87 | can be made to implement that trait by setting `target="self"`. 88 | This doesn't work for traits with associated types and constants, and requires the where clause to be added explicitly (see `where` key). 89 | If the type doesn't actually implement the methods (possibly due to an incomplete `where` clause) this can cause a `[unconditional_recursion]` error. 90 | 91 | A possible use case of this is when refactoring some methods of a public type into a trait, 92 | the type still needs to implement the methods outside the trait for semver reasons, 93 | and using this feature reduces the boilderplate of implementing the trait with the same methods. 94 | 95 | 96 | ```rust 97 | #[derive(Delegate)] 98 | #[delegate(Shout, target="self")] 99 | pub struct Cat; 100 | 101 | impl Cat { 102 | fn shout(&self, input: &str) -> String { 103 | format!("{} - meow!", input) 104 | } 105 | } 106 | ``` 107 | 108 | #### `#[delegate(..., where = "A: Shout")]` - `where` key 109 | 110 | To make a delegation apply only for certain generic bounds, similar to a [native where clause](https://doc.rust-lang.org/stable/rust-by-example/generics/where.html), you can specify a `where` attribute: 111 | 112 | A where clause is automatically applied that makes sure the target field implements the trait being delegated 113 | ```rust 114 | #[derive(Delegate)] 115 | #[delegate(Shout, where = "A: Debug")] // <---- Delegate implementation of Shout to .foo field if foo field implements Debug 116 | pub struct WrappedFoo { 117 | foo: A, 118 | } 119 | ``` 120 | 121 | 122 | #### `#[delegate(Shout)]` - trait generics 123 | 124 | We can also delegate traits with generics. 125 | When doing this all instances of `X` and `'x` followed by arbitrary digits eg. `X0` `X12` `'x3` are treated as maximally generic. 126 | The automatically added where clause ensures they are valid for the inner type being delegated to. 127 | Explict where clauses to further refine these types can be added as normal. 128 | Specific types can be used instead of `X` to only derive for those. 129 | 130 | ```rust 131 | use ambassador::{delegatable_trait, Delegate}; 132 | use std::fmt::Display; 133 | 134 | #[delegatable_trait] // <------- 135 | pub trait Shout { 136 | fn shout(&self, input: T) -> String; 137 | } 138 | 139 | pub struct Cat; 140 | 141 | impl Shout for Cat { 142 | fn shout(&self, input: T) -> String { 143 | format!("{} - meow!", input) 144 | } 145 | } 146 | 147 | #[derive(Delegate)] 148 | #[delegate(Shout, generics = "X")] // <-------- X is fully generic 149 | // The automatic where clause ensures X: Display 150 | // We could also use #[delegate(Shout<& 'a str>, generics = "'a")] to only delegate for &str 151 | pub struct WrappedCat(Cat); 152 | ``` 153 | 154 | 155 | ### For remote traits: `#[delegatable_trait_remote]` 156 | 157 | If you want to make an existing trait that lives outside you crate available for delegation, you can do so by copy-pasting it's signature into your code and using the `#[delegatable_trait_remote]` attribute (see [full code sample](./ambassador/tests/run-pass/delegate_trait_remote_display.rs)): 158 | 159 | ```rust 160 | use ambassador::delegatable_trait_remote; 161 | use std::fmt::Display; 162 | 163 | #[delegatable_trait_remote] 164 | trait Display { 165 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error>; 166 | } 167 | 168 | #[derive(Delegate)] 169 | #[delegate(Display)] // <-------- Delegate implementation of Display to struct field 170 | pub struct WrappedCat(Cat); 171 | ``` 172 | 173 | ### For remote types `#[delegate_remote]` 174 | 175 | If you want to make an existing type that lives outside you crate delegate, you can do so by copy-pasting it's definition into your code and using the `#[delegate_remote]` attribute (see [full code sample](./ambassador/tests/run-pass/delegate_remote.rs)): 176 | 177 | If the type is a struct, not all the fields have to be public, only the ones being delegated to. 178 | 179 | ```rust 180 | mod wrapped { 181 | use super::*; 182 | pub struct WrappedAnimals { 183 | pub foo: Cat, 184 | pub bar: A, 185 | baz: u32, // private field 186 | } 187 | } 188 | 189 | use wrapped::*; 190 | 191 | #[delegate_remote] 192 | #[delegate(Shout, target = "bar")] 193 | struct WrappedAnimals { 194 | foo: Cat, 195 | bar: A, 196 | // We don't even have to include baz since we don't delegate to it 197 | } 198 | ``` 199 | 200 | Note: Because of the orphan rule `#[delegatable_trait_remote]` and `#[delegate_remote]` can't be combined 201 | 202 | ## Usage Examples 203 | 204 | In this example we have a trait `Shout` that is implemented for both `Cat` and `Dog`. 205 | 206 | ### Delegate to enum variants 207 | 208 | We now add an `Animal` enum and add a delegated trait implementation for `Shout`, 209 | that calls the respective implementations of the enum variants: 210 | 211 | ```rust 212 | use ambassador::{delegatable_trait, Delegate}; 213 | 214 | #[delegatable_trait] 215 | pub trait Shout { 216 | fn shout(&self, input: &str) -> String; 217 | } 218 | 219 | pub struct Cat; 220 | 221 | impl Shout for Cat { 222 | fn shout(&self, input: &str) -> String { 223 | format!("{} - meow!", input) 224 | } 225 | } 226 | 227 | pub struct Dog; 228 | 229 | impl Shout for Dog { 230 | fn shout(&self, input: &str) -> String { 231 | format!("{} - wuff!", input) 232 | } 233 | } 234 | 235 | #[derive(Delegate)] 236 | #[delegate(Shout)] 237 | pub enum Animal { 238 | Cat(Cat), 239 | Dog(Dog), 240 | } 241 | 242 | pub fn main() { 243 | let foo_animal = Animal::Cat(Cat); 244 | println!("{}", foo_animal.shout("BAR")); 245 | } 246 | ``` 247 | 248 | ### Delegate to tuple struct field 249 | 250 | Delegating a trait implementation for a tuple struct **(only single-field tuples supported for now)**, for e.g. a newtype pattern. 251 | 252 | ```rust 253 | use ambassador::{delegatable_trait, Delegate}; 254 | 255 | #[delegatable_trait] 256 | pub trait Shout { 257 | fn shout(&self, input: &str) -> String; 258 | } 259 | 260 | pub struct Cat; 261 | 262 | impl Shout for Cat { 263 | fn shout(&self, input: &str) -> String { 264 | format!("{} - meow!", input) 265 | } 266 | } 267 | 268 | #[derive(Delegate)] 269 | #[delegate(Shout)] 270 | pub struct WrappedCat(Cat); 271 | 272 | pub fn main() { 273 | let foo_animal = WrappedCat(Cat); 274 | println!("{}", foo_animal.shout("BAR")); 275 | } 276 | ``` 277 | 278 | ### Delegate to struct field 279 | 280 | Delegating a trait implementation for a normal struct 281 | 282 | ```rust 283 | use ambassador::{delegatable_trait, Delegate}; 284 | 285 | #[delegatable_trait] 286 | pub trait Shout { 287 | fn shout(&self, input: &str) -> String; 288 | } 289 | 290 | pub struct Cat; 291 | 292 | impl Shout for Cat { 293 | fn shout(&self, input: &str) -> String { 294 | format!("{} - meow!", input) 295 | } 296 | } 297 | 298 | #[derive(Delegate)] 299 | #[delegate(Shout)] 300 | pub struct WrappedCat { 301 | inner_cat: Cat, 302 | } 303 | 304 | pub fn main() { 305 | let foo_animal = WrappedCat { inner_cat: Cat }; 306 | println!("{}", foo_animal.shout("BAR")); 307 | } 308 | ``` 309 | 310 | #### License 311 | 312 | 313 | Licensed under either of Apache License, Version 314 | 2.0 or MIT license at your option. 315 | 316 | 317 |
318 | 319 | 320 | Unless you explicitly state otherwise, any contribution intentionally submitted 321 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall 322 | be dual licensed as above, without any additional terms or conditions. 323 | 324 | 325 | -------------------------------------------------------------------------------- /ambassador/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "ambassador" 3 | version = "0.4.1" 4 | authors = ["Maximilian Goisser ", "David Ewert "] 5 | edition = "2018" 6 | description = "Trait implementation delegation via procedural macros" 7 | keywords = ["trait", "delegate", "delegation", "macros", "proc-macros"] 8 | license = "MIT OR Apache-2.0" 9 | repository = "https://github.com/hobofan/ambassador" 10 | readme = "README.md" 11 | 12 | [lib] 13 | proc-macro = true 14 | 15 | [dependencies] 16 | syn = { version = "1.0.25", features = ["full", "extra-traits"] } 17 | quote = "1.0.2" 18 | proc-macro2 = "1.0.6" 19 | itertools = "0.10.3" 20 | 21 | [dev-dependencies] 22 | compiletest_rs = "0.8" 23 | 24 | [package.metadata.release] 25 | no-dev-version = true 26 | tag-prefix = "" 27 | tag-name = "{{version}}" 28 | -------------------------------------------------------------------------------- /ambassador/README.md: -------------------------------------------------------------------------------- 1 | ../README.md -------------------------------------------------------------------------------- /ambassador/src/delegate_shared.rs: -------------------------------------------------------------------------------- 1 | use crate::util::error; 2 | use itertools::Itertools; 3 | use proc_macro2::{Ident, TokenStream as TokenStream2}; 4 | use quote::ToTokens; 5 | use std::cmp::Ordering; 6 | use syn::ext::IdentExt; 7 | use syn::parse::{ParseStream, Parser}; 8 | use syn::punctuated::Punctuated; 9 | use syn::spanned::Spanned; 10 | use syn::token::Comma; 11 | use syn::{ 12 | parse_quote, GenericParam, Generics, ImplGenerics, LitBool, LitStr, PathArguments, Result, 13 | Token, WhereClause, WherePredicate, 14 | }; 15 | 16 | pub(super) trait DelegateTarget: Default { 17 | fn try_update(&mut self, key: &str, lit: LitStr) -> Option>; 18 | } 19 | 20 | #[derive(Default)] 21 | pub(super) struct DelegateArgs { 22 | pub(crate) target: T, 23 | pub(crate) where_clauses: Punctuated, 24 | pub(crate) generics: Vec, 25 | pub(crate) inhibit_automatic_where_clause: bool, 26 | } 27 | 28 | impl DelegateArgs { 29 | fn add_key_value(&mut self, key: Ident, lit: LitStr) -> Result<()> { 30 | let span = key.span(); 31 | match &*key.to_string() { 32 | "where" => { 33 | let where_clause_val = 34 | lit.parse_with(Punctuated::::parse_terminated)?; 35 | self.where_clauses.extend(where_clause_val); 36 | } 37 | "generics" => { 38 | let generics_val = 39 | lit.parse_with(Punctuated::::parse_terminated)?; 40 | self.generics.extend(generics_val); 41 | } 42 | "automatic_where_clause" => { 43 | let auto_where_val: LitBool = lit.parse()?; 44 | self.inhibit_automatic_where_clause = !auto_where_val.value; 45 | } 46 | key => self 47 | .target 48 | .try_update(key, lit) 49 | .unwrap_or_else(|| error!(span, "invalid key for a delegate attribute"))?, 50 | } 51 | Ok(()) 52 | } 53 | } 54 | 55 | pub(super) fn delegate_attr_as_trait_and_iter( 56 | outer_steam: ParseStream<'_>, 57 | ) -> Result<(syn::Path, DelegateArgs)> { 58 | let items; 59 | syn::parenthesized!(items in outer_steam); 60 | let path = items.parse()?; 61 | let mut delegate_args = DelegateArgs::default(); 62 | while !items.is_empty() { 63 | let _: Token![,] = items.parse()?; 64 | let key = items.call(Ident::parse_any)?; 65 | let _: Token![=] = items.parse()?; 66 | let val = items.parse()?; 67 | delegate_args.add_key_value(key, val)?; 68 | } 69 | Ok((path, delegate_args)) 70 | } 71 | 72 | impl DelegateArgs { 73 | pub fn from_tokens(tokens: TokenStream2) -> Result<(syn::Path, Self)> { 74 | let (path, mut res) = delegate_attr_as_trait_and_iter.parse2(tokens)?; 75 | res.generics.sort_unstable_by(|x, y| match (x, y) { 76 | (GenericParam::Lifetime(_), GenericParam::Lifetime(_)) => Ordering::Equal, 77 | (GenericParam::Lifetime(_), _) => Ordering::Less, 78 | (_, GenericParam::Lifetime(_)) => Ordering::Greater, 79 | _ => Ordering::Equal, 80 | }); 81 | Ok((path, res)) 82 | } 83 | } 84 | 85 | pub(super) fn delegate_macro( 86 | input: &I, 87 | attrs: Vec, 88 | delegate_single: impl Fn(&I, TokenStream2) -> Result, 89 | ) -> TokenStream2 { 90 | // Parse the input tokens into a syntax tree 91 | let mut delegate_attributes = attrs 92 | .into_iter() 93 | .filter(|attr| attr.path.is_ident("delegate")) 94 | .map(|attr| attr.tokens) 95 | .peekable(); 96 | if delegate_attributes.peek().is_none() { 97 | return error!( 98 | proc_macro2::Span::call_site(), 99 | "No #[delegate] attribute specified. If you want to delegate an implementation of trait `SomeTrait` add the attribute:\n#[delegate(SomeTrait)]" 100 | ).unwrap_or_else(|x| x.to_compile_error()); 101 | } 102 | 103 | let iter = delegate_attributes.map(|attr| delegate_single(input, attr)); 104 | iter.flat_map(|x| x.unwrap_or_else(|err| err.to_compile_error())) 105 | .collect() 106 | } 107 | 108 | pub(super) fn trait_info(trait_path_full: &syn::Path) -> Result<(&Ident, impl ToTokens + '_)> { 109 | let trait_segment = trait_path_full.segments.last().unwrap(); 110 | let trait_ident: &Ident = &trait_segment.ident; 111 | let trait_generics = match &trait_segment.arguments { 112 | PathArguments::None => None, 113 | PathArguments::AngleBracketed(seg) => Some(super::util::TailingPunctuated(&seg.args)), 114 | _ => return error!(trait_path_full.span(), "cannot delegate to Fn* traits"), 115 | }; 116 | Ok((trait_ident, trait_generics)) 117 | } 118 | 119 | pub(super) fn merge_impl_generics( 120 | impl_generics: ImplGenerics, 121 | added_generics: Vec, 122 | ) -> impl Iterator { 123 | let tokens = impl_generics.into_token_stream(); 124 | let impl_generics = if tokens.is_empty() { 125 | Punctuated::new() 126 | } else { 127 | let generics: Generics = parse_quote!(#tokens); 128 | generics.params 129 | }; 130 | // Make sure all lifetimes come first 131 | impl_generics.into_iter().merge_by(added_generics, |x, _| { 132 | matches!(x, GenericParam::Lifetime(_)) 133 | }) 134 | } 135 | 136 | pub(super) fn merge_generics<'a>( 137 | impl_generics: &'a Punctuated, 138 | added_generics: &'a [GenericParam], 139 | ) -> impl Iterator { 140 | // Make sure all lifetimes come first 141 | impl_generics.iter().merge_by(added_generics, |&x, _| { 142 | matches!(x, GenericParam::Lifetime(_)) 143 | }) 144 | } 145 | 146 | pub(super) fn build_where_clause( 147 | mut explicit_where_clauses: Punctuated, 148 | where_clause: Option<&WhereClause>, 149 | ) -> WhereClause { 150 | // Merges the where clause based on the type generics with all the where clauses specified 151 | // via "where" macro attributes. 152 | explicit_where_clauses.extend(where_clause.into_iter().flat_map(|n| n.predicates.clone())); 153 | WhereClause { 154 | where_token: Default::default(), 155 | predicates: explicit_where_clauses, 156 | } 157 | } 158 | 159 | pub(super) fn add_auto_where_clause( 160 | clause: &mut WhereClause, 161 | trait_path_full: &syn::Path, 162 | ty: &syn::Type, 163 | ) { 164 | clause.predicates.push(parse_quote!(#ty : #trait_path_full)) 165 | } 166 | -------------------------------------------------------------------------------- /ambassador/src/delegate_to_methods.rs: -------------------------------------------------------------------------------- 1 | use super::delegate_shared::{self, add_auto_where_clause}; 2 | use super::register::macro_name; 3 | use super::util; 4 | use crate::util::{error, try_option, ReceiverType}; 5 | use itertools::Itertools; 6 | use proc_macro::TokenStream; 7 | use proc_macro2::{Ident, TokenStream as TokenStream2}; 8 | use quote::{quote, ToTokens}; 9 | use std::cell::Cell; 10 | use std::convert::{TryFrom, TryInto}; 11 | use std::default::Default; 12 | use syn::punctuated::Punctuated; 13 | use syn::spanned::Spanned; 14 | use syn::{ 15 | parse_macro_input, GenericParam, ItemImpl, LitStr, Result, ReturnType, Token, Type, WhereClause, 16 | }; 17 | 18 | struct DelegateImplementer { 19 | ty: Type, 20 | impl_generics: Punctuated, 21 | where_clause: Option, 22 | methods: Vec, 23 | invalid_methods: Vec<(Ident, syn::Error)>, 24 | } 25 | 26 | struct MethodInfo { 27 | name: Ident, 28 | receiver: ReceiverType, 29 | ret: Type, 30 | used: Cell, // modified when a usage is found 31 | } 32 | 33 | impl TryFrom for MethodInfo { 34 | type Error = (Ident, syn::Error); 35 | 36 | fn try_from(method: syn::ImplItemMethod) -> std::result::Result { 37 | let receiver_or_err = util::receiver_type(&method.sig); 38 | let return_span = method.sig.paren_token.span; 39 | let mut ident = Some(method.sig.ident); 40 | let mut add_ident = |err| (ident.take().unwrap(), err); 41 | let receiver = receiver_or_err.map_err(&mut add_ident)?; 42 | let ret = match method.sig.output { 43 | ReturnType::Default => { 44 | error!(return_span, "delegated to methods must return").map_err(&mut add_ident)? 45 | } 46 | ReturnType::Type(_, t) => *t, 47 | }; 48 | let ret = match (ret, receiver) { 49 | (ret, ReceiverType::Owned) => ret, 50 | ( 51 | Type::Reference(syn::TypeReference { 52 | mutability, elem, .. 53 | }), 54 | ReceiverType::Ref, 55 | ) if mutability.is_none() => *elem, 56 | ( 57 | Type::Reference(syn::TypeReference { 58 | mutability, elem, .. 59 | }), 60 | ReceiverType::MutRef, 61 | ) if mutability.is_some() => *elem, 62 | (ret, _) => error!( 63 | ret.span(), 64 | "delegated to methods must have mutability of return type must match \"self\"" 65 | ) 66 | .map_err(&mut add_ident)?, 67 | }; 68 | if method.sig.inputs.len() != 1 { 69 | // Just the receiver 70 | error!( 71 | ret.span(), 72 | "delegated to methods must only take \"self\" parameter" 73 | ) 74 | .map_err(&mut add_ident)? 75 | } 76 | Ok(MethodInfo { 77 | name: ident.unwrap(), 78 | receiver, 79 | ret, 80 | used: Cell::new(false), 81 | }) 82 | } 83 | } 84 | 85 | #[derive(Default)] 86 | struct DelegateTarget { 87 | owned_id: Option, 88 | ref_id: Option, 89 | ref_mut_id: Option, 90 | } 91 | 92 | impl delegate_shared::DelegateTarget for DelegateTarget { 93 | fn try_update(&mut self, key: &str, lit: LitStr) -> Option> { 94 | match key { 95 | "target_owned" => { 96 | self.owned_id = try_option!(lit.parse()); 97 | Some(Ok(())) 98 | } 99 | "target_ref" => { 100 | self.ref_id = try_option!(lit.parse()); 101 | Some(Ok(())) 102 | } 103 | "target_mut" => { 104 | self.ref_mut_id = try_option!(lit.parse()); 105 | Some(Ok(())) 106 | } 107 | _ => None, 108 | } 109 | } 110 | } 111 | 112 | impl DelegateTarget { 113 | fn as_arr(&self) -> [(ReceiverType, Option<&Ident>); 3] { 114 | use ReceiverType::*; 115 | [ 116 | (Owned, self.owned_id.as_ref()), 117 | (Ref, self.ref_id.as_ref()), 118 | (MutRef, self.ref_mut_id.as_ref()), 119 | ] 120 | } 121 | } 122 | 123 | type DelegateArgs = delegate_shared::DelegateArgs; 124 | 125 | fn search_methods<'a>( 126 | id: &Ident, 127 | implementer: &'a DelegateImplementer, 128 | receiver: ReceiverType, 129 | ) -> Result<&'a Type> { 130 | let DelegateImplementer { 131 | methods, 132 | invalid_methods, 133 | .. 134 | } = implementer; 135 | match methods.iter().find(|m| &m.name == id) { 136 | None => match invalid_methods.iter().find(|(name, _)| name == id) { 137 | Some((_, err)) => { 138 | let mut err: syn::Error = err.clone(); 139 | let note = syn::Error::new(id.span(), "Note: method used in #[delegate] attribute"); 140 | err.combine(note); 141 | Err(err) 142 | } 143 | None => error!( 144 | id.span(), 145 | "impl block doesn't have any methods with this name" 146 | ), 147 | }, 148 | Some(res) => { 149 | res.used.set(true); 150 | if res.receiver != receiver { 151 | error!( 152 | id.span(), 153 | "method needs to have a receiver of type {}", receiver 154 | ) 155 | } else { 156 | Ok(&res.ret) 157 | } 158 | } 159 | } 160 | } 161 | 162 | impl DelegateTarget { 163 | /// Select the correct return. 164 | pub fn get_ret_type<'a>( 165 | &self, 166 | span: proc_macro2::Span, 167 | implementer: &'a DelegateImplementer, 168 | ) -> Result<&'a Type> { 169 | let res = self 170 | .as_arr() 171 | .iter() 172 | .flat_map(|(recv_ty, id)| id.map(|id| search_methods(id, implementer, *recv_ty))) 173 | .fold(None, |rsf, x| match (rsf, x) { 174 | (None, x) => Some(x), 175 | (_, Err(x)) | (Some(Err(x)), _) => Some(Err(x)), 176 | (Some(Ok(y)), Ok(x)) if y == x => Some(Ok(x)), 177 | (Some(Ok(y)), Ok(x)) => { 178 | let mut err = 179 | syn::Error::new(span, "target methods have different return types"); 180 | let err1 = syn::Error::new(x.span(), "Note: first return type defined here"); 181 | let err2 = syn::Error::new(y.span(), "Note: other return type defined here"); 182 | err.combine(err1); 183 | err.combine(err2); 184 | Some(Err(err)) 185 | } 186 | }); 187 | res.unwrap_or_else(|| error!(span, "no targets were specified")) 188 | } 189 | } 190 | 191 | // Checks that: 192 | // - all the items in an impl are methods 193 | // - all the methods are _empty_ (no body, just the signature) 194 | fn check_for_method_impls_and_extras(impl_items: &[syn::ImplItem]) -> Result<()> { 195 | let iter = impl_items.iter().filter_map(|i| { 196 | // We're looking for *only* empty methods (no block). 197 | if let syn::ImplItem::Method(m) = i { 198 | let block = &m.block; 199 | let empty_block = syn::parse2::(quote! { fn foo(); }) 200 | .unwrap() 201 | .block; 202 | 203 | // We'll accept `{}` blocks and omitted blocks (i.e. `fn foo();`): 204 | if block.stmts.is_empty() || block == &empty_block { 205 | None 206 | } else { 207 | Some(syn::Error::new( 208 | block.span(), 209 | "Only method signatures are allowed here (no blocks!)", 210 | )) 211 | } 212 | } else { 213 | // Everything else is an error: 214 | Some(syn::Error::new( 215 | i.span(), 216 | "Only method signatures are allowed here, everything else is discarded", 217 | )) 218 | } 219 | }); 220 | fold_errors(Ok(()), iter) 221 | } 222 | 223 | // Checks that: 224 | // - all the methods provided are actually referenced in the `delegate` attributes on the impl 225 | fn check_for_unused_methods(other_errs: Result<()>, methods: &[MethodInfo]) -> Result<()> { 226 | let iter = methods.iter().filter_map(|m| { 227 | if m.used.get() { 228 | None 229 | } else { 230 | Some(syn::Error::new( 231 | m.name.span(), 232 | "This method is not used by any `delegate` attributes; please remove it", 233 | )) 234 | } 235 | }); 236 | fold_errors(other_errs, iter) 237 | } 238 | 239 | fn fold_errors(init: Result<()>, i: impl Iterator) -> Result<()> { 240 | i.fold(init, |errors, error| match (errors, error) { 241 | (Ok(_), err) => Err(err), 242 | (Err(mut errs), err) => { 243 | errs.extend(err); 244 | Err(errs) 245 | } 246 | }) 247 | } 248 | 249 | enum KeepInfo { 250 | Keep(TokenStream2), 251 | Discard(Result<()>), 252 | } 253 | 254 | pub fn delegate_macro(input: TokenStream, keep_impl_block: bool) -> TokenStream { 255 | use KeepInfo::*; 256 | // Parse the input tokens into a syntax tree 257 | let mut input = parse_macro_input!(input as ItemImpl); 258 | let attrs = std::mem::take(&mut input.attrs); 259 | assert!( 260 | attrs.iter().all(|attr| attr.path.is_ident("delegate")), 261 | "All attributes must be \"delegate\"" 262 | ); 263 | let keep_info = if keep_impl_block { 264 | Keep(input.to_token_stream()) 265 | } else { 266 | Discard(check_for_method_impls_and_extras(&input.items)) 267 | }; 268 | let (methods, invalid_methods) = input 269 | .items 270 | .into_iter() 271 | .filter_map(|item| match item { 272 | syn::ImplItem::Method(method) => Some(method.try_into()), 273 | _ => None, 274 | }) 275 | .partition_result(); 276 | let implementer = DelegateImplementer { 277 | ty: *input.self_ty, 278 | impl_generics: input.generics.params, 279 | where_clause: input.generics.where_clause, 280 | methods, 281 | invalid_methods, 282 | }; 283 | let mut res = delegate_shared::delegate_macro(&implementer, attrs, delegate_single_attr); 284 | match keep_info { 285 | Keep(input_copy) => res.extend(input_copy), 286 | Discard(other_errs) => { 287 | let unused_err = check_for_unused_methods(other_errs, &implementer.methods); 288 | let invalid_err_iter = implementer.invalid_methods.into_iter().map(|(_, err)| err); 289 | let invalid_err = fold_errors(unused_err, invalid_err_iter); 290 | if let Err(err) = invalid_err { 291 | res.extend(err.to_compile_error()); 292 | } 293 | } 294 | } 295 | res.into() 296 | } 297 | 298 | fn delegate_single_attr( 299 | implementer: &DelegateImplementer, 300 | delegate_attr: TokenStream2, 301 | ) -> Result { 302 | let span = delegate_attr.span(); 303 | let (trait_path_full, args) = DelegateArgs::from_tokens(delegate_attr)?; 304 | let (trait_ident, trait_generics_p) = delegate_shared::trait_info(&trait_path_full)?; 305 | let macro_name: Ident = macro_name(trait_ident); 306 | 307 | let impl_generics = delegate_shared::merge_generics(&implementer.impl_generics, &args.generics); 308 | let implementer_ty = &implementer.ty; 309 | let mut where_clause = 310 | delegate_shared::build_where_clause(args.where_clauses, implementer.where_clause.as_ref()); 311 | 312 | let delegate_ty = args.target.get_ret_type(span, implementer)?; 313 | let owned_ident = args.target.owned_id.into_iter(); 314 | let ref_ident = args.target.ref_id.into_iter(); 315 | let ref_mut_ident = args.target.ref_mut_id.into_iter(); 316 | add_auto_where_clause(&mut where_clause, &trait_path_full, delegate_ty); 317 | let res = quote! { 318 | impl <#(#impl_generics,)*> #trait_path_full for #implementer_ty #where_clause { 319 | #macro_name!{body_struct(<#trait_generics_p>, #delegate_ty, (#(#owned_ident())*), (#(#ref_ident())*), (#(#ref_mut_ident())*))} 320 | } 321 | }; 322 | Ok(res) 323 | } 324 | -------------------------------------------------------------------------------- /ambassador/src/derive.rs: -------------------------------------------------------------------------------- 1 | use crate::delegate_shared::{self, add_auto_where_clause}; 2 | use crate::register::{macro_name, match_name}; 3 | use crate::util::{error, process_results, try_option}; 4 | use proc_macro::TokenStream; 5 | use proc_macro2::{Ident, Span, TokenStream as TokenStream2}; 6 | use quote::quote; 7 | use std::default::Default; 8 | use syn::spanned::Spanned; 9 | use syn::{parse_macro_input, parse_quote, DeriveInput, Generics, LitStr, Result, WherePredicate}; 10 | 11 | #[derive(Debug)] 12 | struct DelegateImplementer { 13 | generics: Generics, 14 | ty: Ident, 15 | info: DelegateImplementerInfo, 16 | } 17 | 18 | #[derive(Debug)] 19 | enum DelegateImplementerInfo { 20 | Enum { 21 | variant_idents: Vec, 22 | first_type: syn::Type, 23 | other_types: Vec, 24 | }, 25 | SingleFieldStruct { 26 | field_ident: syn::Member, 27 | field_type: syn::Type, 28 | }, 29 | MultiFieldStruct { 30 | fields: Vec<(syn::Member, syn::Type)>, 31 | }, 32 | } 33 | 34 | fn try_info_from_data(span: Span, data: syn::Data) -> Result { 35 | let res = match data { 36 | syn::Data::Enum(enum_data) => { 37 | let iter = enum_data.variants.into_iter().map(|n| { 38 | let span = n.span(); 39 | let mut it = n.fields.into_iter(); 40 | match it.next() { 41 | None => error!(span, "enum variant has no fields"), 42 | Some(_) if it.count() != 0 => error!(span, "enum variant has multiple fields"), 43 | Some(f) => Ok((n.ident, f.ty)), 44 | } 45 | }); 46 | let (variant_idents, mut variant_types): (Vec<_>, Vec<_>) = 47 | process_results(iter, |iter| iter.unzip())?; 48 | let first_type = variant_types.pop().expect("enum has no variants"); 49 | DelegateImplementerInfo::Enum { 50 | variant_idents, 51 | first_type, 52 | other_types: variant_types, 53 | } 54 | } 55 | syn::Data::Struct(struct_data) => match struct_data.fields.len() { 56 | 1 => { 57 | let field = struct_data.fields.into_iter().next().unwrap(); 58 | let field_ident = match field.ident { 59 | Some(id) => syn::Member::Named(id), 60 | None => syn::Member::Unnamed(0.into()), 61 | }; 62 | DelegateImplementerInfo::SingleFieldStruct { 63 | field_ident, 64 | field_type: field.ty, 65 | } 66 | } 67 | _ => { 68 | let fields = struct_data 69 | .fields 70 | .into_iter() 71 | .enumerate() 72 | .map(|(i, field)| match field.ident { 73 | Some(id) => (syn::Member::Named(id), field.ty), 74 | None => (syn::Member::Unnamed(i.into()), field.ty), 75 | }) 76 | .collect(); 77 | DelegateImplementerInfo::MultiFieldStruct { fields } 78 | } 79 | }, 80 | _ => { 81 | return error!( 82 | span, 83 | "ambassador currently only supports #[derive(Delegate)] for: \n\ 84 | - single-field enums\n\ 85 | - (tuple) structs" 86 | ) 87 | } 88 | }; 89 | Ok(res) 90 | } 91 | 92 | enum DelegateTarget { 93 | Field(syn::Member), 94 | TrgNone, 95 | TrgSelf, 96 | } 97 | 98 | impl Default for DelegateTarget { 99 | fn default() -> Self { 100 | DelegateTarget::TrgNone 101 | } 102 | } 103 | 104 | impl delegate_shared::DelegateTarget for DelegateTarget { 105 | fn try_update(&mut self, key: &str, lit: LitStr) -> Option> { 106 | match key { 107 | "target" => { 108 | if !matches!(self, DelegateTarget::TrgNone) { 109 | try_option!(error!( 110 | lit.span(), 111 | "\"target\" value for delegate attribute can only be specified once" 112 | )); 113 | } 114 | *self = if lit.value() == "self" { 115 | DelegateTarget::TrgSelf 116 | } else { 117 | let target_val = try_option!(lit.parse()); 118 | DelegateTarget::Field(target_val) 119 | }; 120 | Some(Ok(())) 121 | } 122 | _ => None, 123 | } 124 | } 125 | } 126 | 127 | type DelegateArgs = delegate_shared::DelegateArgs; 128 | 129 | fn unknown_field(target: &syn::Member) -> Result { 130 | error!( 131 | target.span(), 132 | "Unknown field specified as \"target\" value in #[delegate] attribute" 133 | ) 134 | } 135 | 136 | /// Select the correct field_ident based on the `target`. 137 | fn get_field<'a>( 138 | target: &syn::Member, 139 | field_idents: &'a [(syn::Member, syn::Type)], 140 | ) -> Result<&'a (syn::Member, syn::Type)> { 141 | let field = field_idents.iter().find(|n| n.0 == *target); 142 | match field { 143 | Some(field) => Ok(field), 144 | None => unknown_field(target), 145 | } 146 | } 147 | 148 | pub fn delegate_macro(input: TokenStream) -> TokenStream { 149 | // Parse the input tokens into a syntax tree 150 | let input = parse_macro_input!(input as DeriveInput); 151 | let info = match try_info_from_data(input.span(), input.data) { 152 | Ok(info) => info, 153 | Err(err) => return err.to_compile_error().into(), 154 | }; 155 | let implementer = DelegateImplementer { 156 | info, 157 | generics: input.generics, 158 | ty: input.ident, 159 | }; 160 | delegate_shared::delegate_macro(&implementer, input.attrs, delegate_single_attr).into() 161 | } 162 | 163 | fn delegate_single_attr( 164 | implementer: &DelegateImplementer, 165 | delegate_attr: TokenStream2, 166 | ) -> Result { 167 | let span = delegate_attr.span(); 168 | let (trait_path_full, args) = DelegateArgs::from_tokens(delegate_attr)?; 169 | let (trait_ident, trait_generics_p) = delegate_shared::trait_info(&trait_path_full)?; 170 | let macro_name: Ident = macro_name(trait_ident); 171 | 172 | let generics = &implementer.generics; 173 | let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); 174 | let mut where_clause = delegate_shared::build_where_clause(args.where_clauses, where_clause); 175 | let impl_generics = delegate_shared::merge_impl_generics(impl_generics, args.generics); 176 | let implementer_ident = &implementer.ty; 177 | use {DelegateImplementerInfo::*, DelegateTarget::*}; 178 | let res = match (&args.target, &implementer.info) { 179 | (TrgSelf, _) => quote! { 180 | impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause { 181 | #macro_name!{body_self(<#trait_generics_p>)} 182 | } 183 | }, 184 | (Field(field), Enum {..}) => return error!( 185 | field.span(), 186 | "\"target\" value on #[delegate] attribute can not be specified for enums" 187 | ), 188 | (TrgNone, Enum {variant_idents, first_type, other_types}) => { 189 | if !args.inhibit_automatic_where_clause { 190 | add_auto_where_clause(&mut where_clause, &trait_path_full, first_type); 191 | } 192 | let match_name = match_name(trait_ident); 193 | where_clause 194 | .predicates 195 | .extend(other_types.iter().map::( 196 | |arg| parse_quote!(#arg : #match_name<#trait_generics_p #first_type>), 197 | )); 198 | let mod_name = quote::format_ident!( 199 | "ambassador_module_{}_for_{}", 200 | trait_ident, 201 | implementer_ident 202 | ); 203 | quote! { 204 | #[allow(non_snake_case)] 205 | mod #mod_name { 206 | use super::*; 207 | #macro_name!{use_assoc_ty_bounds} 208 | impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause { 209 | #macro_name!{body_enum(<#trait_generics_p>, #first_type, (#(#other_types),*), (#(#implementer_ident::#variant_idents),*))} 210 | } 211 | } 212 | } 213 | } 214 | (trg, SingleFieldStruct {field_ident, field_type}) => { 215 | match trg { 216 | Field(f) if f != field_ident => { 217 | unknown_field(f)?; 218 | } 219 | _ => {} 220 | } 221 | if !args.inhibit_automatic_where_clause { 222 | add_auto_where_clause(&mut where_clause, &trait_path_full, field_type); 223 | } 224 | 225 | quote! { 226 | impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause { 227 | #macro_name!{body_struct(<#trait_generics_p>, #field_type, #field_ident)} 228 | } 229 | } 230 | } 231 | (TrgNone, MultiFieldStruct {..}) => return error!( 232 | span, 233 | "\"target\" value on #[delegate] attribute has to be specified for structs with multiple fields" 234 | ), 235 | (Field(field), MultiFieldStruct {fields}) => { 236 | let field = get_field(field, fields)?; 237 | let field_ident = &field.0; 238 | let field_type = &field.1; 239 | if !args.inhibit_automatic_where_clause { 240 | add_auto_where_clause(&mut where_clause, &trait_path_full, field_type); 241 | } 242 | 243 | quote! { 244 | impl <#(#impl_generics,)*> #trait_path_full for #implementer_ident #ty_generics #where_clause { 245 | #macro_name!{body_struct(<#trait_generics_p>, #field_type, #field_ident)} 246 | } 247 | } 248 | } 249 | }; 250 | Ok(res) 251 | } 252 | -------------------------------------------------------------------------------- /ambassador/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! 2 | //! # Ambassador - Delegate trait implementations via procedural macros 3 | //! 4 | //! 5 | //! 6 | //! Crates.io version 8 | //! 9 | //! 10 | //! 11 | //! docs.rs docs 13 | //! 14 | //! 15 | //!
16 | //! 17 | //! 18 | //! Delegating the implementation of traits to enum variants or fields of a struct normally requires a lot of boilerplate code. Ambassador is an attempt to eliminate that boilerplate by deriving the delegating trait implementation via procedural macros. 19 | //! 20 | //! **The minimum supported Rust version is 1.53.0.** 21 | //! 22 | //! See individual macro documentation for detailed instructions. 23 | //! 24 | //! This is one example combining a large number of features: 25 | //! 26 | //! ``` 27 | //! extern crate ambassador; 28 | //! 29 | //! use std::collections::{HashMap, BTreeMap}; 30 | //! use std::borrow::Borrow; 31 | //! use std::cmp::{Eq, Ord}; 32 | //! use std::hash::{Hash, BuildHasher}; 33 | //! use std::ops::Deref; 34 | //! use ambassador::{delegatable_trait, delegate_remote, delegate_to_remote_methods, Delegate}; 35 | //! 36 | //! #[delegatable_trait] 37 | //! pub trait Map { 38 | //! type V; 39 | //! } 40 | //! 41 | //! #[delegatable_trait] 42 | //! pub trait Get: Map { 43 | //! fn get(&self, k: &Q) -> Option<&Self::V>; 44 | //! } 45 | //! 46 | //! impl Map for HashMap { 47 | //! type V = V; 48 | //! } 49 | //! 50 | //! impl Map for BTreeMap { 51 | //! type V = V; 52 | //! } 53 | //! 54 | //! 55 | //! // No automatic where clause provided for target = "self" 56 | //! #[delegate_remote] 57 | //! #[delegate(Get, target = "self", generics = "X", where = "K: Hash + Eq + Borrow, S: BuildHasher, X: Hash + Eq + ?Sized")] 58 | //! struct HashMap(); 59 | //! 60 | //! #[delegate_remote] 61 | //! #[delegate(Get, target = "self", generics = "X", where = "K: Ord + Borrow, X: Ord + ?Sized")] 62 | //! struct BTreeMap(); 63 | //! 64 | //! #[derive(Delegate)] 65 | //! #[delegate(Map)] 66 | //! #[delegate(Get, generics = "X", where = "X: ?Sized, B: Map")] //auto where clause misses required on super trait 67 | //! pub enum Either { 68 | //! Left(A), 69 | //! Right(B), 70 | //! } 71 | //! 72 | //! #[delegate_to_remote_methods] 73 | //! #[delegate(Map, target_ref = "deref")] 74 | //! #[delegate(Get, target_ref = "deref", generics = "X", where = "X: ?Sized")] 75 | //! impl Box { 76 | //! fn deref(&self) -> &M; 77 | //! } 78 | //! 79 | //! pub fn main() { 80 | //! let x: HashMap<&'static str, u32> = [("a", 1)].into(); 81 | //! let my_map: Either>, BTreeMap<&'static str, u32>> = Either::Left(Box::new(x)); 82 | //! assert_eq!(my_map.get("a"), Some(&1)); 83 | //! } 84 | //! ``` 85 | //! 86 | //! # Cross module uses 87 | //! Modules using delegateable traits should add `use ::ambassador_impl_;` 88 | //! where `` is the path to the module which used either 89 | //! [`macro@delegatable_trait`] or [`macro@delegatable_trait_remote`] 90 | //! ### Example 91 | //! ``` 92 | //! mod m{ 93 | //! pub mod m1 { 94 | //! use ambassador::delegatable_trait; 95 | //! #[delegatable_trait] 96 | //! pub trait Shout { 97 | //! fn shout(&self); 98 | //! } 99 | //! } 100 | //! 101 | //! mod m2 { 102 | //! use ambassador::Delegate; 103 | //! use super::m1::{Shout, ambassador_impl_Shout}; 104 | //! 105 | //! #[derive(Delegate)] 106 | //! #[delegate(Shout)] 107 | //! struct Wrap(X); 108 | //! } 109 | //! } 110 | //! 111 | //! ``` 112 | //! 113 | //! # Backwards Compatibility 114 | //! ## 0.3.x -> 0.4.x 115 | //! ### Creating delegateable traits 116 | //! Delagatable trait macros `ambassador_impl_Trait` are no longer exported at the crate root, and 117 | //! are instead exported in the module where [`macro@delegatable_trait`] or 118 | //! [`macro@delegatable_trait_remote`] are used. If these traits are public then upgrading is also 119 | //! a breaking change for users of your library. The "backward_compatible" is also removed. 120 | //! ### Using delegateable traits 121 | //! Switching versions does not affect usages of delegateable traits 122 | //! ## 0.2.x -> 0.3.x 123 | //! Since delegateable traits from one crate can be used in anther crate backwards compatibility of switching to 0.3.x depends on the use case 124 | //! ### Self Contained Crate 125 | //! Switching to 0.3.x should just work, 126 | //! in this case it safe to disable the "backward_compatible" feature 127 | //! ### Library with public delegatable traits 128 | //! Make sure use the "backward_compatible" feature (enabled by default), 129 | //! this makes sure users of your library using an older version of ambassador aren't affected by the upgrade 130 | //! ### Users of a library with public delegatable traits 131 | //! Try to use the same version of ambassador as the library you're using 132 | 133 | extern crate core; 134 | extern crate proc_macro; 135 | 136 | mod delegate_shared; 137 | mod delegate_to_methods; 138 | mod derive; 139 | mod register; 140 | mod util; 141 | 142 | use proc_macro::TokenStream; 143 | use quote::quote; 144 | 145 | use crate::register::build_register_trait; 146 | 147 | /// Delegate the implementation of a trait to a struct field/enum variants by adding `#[derive(Delegate)]` and its associated attribute `#[delegate(Trait)]` to it: 148 | /// 149 | /// ``` 150 | /// use ambassador::{Delegate, delegatable_trait}; 151 | /// 152 | /// #[delegatable_trait] 153 | /// pub trait Shout { 154 | /// fn shout(&self, input: &str) -> String; 155 | /// } 156 | /// 157 | /// pub struct Cat; 158 | /// 159 | /// impl Shout for Cat { 160 | /// fn shout(&self, input: &str) -> String { 161 | /// format!("{} - meow!", input) 162 | /// } 163 | /// } 164 | /// 165 | /// #[derive(Delegate)] // <------- 166 | /// #[delegate(Shout)] // <-------- Delegate implementation of Shout to struct field 167 | /// pub struct WrappedCat(Cat); 168 | /// ``` 169 | /// 170 | ///#### `#[delegate(..., target = "foo")]` - `target` key 171 | /// 172 | /// For structs with multiple fields, the field that should act as delegation target can be specified via the `target` key: 173 | /// 174 | /// ``` 175 | /// # use ambassador::{Delegate, delegatable_trait}; 176 | /// # #[delegatable_trait] 177 | /// # pub trait Shout { 178 | /// # fn shout(&self, input: &str) -> String; 179 | /// # } 180 | /// # pub struct Cat; 181 | /// # 182 | /// # impl Shout for Cat { 183 | /// # fn shout(&self, input: &str) -> String { 184 | /// # format!("{} - meow!", input) 185 | /// # } 186 | /// # } 187 | /// 188 | /// #[derive(Delegate)] 189 | /// #[delegate(Shout, target = "foo")] // <-------- Delegate implementation of Shout to struct field .foo 190 | /// pub struct WrappedCats { 191 | /// foo: Cat, 192 | /// bar: Cat, 193 | /// } 194 | /// ``` 195 | /// This also works for tuple structs with multiple fields, by using their index as a target key: 196 | /// 197 | /// ``` 198 | /// # use ambassador::{Delegate, delegatable_trait}; 199 | /// # #[delegatable_trait] 200 | /// # pub trait Shout { 201 | /// # fn shout(&self, input: &str) -> String; 202 | /// # } 203 | /// # pub struct Cat; 204 | /// # 205 | /// # impl Shout for Cat { 206 | /// # fn shout(&self, input: &str) -> String { 207 | /// # format!("{} - meow!", input) 208 | /// # } 209 | /// # } 210 | /// 211 | /// #[derive(Delegate)] 212 | /// #[delegate(Shout, target = "1")] // <-------- Delegate implementation of Shout to second field 213 | /// pub struct WrappedCats(Cat, Cat); 214 | /// ``` 215 | /// 216 | /// #### `#[delegate(..., target = "self")]` - `target="self"` 217 | /// Types that implement all the methods of a trait without implementing the trait itself, 218 | /// can be made to implement that trait by setting `target="self"`. 219 | /// This doesn't work for traits with associated types and constants, and requires the where clause to be added explicitly (see `where` key). 220 | /// If the type doesn't actually implement the methods (possibly due to an incomplete `where` clause) this can cause a `[unconditional_recursion]` error. 221 | /// 222 | /// A possible use case of this is when refactoring some methods of a public type into a trait, 223 | /// the type still needs to implement the methods outside the trait for semver reasons, 224 | /// and using this feature reduces the boilderplate of implementing the trait with the same methods. 225 | /// 226 | /// 227 | /// ``` 228 | /// # use ambassador::{Delegate, delegatable_trait}; 229 | /// # #[delegatable_trait] 230 | /// # pub trait Shout { 231 | /// # fn shout(&self, input: &str) -> String; 232 | /// # } 233 | /// #[derive(Delegate)] 234 | /// #[delegate(Shout, target="self")] 235 | /// pub struct Cat; 236 | /// 237 | /// impl Cat { 238 | /// fn shout(&self, input: &str) -> String { 239 | /// format!("{} - meow!", input) 240 | /// } 241 | /// } 242 | /// ``` 243 | /// 244 | /// #### `#[delegate(..., where = "A: Debug")]` - `where` key 245 | /// 246 | /// To make a delegation apply only for certain generic bounds, similar to a [native where clause](https://doc.rust-lang.org/stable/rust-by-example/generics/where.html), you can specify a `where` attribute: 247 | /// 248 | /// A where clause is automatically applied that makes sure the target field implements the trait being delegated 249 | /// ``` 250 | /// # use ambassador::{Delegate, delegatable_trait}; 251 | /// # #[delegatable_trait] 252 | /// # pub trait Shout { 253 | /// # fn shout(&self, input: &str) -> String; 254 | /// # } 255 | /// use std::fmt::Debug; 256 | /// 257 | /// #[derive(Delegate)] 258 | /// #[delegate(Shout, where = "A: Debug")] // <---- Delegate implementation of Shout to .foo field if foo field implements Debug 259 | /// // "A: Shout" is automatically added 260 | /// pub struct WrappedFoo { 261 | /// foo: A, 262 | /// } 263 | /// ``` 264 | /// 265 | /// 266 | /// #### `#[delegate(Shout, generics = "X")]` - trait generics 267 | /// 268 | /// We can also delegate traits with generics. 269 | /// The type parameters listed in the `generics` key are treated as fully generic. 270 | /// The automatically added where clause ensures they are valid for the inner type being delegated to. 271 | /// Explict where clauses to further refine these types can be added as normal. 272 | /// 273 | /// ``` 274 | /// use ambassador::{delegatable_trait, Delegate}; 275 | /// use std::fmt::Display; 276 | /// 277 | /// #[delegatable_trait] 278 | /// pub trait Shout { 279 | /// fn shout(&self, input: T) -> String; 280 | /// } 281 | /// 282 | /// pub struct Cat; 283 | /// 284 | /// impl Shout for Cat { 285 | /// fn shout(&self, input: T) -> String { 286 | /// format!("{} - meow!", input) 287 | /// } 288 | /// } 289 | /// 290 | /// #[derive(Delegate)] 291 | /// #[delegate(Shout, generics = "X")] // <-------- `X` is fully generic 292 | /// // The automatic where clause ensures X: Display 293 | /// // We could also use #[delegate(Shout<& 'a str>, generics = "'a")] to only delegate for &str 294 | /// pub struct WrappedCat(Cat); 295 | /// ``` 296 | /// 297 | /// 298 | /// #### `#[delegate(Shout, automatic_where_clause = "false")]` - inhibit automatic generation of `where` clause. 299 | /// 300 | /// Normally `#[derive(Delegate)]` generates code to ensure that chosen field 301 | /// indeed implements the trait you want to implement for the container struct. 302 | /// 303 | /// For example, the `#[derive(Delegate)]` + `#[delegate(Shout, generics = "X")]` 304 | /// in the example above will emit code that requires `Cat` (the type we're 305 | /// delegating to) to implement `Shout`: `Cat: Shout`. 306 | /// 307 | /// However, you may want to delegate implementation to a type that does not in 308 | /// fact fully implement the trait in question but instead has _compatible 309 | /// methods_ that can be called as if the trait were in fact implemented. 310 | /// 311 | /// One notable examples of this is delegating a trait implementation to 312 | /// container types holding a trait object; i.e. `MyTrait` for 313 | /// `Box`. In this example, `Box` even though `Box` does not actually 317 | /// implement `MyTrait`. 318 | /// 319 | /// `automatic_where_clause = "false"` lets us create a delegated impl of 320 | /// `MyTrait` that takes advantage of this. 321 | /// 322 | /// ``` 323 | /// use ambassador::{delegatable_trait, Delegate}; 324 | /// use std::fmt::Display; 325 | /// 326 | /// #[delegatable_trait] 327 | /// pub trait Shout { 328 | /// fn shout(&self, input: &str) -> String; 329 | /// } 330 | /// 331 | /// pub struct Cat; 332 | /// 333 | /// impl Shout for Cat { 334 | /// fn shout(&self, input: &str) -> String { 335 | /// format!("{} - meow!", input) 336 | /// } 337 | /// } 338 | /// 339 | /// #[derive(Delegate)] 340 | /// #[delegate(Shout, automatic_where_clause = "false")] 341 | /// pub struct BoxedAnimal(pub Box); 342 | /// 343 | /// // Can accept both `Cat` and `BoxedAnimal`. 344 | /// fn recording_studio(voice_actor: S){} 345 | /// ``` 346 | /// 347 | /// Note that it is also possible to create such a delegated impl by making use 348 | /// of [`macro@delegate_to_remote_methods`] with [`Deref::deref`] and 349 | /// [`DerefMut::deref_mut`] as the target methods. The docs on 350 | /// [`macro@delegate_to_remote_methods`] contain an example of this. 351 | /// 352 | /// [`Deref::deref`]: core::ops::Deref::deref 353 | /// [`DerefMut::deref_mut`]: core::ops::DerefMut::deref_mut 354 | #[proc_macro_derive(Delegate, attributes(delegate))] 355 | pub fn delegate_macro(input: TokenStream) -> TokenStream { 356 | derive::delegate_macro(input) 357 | } 358 | 359 | /// Delegate the implementation of a trait to a type's methods by adding `#[delegate_to_methods]` and its associated attribute `#[delegate(Trait)]` to the relevant impl block 360 | /// 361 | /// #### `#[delegate(..., target_owned = "foo", target_ref = "bar", target_mut = "baz")]` - `target` keys 362 | /// Three different target methods can be specified depending on the receiver of of the trait method being delegated. 363 | /// These methods must have the signatures target_owned: "fn foo(self) -> X", target_ref: "fn bar(&self) -> &X", and target_mut: "fn baz(&mut self) -> &mut X" 364 | /// where X is the same type for all three. 365 | /// Excluding some of these attributes is allowed as long as the trait being delegated to doesn't have any methods with the relevant receiver 366 | /// Additional methods that don't have any of the relevant signature types may be included in the impl block as long as they are never used as targets. 367 | /// 368 | /// #### The `where` and `generics` keys described in [`Delegate`] are also supported and function the same way 369 | /// 370 | /// ``` 371 | /// use std::ops::{Deref, DerefMut}; 372 | /// use ambassador::{delegate_to_methods, delegatable_trait}; 373 | /// 374 | /// #[delegatable_trait] 375 | /// pub trait Shout { 376 | /// fn shout(&self, input: &str) -> String; 377 | /// } 378 | /// 379 | /// pub struct Cat; 380 | /// 381 | /// impl Shout for Cat { 382 | /// fn shout(&self, input: &str) -> String { 383 | /// format!("{} - meow!", input) 384 | /// } 385 | /// } 386 | /// 387 | /// pub struct BoxedCat(Box); // Target is hidden behind a box 388 | /// 389 | /// #[delegate_to_methods] 390 | /// #[delegate(Shout, target_ref = "inner", target_mut = "inner_mut")] 391 | /// impl BoxedCat { 392 | /// fn inner(&self) -> &Cat { 393 | /// self.0.deref() 394 | /// } 395 | /// 396 | /// // Note we don't need target_mut = "inner_mut" in this case but it is included as an example 397 | /// // The return type must be &mut Cat to match inner's return type &Cat 398 | /// fn inner_mut(&mut self) -> &mut Cat { 399 | /// self.0.deref_mut() 400 | /// } 401 | /// 402 | /// // You can also have extra methods here: 403 | /// fn another_one(&self) { } 404 | /// } 405 | /// ``` 406 | /// 407 | /// #### `delegate_to_methods` on an `impl Trait for ...` block 408 | /// 409 | /// It's also valid to use `delegate_to_methods` with a trait impl; i.e. to use 410 | /// the methods from a different trait to delegate a trait. For example: 411 | /// ``` 412 | /// # use std::ops::{Deref, DerefMut}; 413 | /// # use ambassador::{delegate_to_methods, delegatable_trait}; 414 | /// # #[delegatable_trait] 415 | /// # pub trait Shout { 416 | /// # fn shout(&self, input: &str) -> String; 417 | /// # } 418 | /// # pub struct Cat; 419 | /// # impl Shout for Cat { 420 | /// # fn shout(&self, input: &str) -> String { 421 | /// # format!("{} - meow!", input) 422 | /// # } 423 | /// # } 424 | /// 425 | /// pub struct RefCat<'a>(&'a Cat); 426 | /// 427 | /// #[delegate_to_methods] 428 | /// #[delegate(Shout, target_ref = "deref")] 429 | /// impl<'a> Deref for RefCat<'a> { 430 | /// type Target = Cat; 431 | /// 432 | /// fn deref(&self) -> &Cat { &self.0 } 433 | /// } 434 | /// ``` 435 | /// 436 | /// Note that this has a caveat: if the target methods you are delegating to 437 | /// share the same name as any of the methods in the trait being delegated you 438 | /// will likely get errors about ambiguity. For example: 439 | /// 440 | /// ```rust,compile_fail 441 | /// # use std::ops::{Deref, DerefMut}; 442 | /// # use ambassador::{delegate_to_methods, delegatable_trait}; 443 | /// # #[delegatable_trait] 444 | /// # pub trait Shout { 445 | /// # fn shout(&self, input: &str) -> String; 446 | /// # } 447 | /// # pub struct Cat; 448 | /// # impl Shout for Cat { 449 | /// # fn shout(&self, input: &str) -> String { 450 | /// # format!("{} - meow!", input) 451 | /// # } 452 | /// # } 453 | /// 454 | /// pub struct RefCat<'a>(&'a Cat); 455 | /// 456 | /// trait GetAShouter { 457 | /// type Shouter: Shout; 458 | /// 459 | /// fn shout(&self) -> &Self::Shouter; 460 | /// } 461 | /// 462 | /// #[delegate_to_methods] 463 | /// #[delegate(Shout, target_ref = "shout")] 464 | /// impl<'a> GetAShouter for RefCat<'a> { 465 | /// type Shouter = Cat; 466 | /// 467 | /// fn shout(&self) -> &Cat { &self.0 } 468 | /// } 469 | /// ``` 470 | /// 471 | /// Yields: 472 | /// ```text 473 | /// error[E0034]: multiple applicable items in scope 474 | /// --> src/lib.rs:363:32 475 | /// | 476 | /// 26 | #[delegate(Shout, target_ref = "shout")] 477 | /// | ^^^^^^^ multiple `shout` found 478 | /// | 479 | /// note: candidate #1 is defined in an impl of the trait `Shout` for the type `RefCat<'a>` 480 | /// --> src/lib.rs:345:5 481 | /// | 482 | /// 8 | fn shout(&self, input: &str) -> String; 483 | /// | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 484 | /// ... 485 | /// 25 | #[delegate_to_methods] 486 | /// | ---------------------- in this procedural macro expansion 487 | /// note: candidate #2 is defined in an impl of the trait `GetAShouter` for the type `RefCat<'a>` 488 | /// --> src/lib.rs:367:5 489 | /// | 490 | /// 30 | fn shout(&self) -> &Cat { &self.0 } 491 | /// | ^^^^^^^^^^^^^^^^^^^^^^^ 492 | /// = note: this error originates in the macro `ambassador_impl_Shout` (in Nightly builds, run with -Z macro-backtrace for more info) 493 | /// ``` 494 | /// 495 | /// This is not an issue when the target methods are _inherent_ since inherent 496 | /// methods explicitly [have priority over trait methods](https://dtolnay.github.io/rust-quiz/23) 497 | /// during method resolution. 498 | /// 499 | /// The workaround is to create wrapper inherent methods for the trait's methods: 500 | /// ``` 501 | /// # use std::ops::{Deref, DerefMut}; 502 | /// # use ambassador::{delegate_to_methods, delegatable_trait}; 503 | /// # #[delegatable_trait] 504 | /// # pub trait Shout { 505 | /// # fn shout(&self, input: &str) -> String; 506 | /// # } 507 | /// # pub struct Cat; 508 | /// # impl Shout for Cat { 509 | /// # fn shout(&self, input: &str) -> String { 510 | /// # format!("{} - meow!", input) 511 | /// # } 512 | /// # } 513 | /// 514 | /// pub struct RefCat<'a>(&'a Cat); 515 | /// 516 | /// trait GetAShouter { 517 | /// type Shouter: Shout; 518 | /// 519 | /// fn shout(&self) -> &Self::Shouter; 520 | /// } 521 | /// 522 | /// impl<'a> GetAShouter for RefCat<'a> { 523 | /// type Shouter = Cat; 524 | /// 525 | /// fn shout(&self) -> &Cat { &self.0 } 526 | /// } 527 | /// 528 | /// #[delegate_to_methods] 529 | /// #[delegate(Shout, target_ref = "get_a_shouter")] 530 | /// impl<'a> RefCat<'a> { fn get_a_shouter(&self) -> &Cat { GetAShouter::shout(self) } } 531 | /// ``` 532 | #[proc_macro_attribute] 533 | pub fn delegate_to_methods(_attr: TokenStream, input: TokenStream) -> TokenStream { 534 | delegate_to_methods::delegate_macro(input, true) 535 | } 536 | 537 | /// Delegate the implementation of a trait to methods on a type that are defined 538 | /// _elsewhere_. 539 | /// 540 | /// This macro is identical to [`macro@delegate_to_methods`] except that it does not 541 | /// actually produce an `impl` block on the type for the target methods; instead 542 | /// it assumes that the methods are implemented elsewhere. 543 | /// ``` 544 | /// use ambassador::{delegate_to_remote_methods, delegatable_trait}; 545 | /// 546 | /// #[delegatable_trait] 547 | /// pub trait Shout { 548 | /// fn shout(&self, input: &str) -> String; 549 | /// } 550 | /// 551 | /// pub struct Cat; 552 | /// 553 | /// impl Shout for Cat { 554 | /// fn shout(&self, input: &str) -> String { 555 | /// format!("{} - meow!", input) 556 | /// } 557 | /// } 558 | /// 559 | /// pub struct BoxedCat(Box); 560 | /// 561 | /// impl BoxedCat { 562 | /// fn inner(&self) -> &Cat { &self.0 } 563 | /// } 564 | /// 565 | /// #[delegate_to_remote_methods] 566 | /// #[delegate(Shout, target_ref = "inner")] 567 | /// impl BoxedCat { 568 | /// // `inner` can be defined anywhere: trait method or inherent method, in 569 | /// // this crate or elsewhere 570 | /// fn inner(&self) -> &Cat; 571 | /// } 572 | /// ``` 573 | /// 574 | /// As such, this macro will actually error if you provide it with methods with 575 | /// blocks, method signatures that aren't used by a `delegate` attribute on the 576 | /// impl, or other impl items: 577 | /// ```rust,compile_fail 578 | /// # use ambassador::{delegate_to_remote_methods, delegatable_trait}; 579 | /// # #[delegatable_trait] 580 | /// # pub trait Shout { fn shout(&self, input: &str) -> String; } 581 | /// # pub struct Cat; 582 | /// # impl Shout for Cat { 583 | /// # fn shout(&self, input: &str) -> String { format!("{} - meow!", input) } 584 | /// # } 585 | /// # pub struct BoxedCat(Box); 586 | /// # impl BoxedCat { fn inner(&self) -> &Cat { &self.0 } } 587 | /// 588 | /// #[delegate_to_remote_methods] 589 | /// #[delegate(Shout, target_ref = "inner")] 590 | /// impl BoxedCat { 591 | /// fn inner(&self) -> &Cat { &self.0 } // This is an error, method bodies 592 | /// // aren't accepted 593 | /// 594 | /// fn extra(&self); // Extra methods are also error since no 595 | /// // `impl BoxedCat { ... }` is actually 596 | /// // emitted 597 | /// 598 | /// const CONST: () = (); // This is also an error. 599 | /// } 600 | /// ``` 601 | /// 602 | /// Target methods can come from a trait or be inherent methods whose actual 603 | /// implementation lives elsewhere. You can mix and match: 604 | /// ``` 605 | /// # use ambassador::{delegate_to_remote_methods, delegatable_trait}; 606 | /// # #[delegatable_trait] 607 | /// # pub trait Shout { fn shout(&self, input: &str) -> String; } 608 | /// # pub struct Cat; 609 | /// # impl Shout for Cat { 610 | /// # fn shout(&self, input: &str) -> String { format!("{} - meow!", input) } 611 | /// # } 612 | /// # pub struct BoxedCat(Box); 613 | /// # impl BoxedCat { fn inner(&self) -> &Cat { &self.0 } } 614 | /// use std::ops::{Deref, DerefMut}; 615 | /// 616 | /// impl Deref for BoxedCat { 617 | /// type Target = Cat; 618 | /// 619 | /// fn deref(&self) -> &Self::Target { &self.0 } 620 | /// } 621 | /// 622 | /// impl DerefMut for BoxedCat { 623 | /// fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 } 624 | /// } 625 | /// 626 | /// #[delegate_to_remote_methods] 627 | /// #[delegate(Shout, target_ref = "inner", target_mut = "deref_mut")] 628 | /// impl BoxedCat { 629 | /// fn inner(&self) -> &Cat; 630 | /// fn deref_mut(&mut self) -> &mut Cat; 631 | /// } 632 | /// ``` 633 | /// Note that if you do use target methods from a trait, the trait must be in 634 | /// scope. 635 | /// 636 | /// Because this macro does not implement any inherent methods on the type being 637 | /// delegated to, the type can be remote (like with [`macro@delegate_remote`]): 638 | /// ``` 639 | /// # use ambassador::{delegate_to_remote_methods, delegatable_trait}; 640 | /// # #[delegatable_trait] 641 | /// # pub trait Shout { fn shout(&self, input: &str) -> String; } 642 | /// # pub struct Cat; 643 | /// # impl Shout for Cat { 644 | /// # fn shout(&self, input: &str) -> String { format!("{} - meow!", input) } 645 | /// # } 646 | /// use std::ops::{Deref, DerefMut}; 647 | /// 648 | /// // Note that this impl says `Deref for Box`. 649 | /// // 650 | /// // The trait in this impl is ignored and only serves as documentation here. 651 | /// #[delegate_to_remote_methods] 652 | /// #[delegate(Shout, target_ref = "deref")] 653 | /// impl Deref for Box { 654 | /// fn deref(&self) -> &Cat; 655 | /// } 656 | /// 657 | /// fn shout(_: &impl Shout) { } 658 | /// 659 | /// fn main() { 660 | /// let c = Cat; 661 | /// shout(&c); 662 | /// 663 | /// let boxed: Box = Box::new(c); 664 | /// shout(&boxed); 665 | /// } 666 | /// ``` 667 | /// 668 | /// This can be used in conjunction with generics to provide blanket delegated 669 | /// implementations of a local trait without needing to create intermediary 670 | /// local traits that provide target methods to use: 671 | /// ``` 672 | /// # use ambassador::{delegatable_trait, delegate_to_remote_methods}; 673 | /// # use std::ops::{Deref, DerefMut}; 674 | /// # #[delegatable_trait] 675 | /// # pub trait Shout { fn shout(&self, input: &str) -> String; } 676 | /// # pub struct Cat; 677 | /// # impl Shout for Cat { fn shout(&self, input: &str) -> String { format!("{} - meow!", input) } } 678 | /// use std::{sync::Arc, rc::Rc}; 679 | /// 680 | /// pub struct Dog; 681 | /// impl Shout for Dog { 682 | /// fn shout(&self, input: &str) -> String { format!("{} - wuff!", input) } 683 | /// } 684 | /// 685 | /// #[delegate_to_remote_methods] 686 | /// #[delegate(Shout, target_ref = "deref")] 687 | /// impl> T { 688 | /// fn deref(&self) -> &S; 689 | /// } 690 | /// 691 | /// pub fn shout(pet: &impl Shout) { println!("{}", pet.shout("hi")); } 692 | /// 693 | /// pub fn main() { 694 | /// shout(&Cat); 695 | /// shout(&Dog); 696 | /// 697 | /// let a: Box = Box::new(Cat); 698 | /// let b: Arc = Arc::new(Dog); 699 | /// let c: Rc = Rc::new(Cat); 700 | /// shout(&a); 701 | /// shout(&b); 702 | /// shout(&c); 703 | /// 704 | /// let d: Box = Box::new(Cat); 705 | /// shout(&d); 706 | /// } 707 | /// ``` 708 | #[proc_macro_attribute] 709 | pub fn delegate_to_remote_methods(_attr: TokenStream, input: TokenStream) -> TokenStream { 710 | delegate_to_methods::delegate_macro(input, false) 711 | } 712 | 713 | /// Make an existing type that lives outside you crate delegate traits to it's members 714 | /// 715 | /// This can be done by copy-pasting it's definition into your code under this attribute. 716 | /// 717 | /// If the type is a struct, not all the fields have to be public, only the ones being delegated to. 718 | /// 719 | /// ``` 720 | /// use ambassador::{delegate_remote, delegatable_trait}; 721 | /// 722 | /// #[delegatable_trait] 723 | /// pub trait Shout { 724 | /// fn shout(&self, input: &str) -> String; 725 | /// } 726 | /// 727 | /// pub struct Cat; 728 | /// 729 | /// impl Shout for Cat { 730 | /// fn shout(&self, input: &str) -> String { 731 | /// format!("{} - meow!", input) 732 | /// } 733 | /// } 734 | /// 735 | /// mod wrapped { 736 | /// pub struct WrappedAnimals { 737 | /// pub foo: A, 738 | /// pub bar: B, 739 | /// baz: u32, // private field 740 | /// } 741 | /// } 742 | /// 743 | /// use wrapped::*; 744 | /// 745 | /// #[delegate_remote] 746 | /// #[delegate(Shout, target = "bar")] 747 | /// struct WrappedAnimals { 748 | /// foo: A, 749 | /// bar: B, 750 | /// // We don't even have to include baz since we don't delegate to it 751 | /// } 752 | /// ``` 753 | #[proc_macro_attribute] 754 | pub fn delegate_remote(_attr: TokenStream, input: TokenStream) -> TokenStream { 755 | derive::delegate_macro(input) 756 | } 757 | 758 | /// Make a trait available for delegation 759 | /// 760 | /// This also makes your trait delegatable in other crates: 761 | /// 762 | /// ``` 763 | /// use ambassador::delegatable_trait; 764 | /// 765 | /// #[delegatable_trait] // <------- 766 | /// pub trait Shout { 767 | /// fn shout(&self, input: &str) -> String; 768 | /// } 769 | /// ``` 770 | #[proc_macro_attribute] 771 | pub fn delegatable_trait(_attr: TokenStream, item: TokenStream) -> TokenStream { 772 | let original_item: syn::ItemTrait = syn::parse(item).unwrap(); 773 | let register_trait = build_register_trait(&original_item); 774 | 775 | let expanded = quote! { 776 | #original_item 777 | 778 | #register_trait 779 | }; 780 | TokenStream::from(expanded) 781 | } 782 | 783 | /// Make an existing trait that lives outside you crate available for delegation. 784 | /// 785 | /// This can be done by copy-pasting the existing traits signature into your code under this attribute 786 | /// 787 | /// ``` 788 | /// use ambassador::{Delegate, delegatable_trait_remote}; 789 | /// use std::fmt::Display; 790 | /// 791 | /// #[delegatable_trait_remote] 792 | /// trait Display { 793 | /// fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>; 794 | /// } 795 | /// 796 | /// struct Cat; 797 | /// 798 | /// impl Display for Cat { 799 | /// fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error>{ 800 | /// f.write_str("Cat") 801 | /// } 802 | /// } 803 | /// 804 | /// #[derive(Delegate)] 805 | /// #[delegate(Display)] // <-------- Delegate implementation of Display to struct field 806 | /// pub struct WrappedCat(Cat); 807 | /// ``` 808 | #[proc_macro_attribute] 809 | pub fn delegatable_trait_remote(_attr: TokenStream, item: TokenStream) -> TokenStream { 810 | let original_item: syn::ItemTrait = syn::parse(item).unwrap(); 811 | let register_trait = build_register_trait(&original_item); 812 | 813 | TokenStream::from(register_trait) 814 | } 815 | -------------------------------------------------------------------------------- /ambassador/src/register.rs: -------------------------------------------------------------------------------- 1 | use crate::util::{error, process_results, receiver_type, ReceiverType}; 2 | use itertools::Itertools; 3 | use proc_macro2::{Delimiter, Ident, TokenStream, TokenTree}; 4 | use quote::{quote, ToTokens, TokenStreamExt}; 5 | use syn::spanned::Spanned; 6 | use syn::{ 7 | AttrStyle, Attribute, ConstParam, GenericParam, ItemTrait, LifetimeDef, TraitItem, 8 | TraitItemConst, TraitItemType, TypeParam, Visibility, 9 | }; 10 | 11 | pub(crate) fn macro_name(trait_ident: &Ident) -> Ident { 12 | quote::format_ident!("ambassador_impl_{}", trait_ident) 13 | } 14 | 15 | pub(crate) fn match_name(trait_ident: &Ident) -> Ident { 16 | quote::format_ident!("Match{}", trait_ident) 17 | } 18 | 19 | #[derive(Default)] 20 | struct CfgNames(Vec<(TokenStream, Ident)>); 21 | 22 | impl CfgNames { 23 | fn get_macro(&mut self, pred: TokenStream) -> &Ident { 24 | let fresh = self.0.len(); 25 | self.0.push((pred, quote::format_ident!("cfg{}", fresh))); 26 | &self.0.last().unwrap().1 27 | } 28 | 29 | fn definitions(&self, mod_name: &Ident, vis: &Visibility) -> TokenStream { 30 | let iter = self.0.iter().map(|(cfg, name)| { 31 | let base_macro_name = quote::format_ident!("_{}_{}", mod_name, name); 32 | quote! { 33 | #[cfg(#cfg)] 34 | #[doc(hidden)] 35 | #[macro_export] 36 | macro_rules! #base_macro_name { 37 | ($($t:tt)*) => {$($t)*}; 38 | } 39 | 40 | #[cfg(not(#cfg))] 41 | #[doc(hidden)] 42 | #[macro_export] 43 | macro_rules! #base_macro_name { 44 | ($($t:tt)*) => {}; 45 | } 46 | 47 | pub use #base_macro_name as #name; 48 | } 49 | }); 50 | quote! { 51 | #[doc(hidden)] 52 | #[allow(non_snake_case)] 53 | #vis mod #mod_name { 54 | #(#iter)* 55 | } 56 | } 57 | } 58 | } 59 | 60 | pub fn build_register_trait(original_item: &ItemTrait) -> TokenStream { 61 | let trait_ident = &original_item.ident; 62 | let macro_name = macro_name(trait_ident); 63 | let macro_def = quote::format_ident!("_{}", macro_name); 64 | let match_name = match_name(trait_ident); 65 | let gen_params = &original_item.generics.params; 66 | let gen_idents: Vec<_> = gen_params.iter().map(param_to_ident).collect(); 67 | let gen_matcher: TokenStream = gen_params.iter().map(param_to_matcher).collect(); 68 | 69 | let mut cfg_names = CfgNames::default(); 70 | let iter = original_item 71 | .items 72 | .iter() 73 | .map(|item| build_trait_items(item, trait_ident, &gen_idents, &mut cfg_names)); 74 | let (struct_items, enum_items, self_items): (Vec<_>, Vec<_>, Vec<_>) = 75 | match process_results(iter, |iter| iter.multiunzip()) { 76 | Ok(tup) => tup, 77 | Err(err) => return err.to_compile_error(), 78 | }; 79 | let assoc_ty_bounds = make_assoc_ty_bound(&original_item.items, original_item, &match_name); 80 | let gen_idents_pat: TokenStream = gen_idents.into_iter().map(|id| quote! {$ #id ,}).collect(); 81 | let vis = &original_item.vis; 82 | let cfg_definitions = cfg_names.definitions(¯o_name, vis); 83 | quote! { 84 | #[macro_export] 85 | #[doc(hidden)] 86 | macro_rules! #macro_def { 87 | (body_struct(<#gen_matcher>, $ty:ty, $field_ident:tt)) => { 88 | #macro_name!{body_struct(<#gen_idents_pat>, $ty, ($field_ident), ($field_ident), ($field_ident))} 89 | }; 90 | (body_struct(<#gen_matcher>, $ty:ty, ($($ident_owned:tt)*), ($($ident_ref:tt)*), ($($ident_ref_mut:tt)*))) => { 91 | #(#struct_items)* 92 | }; 93 | (check_non_empty($err:literal, $s:ident.$($t:tt)+)) => {$s.$($t)*}; 94 | (check_non_empty($err:literal, $s:ident.)) => { 95 | compile_error! {$err} 96 | }; 97 | (body_enum(<#gen_matcher>, $ty:ty, ($( $other_tys:ty ),*), ($( $variants:path ),+))) => { 98 | #(#enum_items)* 99 | }; 100 | (body_self(<#gen_matcher>)) => { 101 | #(#self_items)* 102 | }; 103 | (use_assoc_ty_bounds) => { 104 | #assoc_ty_bounds 105 | }; 106 | } 107 | 108 | #[doc(inline)] 109 | #[doc = concat!("A macro to be used by [`ambassador::Delegate`] to delegate [`", stringify!(#trait_ident), "`]")] 110 | #vis use #macro_def as #macro_name; 111 | 112 | #cfg_definitions 113 | } 114 | } 115 | 116 | fn param_to_ident(param: &GenericParam) -> &Ident { 117 | match param { 118 | GenericParam::Type(TypeParam { ident, .. }) => ident, 119 | GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => &lifetime.ident, 120 | GenericParam::Const(ConstParam { ident, .. }) => ident, 121 | } 122 | } 123 | 124 | fn param_to_matcher(param: &GenericParam) -> TokenStream { 125 | match param { 126 | GenericParam::Type(TypeParam { ident, .. }) => quote!($ #ident : ty,), 127 | GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => { 128 | let ident = &lifetime.ident; 129 | quote!($ #ident : lifetime,) 130 | } 131 | GenericParam::Const(ConstParam { ident, .. }) => quote!($ #ident : expr,), 132 | } 133 | } 134 | 135 | fn param_to_tokens(param: &GenericParam) -> TokenStream { 136 | match param { 137 | GenericParam::Type(TypeParam { ident, .. }) => quote!(#ident,), 138 | GenericParam::Lifetime(LifetimeDef { lifetime, .. }) => quote!(#lifetime,), 139 | GenericParam::Const(ConstParam { ident, .. }) => quote!(#ident,), 140 | } 141 | } 142 | 143 | fn make_assoc_ty_bound( 144 | items: &[TraitItem], 145 | item_trait: &ItemTrait, 146 | match_name: &Ident, 147 | ) -> TokenStream { 148 | let trait_ident = &item_trait.ident; 149 | let gen_params = &item_trait.generics.params; 150 | let gen_params_t = super::util::TailingPunctuated(gen_params); 151 | 152 | let gen_tokens: TokenStream = gen_params.iter().flat_map(param_to_tokens).collect(); 153 | let assoc_type_bounds: Vec<_> = items.iter() 154 | .filter_map(|item| match item { 155 | TraitItem::Type(ty) => { 156 | let ty_ident = &ty.ident; 157 | Some(match ty.generics.params.len() { 158 | 0 => quote! {#ty_ident = >::#ty_ident}, 159 | _ => quote! {compile_error!("Enums cannot currently delegate to traits with GATs")} 160 | }) 161 | }, 162 | _ => None, 163 | }).collect(); 164 | let new_bound = quote! {#trait_ident<#gen_tokens #(#assoc_type_bounds,)*>}; 165 | 166 | quote! { 167 | #[doc(hidden)] 168 | #[allow(non_camel_case_types)] 169 | pub trait #match_name<#gen_params_t ambassador_X: #trait_ident<#gen_tokens>>: #new_bound {} 170 | #[allow(non_camel_case_types)] 171 | impl<#gen_params_t ambassador_X: #trait_ident<#gen_tokens>, ambassador_Y: #new_bound> #match_name<#gen_tokens ambassador_X> for ambassador_Y {} // Replace with trait alias when they become stable 172 | } 173 | } 174 | 175 | // Replaces the identifiers in gen_idents with their macro args (X => $X) ('a => $a) 176 | fn replace_gen_idents(tokens: TokenStream, gen_idents: &[&Ident]) -> TokenStream { 177 | let mut res = TokenStream::new(); 178 | let mut apostrophe: Option = None; 179 | for tt in tokens { 180 | match tt { 181 | TokenTree::Group(g) => res.append(proc_macro2::Group::new( 182 | g.delimiter(), 183 | replace_gen_idents(g.stream(), gen_idents), 184 | )), 185 | TokenTree::Ident(ref id) if gen_idents.contains(&id) => { 186 | apostrophe = None; 187 | res.append(TokenTree::Punct(proc_macro2::Punct::new( 188 | '$', 189 | proc_macro2::Spacing::Joint, 190 | ))); 191 | res.append(tt) 192 | } 193 | TokenTree::Punct(p) if p.as_char() == '\'' => apostrophe = Some(p), 194 | _ => { 195 | if let Some(ap) = apostrophe.take() { 196 | res.append(ap); 197 | } 198 | res.append(tt) 199 | } 200 | } 201 | } 202 | res 203 | } 204 | fn build_method( 205 | method_sig: &TokenStream, 206 | method_invocation: TokenStream, 207 | extr_attrs: TokenStream, 208 | ) -> TokenStream { 209 | quote! { 210 | #[inline] 211 | #[allow(unused_braces)] 212 | #extr_attrs 213 | #method_sig { 214 | #method_invocation 215 | } 216 | } 217 | } 218 | 219 | fn extract_cfg(attrs: &[Attribute]) -> Option { 220 | let mut iter = attrs 221 | .iter() 222 | .filter(|attr| attr.style == AttrStyle::Outer && attr.path.is_ident("cfg")) 223 | .filter_map(|attr| match attr.tokens.clone().into_iter().next()? { 224 | TokenTree::Group(x) if x.delimiter() == Delimiter::Parenthesis => Some(x.stream()), 225 | _ => None, 226 | }); 227 | let e0 = iter.next()?; 228 | if let Some(e1) = iter.next() { 229 | let iter = IntoIterator::into_iter([e0, e1]).chain(iter); 230 | Some(quote!(all(#(#iter)*))) 231 | } else { 232 | Some(e0) 233 | } 234 | } 235 | 236 | fn build_trait_items( 237 | original_item: &TraitItem, 238 | trait_ident: &Ident, 239 | gen_idents: &[&Ident], 240 | cfg_names: &mut CfgNames, 241 | ) -> syn::Result<(TokenStream, TokenStream, TokenStream)> { 242 | let gen_pat: TokenStream = gen_idents.iter().flat_map(|id| quote! {$#id,}).collect(); 243 | let macro_name = macro_name(trait_ident); 244 | let mut res = match original_item { 245 | TraitItem::Const(TraitItemConst { ident, ty, .. }) => ( 246 | quote! { 247 | const #ident : #ty = <$ty as #trait_ident<#gen_pat>>::#ident; 248 | }, 249 | quote! { 250 | const #ident : #ty = { 251 | $(assert!(<$ty as #trait_ident<#gen_pat>>::#ident == <$other_tys as #trait_ident<#gen_pat>>::#ident);)* 252 | <$ty as #trait_ident<#gen_pat>>::#ident 253 | }; 254 | }, 255 | quote! {compile_error!("trg=\"self\" is not allowed with associated constants")}, 256 | ), 257 | TraitItem::Type(TraitItemType { 258 | ident, generics, .. 259 | }) => { 260 | let where_clause = &generics.where_clause; 261 | let item = quote! { 262 | type #ident #generics = <$ty as #trait_ident<#gen_pat>>::#ident #generics #where_clause; 263 | }; 264 | ( 265 | item.clone(), 266 | item, 267 | quote! {compile_error!("trg=\"self\" is not allowed with associated types")}, 268 | ) 269 | } 270 | TraitItem::Method(original_method) => { 271 | let method_sig = original_method.sig.to_token_stream(); 272 | let method_sig = replace_gen_idents(method_sig, gen_idents); 273 | ( 274 | { 275 | let field_ident = match receiver_type(&original_method.sig)? { 276 | ReceiverType::Owned => { 277 | quote!(#macro_name!(check_non_empty( 278 | "target_owned was not specified but was needed", 279 | self.$($ident_owned)*))) 280 | } 281 | ReceiverType::Ref => { 282 | quote!(#macro_name!(check_non_empty( 283 | "target_ref was not specified but was needed", 284 | self.$($ident_ref)*))) 285 | } 286 | ReceiverType::MutRef => { 287 | quote!(#macro_name!(check_non_empty( 288 | "target_mut was not specified but was needed", 289 | self.$($ident_ref_mut)*))) 290 | } 291 | }; 292 | let method_invocation = build_method_invocation(original_method, &field_ident); 293 | build_method(&method_sig, method_invocation, quote!()) 294 | }, 295 | { 296 | let method_invocation = 297 | build_method_invocation(original_method, "e!(inner)); 298 | let method_invocation = quote! { 299 | match self { 300 | $($variants(inner) => #method_invocation),* 301 | } 302 | }; 303 | build_method(&method_sig, method_invocation, quote!()) 304 | }, 305 | { 306 | let method_invocation = build_method_invocation(original_method, "e!(self)); 307 | build_method( 308 | &method_sig, 309 | method_invocation, 310 | quote!(#[deny(unconditional_recursion)]), 311 | ) 312 | }, 313 | ) 314 | } 315 | _ => return error!(original_item.span(), "unsupported trait item"), 316 | }; 317 | let attrs: &[Attribute] = match original_item { 318 | TraitItem::Const(c) => &c.attrs, 319 | TraitItem::Type(t) => &t.attrs, 320 | TraitItem::Method(m) => &m.attrs, 321 | _ => &[], 322 | }; 323 | if let Some(pred) = extract_cfg(attrs) { 324 | let wrapper_macro = cfg_names.get_macro(pred); 325 | let wrap = |x: TokenStream| quote!(#macro_name::#wrapper_macro!{#x}); 326 | res = (wrap(res.0), wrap(res.1), wrap(res.2)); 327 | } 328 | Ok(res) 329 | } 330 | 331 | fn build_method_invocation( 332 | original_method: &syn::TraitItemMethod, 333 | field_ident: &TokenStream, 334 | ) -> TokenStream { 335 | let method_sig = &original_method.sig; 336 | let method_ident = &method_sig.ident; 337 | let argument_list: syn::punctuated::Punctuated<&Box, syn::token::Comma> = method_sig 338 | .inputs 339 | .iter() 340 | .filter_map(|fn_arg| match fn_arg { 341 | syn::FnArg::Receiver(_) => None, 342 | syn::FnArg::Typed(pat_type) => Some(&pat_type.pat), 343 | }) 344 | .collect(); 345 | 346 | let generics = method_sig.generics.params.iter().filter_map(|x| match x { 347 | GenericParam::Type(t) => Some(&t.ident), 348 | GenericParam::Lifetime(_) => None, 349 | GenericParam::Const(c) => Some(&c.ident), 350 | }); 351 | let post = if original_method.sig.asyncness.is_some() { 352 | quote!(.await) 353 | } else { 354 | quote!() 355 | }; 356 | 357 | let method_invocation = 358 | quote! { #field_ident.#method_ident::<#(#generics,)*>(#argument_list) #post }; 359 | method_invocation 360 | } 361 | -------------------------------------------------------------------------------- /ambassador/src/util.rs: -------------------------------------------------------------------------------- 1 | use proc_macro2::TokenStream; 2 | use quote::ToTokens; 3 | use std::fmt::{Display, Formatter}; 4 | use syn::punctuated::Punctuated; 5 | use syn::spanned::Spanned; 6 | use syn::{Receiver, Result}; 7 | 8 | macro_rules! error { 9 | ($span:expr, $($rest:expr),*) => {Err(syn::parse::Error::new($span, format_args!($($rest),*)))}; 10 | } 11 | 12 | /// Like the '?' operator but returns Some(err) instead of err 13 | macro_rules! try_option { 14 | ($result:expr) => { 15 | match $result { 16 | Ok(ok) => ok, 17 | Err(err) => return Some(Err(err)), 18 | } 19 | }; 20 | } 21 | 22 | pub(crate) use {error, try_option}; 23 | 24 | pub(crate) struct ProcessResult<'a, T, I: Iterator>> { 25 | iter: I, 26 | err: &'a mut Option, 27 | } 28 | 29 | fn add_err(old_err: &mut Option, err: syn::Error) { 30 | match old_err { 31 | Some(old_err) => old_err.combine(err), 32 | None => *old_err = Some(err), 33 | } 34 | } 35 | 36 | impl<'a, T, I: Iterator>> Iterator for ProcessResult<'a, T, I> { 37 | type Item = T; 38 | 39 | fn next(&mut self) -> Option { 40 | loop { 41 | match self.iter.next() { 42 | None => return None, 43 | Some(Ok(x)) => return Some(x), 44 | Some(Err(err)) => add_err(self.err, err), 45 | } 46 | } 47 | } 48 | 49 | fn fold(self, init: B, mut f: F) -> B 50 | where 51 | Self: Sized, 52 | F: FnMut(B, Self::Item) -> B, 53 | { 54 | let self_err = self.err; 55 | self.iter.fold(init, |acc, elt| match elt { 56 | Ok(elt) => f(acc, elt), 57 | Err(err) => { 58 | add_err(self_err, err); 59 | acc 60 | } 61 | }) 62 | } 63 | } 64 | 65 | /// Similar to [`Itertools::process_results`] but runs iter to completion and combines errors 66 | pub(crate) fn process_results(iter: I, f: F) -> Result 67 | where 68 | I: Iterator>, 69 | F: FnOnce(ProcessResult) -> R, 70 | { 71 | let mut err = None; 72 | let pr = ProcessResult { 73 | iter, 74 | err: &mut err, 75 | }; 76 | let res = f(pr); 77 | match err { 78 | None => Ok(res), 79 | Some(err) => Err(err), 80 | } 81 | } 82 | 83 | pub(crate) struct TailingPunctuated<'a, T, P>(pub(crate) &'a Punctuated); 84 | 85 | impl<'a, T, P: Default + ToTokens> ToTokens for TailingPunctuated<'a, T, P> 86 | where 87 | Punctuated: ToTokens, 88 | { 89 | fn to_tokens(&self, tokens: &mut TokenStream) { 90 | self.0.to_tokens(tokens); 91 | if !self.0.empty_or_trailing() { 92 | P::default().to_tokens(tokens) 93 | } 94 | } 95 | } 96 | #[derive(Copy, Clone, Eq, PartialEq)] 97 | pub(crate) enum ReceiverType { 98 | Owned, 99 | Ref, 100 | MutRef, 101 | } 102 | 103 | impl Display for ReceiverType { 104 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 105 | match self { 106 | ReceiverType::Owned => write!(f, "\"self\""), 107 | ReceiverType::Ref => write!(f, "\"&self\""), 108 | ReceiverType::MutRef => write!(f, "\"&mut self\""), 109 | } 110 | } 111 | } 112 | 113 | fn receiver_type_inner(r: &Receiver) -> ReceiverType { 114 | if r.reference.is_none() { 115 | ReceiverType::Owned 116 | } else if r.mutability.is_none() { 117 | ReceiverType::Ref 118 | } else { 119 | ReceiverType::MutRef 120 | } 121 | } 122 | 123 | pub(crate) fn receiver_type(sig: &syn::Signature) -> Result { 124 | match sig.receiver() { 125 | Some(syn::FnArg::Receiver(r)) => Ok(receiver_type_inner(r)), 126 | Some(syn::FnArg::Typed(t)) => error!( 127 | t.span(), 128 | "method's receiver type is not supported (must one of self, &self, or &mut self)" 129 | ), 130 | None => error!(sig.paren_token.span, "method must have a receiver"), 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/delegatable_trait.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Cry { 7 | fn shout() -> String; //~ method must have a receiver 8 | 9 | fn shout2(self: Box); //~ method's receiver type is not supported 10 | } 11 | 12 | pub fn main() {} 13 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/delegate_self_fail.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_remote, Delegate}; 4 | use std::borrow::Borrow; 5 | use std::cmp::{Eq, Ord}; 6 | use std::collections::{BTreeMap, HashMap}; 7 | use std::hash::Hash; 8 | 9 | #[delegatable_trait] 10 | pub trait Map { 11 | type K; 12 | type V; 13 | } 14 | 15 | #[delegatable_trait] 16 | pub trait Get: Map { 17 | fn get(&self, k: &Q) -> Option<&Self::V>; //~ ERROR function cannot return without recursing [unconditional_recursion] 18 | } 19 | 20 | impl Map for HashMap { 21 | type K = K; 22 | type V = V; 23 | } 24 | 25 | impl Map for BTreeMap { 26 | type K = K; 27 | type V = V; 28 | } 29 | 30 | #[delegate_remote] 31 | #[delegate(Get, target = "self", generics = "X", where = "K: Hash + Eq + Borrow, X: Hash + Eq + ?Sized")] //Forgot S: BuildHasher 32 | struct HashMap(); 33 | 34 | #[delegate_remote] 35 | #[delegate(Get, target = "self", generics = "X", where = "K: Ord + Borrow, X: Ord + ?Sized")] 36 | struct BTreeMap(); 37 | 38 | #[derive(Delegate)] 39 | #[delegate(Map)] 40 | #[delegate(Get, generics = "X", where = "X: ?Sized, A: Map, B: Map")] 41 | pub enum Either { 42 | Left(A), 43 | Right(B), 44 | } 45 | 46 | pub fn main() { 47 | let my_map: Either, BTreeMap<&'static str, u32>> = 48 | Either::Left([("a", 1)].into()); 49 | println!("{:?}", my_map.get("a")); 50 | } 51 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/delegate_to_method.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn into_u32(self) -> u32; 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { 14 | *self 15 | } 16 | 17 | fn into_u32(self) -> u32 { 18 | self 19 | } 20 | } 21 | 22 | struct Wrap(X); 23 | 24 | #[delegate_to_methods] 25 | #[delegate(MyTrait, target_owned = "inner", target_ref = "inner_ref")] 26 | //~^ Error target methods have different return types 27 | #[delegate(MyTrait, target_owned = "inner_ref", target_ref = "inner_ref")] 28 | //~^ Error method needs to have a receiver of type "self" 29 | #[delegate(MyTrait, target_owned = "inner", target_ref = "fail1")] 30 | //~^ Error Note: method used in #[delegate] attribute 31 | #[delegate(MyTrait)] //~ Error no targets were specified 32 | impl Wrap 33 | where 34 | X: DerefMut + Into, 35 | X::Target: Sized, 36 | { 37 | fn inner(self) -> u32 { 38 | //~^ Error Note: other return type defined here 39 | self.0.into() 40 | } 41 | 42 | fn inner_ref(&self) -> &X::Target { 43 | //~^ Error Note: first return type defined here 44 | self.0.deref() 45 | } 46 | 47 | fn inner_mut(&mut self) -> &mut X::Target { 48 | self.0.deref_mut() 49 | } 50 | 51 | fn fail1() -> u32 { 52 | //~^ method must have a receiver 53 | 5 54 | } 55 | 56 | fn fail2() -> u32 { 57 | 5 58 | } 59 | } 60 | 61 | fn main() {} 62 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/delegate_to_method_where.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | fn into_u32(self) -> u32; 11 | } 12 | 13 | impl MyTrait for u32 { 14 | fn get(&self) -> u32 { 15 | *self 16 | } 17 | 18 | fn change(&mut self) { 19 | *self += 1 20 | } 21 | 22 | fn into_u32(self) -> u32 { 23 | self 24 | } 25 | } 26 | 27 | struct Wrap(X); 28 | 29 | #[delegate_to_methods] 30 | #[delegate( 31 | MyTrait, 32 | target_owned = "inner", 33 | target_ref = "inner_ref", 34 | target_mut = "inner_mut" 35 | )] 36 | impl Wrap 37 | where 38 | X: DerefMut + Into, 39 | X::Target: Sized, 40 | { 41 | fn inner(self) -> X::Target { 42 | self.0.into() 43 | } 44 | 45 | fn inner_ref(&self) -> &X::Target { 46 | self.0.deref() 47 | } 48 | 49 | fn inner_mut(&mut self) -> &mut X::Target { 50 | self.0.deref_mut() 51 | } 52 | } 53 | 54 | fn main() { 55 | let x = Wrap(Box::new(42u32)); 56 | assert_eq!(x.into_u32(), 42) //~ ERROR the method `into_u32` exists for struct `Wrap>`, but its trait bounds were not satisfied 57 | } 58 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/delegate_to_missing_method.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] //~ ERROR target_owned was not specified but was needed 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | fn into_u32(self) -> u32; 11 | } 12 | 13 | impl MyTrait for u32 { 14 | fn get(&self) -> u32 { 15 | *self 16 | } 17 | 18 | fn change(&mut self) { 19 | *self += 1 20 | } 21 | 22 | fn into_u32(self) -> u32 { 23 | self 24 | } 25 | } 26 | 27 | struct Wrap(X); 28 | 29 | #[delegate_to_methods] 30 | #[delegate(MyTrait, target_ref = "inner_ref", target_mut = "inner_mut")] 31 | impl Wrap 32 | where 33 | X: DerefMut, 34 | { 35 | fn inner_ref(&self) -> &X::Target { 36 | self.0.deref() 37 | } 38 | 39 | fn inner_mut(&mut self) -> &mut X::Target { 40 | self.0.deref_mut() 41 | } 42 | } 43 | 44 | fn main() { 45 | let mut x = Wrap(Box::new(42u32)); 46 | assert_eq!(x.get(), 42); 47 | x.change(); 48 | assert_eq!(x.into_u32(), 43); 49 | } 50 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/enum_associated_constant.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | 5 | #[delegatable_trait] //~ ERROR evaluation of constant value failed [E0080] 6 | pub trait Legs { 7 | const NUM_LEGS: usize; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Legs for Cat { 13 | const NUM_LEGS: usize = 4; 14 | } 15 | 16 | pub struct Bird; 17 | 18 | impl Legs for Bird { 19 | const NUM_LEGS: usize = 2; 20 | } 21 | 22 | #[derive(Delegate)] 23 | #[delegate(Legs)] 24 | pub enum Pet { 25 | Cat(Cat), 26 | Bird(Bird), 27 | } 28 | 29 | fn main() { 30 | println!("{}", Pet::NUM_LEGS) 31 | } 32 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/enum_associated_types.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | use std::any::type_name; 5 | 6 | #[delegatable_trait] 7 | pub trait Animal { 8 | type Baby; 9 | } 10 | 11 | pub struct Kitten; 12 | pub struct Cat; 13 | pub struct Puppy; 14 | pub struct Dog; 15 | 16 | impl Animal for Puppy { 17 | type Baby = Puppy; 18 | } 19 | 20 | impl Animal for Dog { 21 | type Baby = Puppy; 22 | } 23 | 24 | impl Animal for Kitten { 25 | type Baby = Kitten; 26 | } 27 | 28 | impl Animal for Cat { 29 | type Baby = Kitten; 30 | } 31 | 32 | 33 | #[derive(Delegate)] 34 | #[delegate(Animal)] 35 | pub enum Either { 36 | A(A), 37 | B(B), 38 | } 39 | 40 | pub fn main() { 41 | println!("{:?}", type_name::< as Animal>::Baby>()); 42 | //~^ ERROR type mismatch resolving `::Baby == Puppy` 43 | } 44 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/extra_args_in_delegate_attr.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | use ambassador::{delegatable_trait, Delegate}; 3 | 4 | #[delegatable_trait] 5 | pub trait Shout { 6 | fn shout(self) -> String; 7 | } 8 | 9 | pub struct Cat; 10 | 11 | impl Shout for Cat { 12 | fn shout(self) -> String { 13 | "meow!".to_owned() 14 | } 15 | } 16 | 17 | #[derive(Delegate)] 18 | #[delegate(Shout, whoops = )] //~ ERROR unexpected end of input, expected string literal 19 | pub struct Boxed(Cat); 20 | 21 | pub fn main() {} 22 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/extra_items_in_remote_methods_impl.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_remote_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { 14 | *self 15 | } 16 | fn change(&mut self) { 17 | *self += 1 18 | } 19 | } 20 | 21 | #[delegate_to_remote_methods] 22 | #[delegate(MyTrait, target_ref = "deref", target_mut = "deref_mut")] 23 | //~^ no method named `deref` found for reference `&Box` in the current scope 24 | impl Box { 25 | fn deref(&self) -> &X; 26 | fn deref_mut(&mut self) -> &mut X; 27 | 28 | fn unused_signature(&self) -> &X; //~ ERROR This method is not used by any `delegate` attributes; please remove it 29 | fn bad_signature(&self); //~ ERROR delegated to methods must return 30 | 31 | fn method_with_body(&mut self) -> &mut X { 32 | //~^ ERROR Only method signatures are allowed here (no blocks!) 33 | //~^^ ERROR This method is not used by any `delegate` attributes; please remove it 34 | todo!() 35 | } 36 | 37 | const ASSOCIATED_CONST: () = (); 38 | //~^ ERROR Only method signatures are allowed here, everything else is discarded 39 | 40 | type SomeType = u8; 41 | //~^ ERROR Only method signatures are allowed here, everything else is discarded 42 | } 43 | 44 | fn main() {} 45 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/generic_trait_bad_where.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | use std::collections::{BTreeMap, HashMap}; 5 | use std::ops::Index; 6 | 7 | #[delegatable_trait_remote] 8 | pub trait Index { 9 | type Output: ?Sized; 10 | fn index(&self, index: Idx) -> &Self::Output; 11 | } 12 | 13 | #[derive(Delegate)] 14 | #[delegate(Index, generics = "X", where = "X: Into")] 15 | pub enum SomeMap { 16 | Hash(HashMap), 17 | BTree(BTreeMap), 18 | } 19 | 20 | fn main() { 21 | let m1 = SomeMap::Hash(HashMap::from([("dog".to_string(), "wuff")])); 22 | let m2 = SomeMap::BTree(BTreeMap::from([("cat".to_string(), "meow")])); 23 | assert_eq!(m1["dog"], "wuff"); 24 | //~^ ERROR the trait bound `&_: Into` is not satisfied 25 | assert_eq!(m2["cat"], "meow"); 26 | //~^ ERROR the trait bound `&_: Into` is not satisfied 27 | } 28 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/missing_delegate_attribute.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] //~ ERROR No #[delegate] attribute specified 27 | pub enum Animals { 28 | Cat(Cat), 29 | Dog(Dog), 30 | } 31 | 32 | pub fn main() { 33 | let foo_animal = Animals::Cat(Cat); 34 | println!("{}", foo_animal.shout("BAR")); //~ ERROR no method named `shout` found for enum `Animals` in the current scope 35 | } 36 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/muti_field_struct.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | //~^ Error "target" value on #[delegate] attribute has to be specified for structs with multiple fields 29 | #[delegate(Shout target = "1")] //~ Error expected `,` 30 | #[delegate(Shout, target "1")] //~ Error expected `=` 31 | #[delegate(Shout, not_target = "1")] //~ Error invalid key for a delegate attribute 32 | #[delegate(Shout, target = "0+1")] //~ Error unexpected token 33 | #[delegate(Shout, target = "2")] //~ Error Unknown field specified as "target" value in #[delegate] attribute 34 | #[delegate(Shout, target = "foo")] //~ Error Unknown field specified as "target" value in #[delegate] attribute 35 | pub struct WrappedAnimals(Cat, Dog); 36 | 37 | pub fn main() {} 38 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/single_field_struct.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout, target = "foo")] //~ Error Unknown field specified as "target" value in #[delegate] attribute 20 | pub struct WrappedAnimals(Cat); 21 | 22 | pub fn main() {} 23 | -------------------------------------------------------------------------------- /ambassador/tests/compile-fail/taxonomy_fail.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | use std::any::type_name; 5 | 6 | #[delegatable_trait] 7 | pub trait Taxonomy { 8 | type Res; 9 | } 10 | 11 | pub struct Cat; 12 | pub struct Dog; 13 | pub struct Alligator; 14 | 15 | pub struct Class; 16 | pub struct Mammal; 17 | pub struct Reptile; 18 | 19 | pub struct Kingdom; 20 | pub struct Animal; 21 | 22 | pub trait Base {} 23 | 24 | impl Base for Cat {} 25 | impl Taxonomy for Cat { 26 | type Res = Mammal; 27 | } 28 | 29 | impl Base for Dog {} 30 | impl Taxonomy for Dog { 31 | type Res = Mammal; 32 | } 33 | 34 | impl Base for Alligator {} 35 | impl Taxonomy for Alligator { 36 | type Res = Reptile; 37 | } 38 | 39 | impl Taxonomy for Mammal { 40 | type Res = Animal; 41 | } 42 | 43 | impl Taxonomy for Reptile { 44 | type Res = Animal; 45 | } 46 | 47 | impl> Taxonomy for E 48 | where 49 | >::Res: Taxonomy, 50 | { 51 | type Res = <>::Res as Taxonomy>::Res; 52 | } 53 | 54 | #[derive(Delegate)] 55 | #[delegate(Taxonomy, generics = "X")] 56 | pub enum Either { 57 | A(A), 58 | B(B), 59 | } 60 | 61 | pub fn main() { 62 | assert_eq!( 63 | type_name::< as Taxonomy>::Res>(), 64 | //~^ ERROR type mismatch resolving `>::Res == Reptile` 65 | type_name::() 66 | ); 67 | } 68 | -------------------------------------------------------------------------------- /ambassador/tests/compiletest.rs: -------------------------------------------------------------------------------- 1 | extern crate compiletest_rs as compiletest; 2 | 3 | use std::path::PathBuf; 4 | 5 | fn run_mode(mode: &'static str) { 6 | let mut config = compiletest::Config::default(); 7 | 8 | config.mode = mode.parse().expect("Invalid mode"); 9 | config.target_rustcflags = Some("-L ../target/debug/deps --edition 2018".to_owned()); 10 | config.src_base = PathBuf::from(format!("tests/{}", mode)); 11 | config.link_deps(); // Populate config.target_rustcflags with dependencies on the path 12 | 13 | config.clean_rmeta(); // If your tests import the parent crate, this helps with E0464 14 | 15 | compiletest::run_tests(&config); 16 | } 17 | 18 | #[test] 19 | fn run_pass() { 20 | run_mode("run-pass"); 21 | } 22 | 23 | #[test] 24 | fn compile_fail() { 25 | run_mode("compile-fail"); 26 | } 27 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/associated_types.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait_remote, Delegate}; 4 | use std::iter::IntoIterator; 5 | 6 | #[delegatable_trait_remote] 7 | pub trait IntoIterator { 8 | type Item; 9 | type IntoIter: Iterator; 10 | fn into_iter(self) -> Self::IntoIter; 11 | } 12 | 13 | #[derive(Delegate)] 14 | #[delegate(IntoIterator)] 15 | pub struct WrappedVec(Vec); 16 | 17 | pub fn main() { 18 | let wrapped = WrappedVec(vec![1,2,3]); 19 | println!("{:?}", wrapped.into_iter().collect::>()); 20 | } 21 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/async.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | 5 | pub struct Base1 {} 6 | 7 | #[derive(Delegate)] 8 | #[delegate(Hello, target = "self")] 9 | pub struct Base2 {} 10 | 11 | impl Base2 { 12 | async fn hello(&self) {} 13 | } 14 | 15 | #[delegatable_trait] 16 | trait Hello { 17 | async fn hello(&self); 18 | } 19 | 20 | impl Hello for Base1 { 21 | async fn hello(&self) {} 22 | } 23 | 24 | #[derive(Delegate)] 25 | #[delegate(Hello)] 26 | pub struct Dram { 27 | inner: Base1, 28 | } 29 | 30 | #[derive(Delegate)] 31 | #[delegate(Hello)] 32 | pub enum Either { 33 | Base1(Base1), 34 | Base2(Base2), 35 | } 36 | 37 | struct OpBase(Option); 38 | 39 | #[delegate_to_methods] 40 | #[delegate(Hello, target_ref = "unwrap_ref")] 41 | impl OpBase { 42 | fn unwrap_ref(&self) -> &Base1 { 43 | self.0.as_ref().unwrap() 44 | } 45 | } 46 | 47 | fn main() { 48 | let d = Dram { inner: Base1 {} }; 49 | 50 | let _ = d.hello(); 51 | } 52 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/auto_where_clause.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::Delegate; 4 | 5 | use ambassador::delegatable_trait; 6 | #[delegatable_trait] 7 | pub trait Shout { 8 | fn shout(&self, input: &str) -> String; 9 | } 10 | 11 | 12 | 13 | pub struct Cat; 14 | 15 | impl Shout for Cat { 16 | fn shout(&self, input: &str) -> String { 17 | format!("{} - meow!", input) 18 | } 19 | } 20 | 21 | pub struct Dog; 22 | 23 | 24 | impl Shout for Dog { 25 | fn shout(&self, input: &str) -> String { 26 | format!("{} - wuff!", input) 27 | } 28 | } 29 | 30 | #[derive(Delegate)] 31 | #[delegate(Shout)] 32 | pub enum Either { 33 | Left(A), 34 | Right(B), 35 | } 36 | 37 | 38 | pub fn main() { 39 | let foo_animal = Either::Left::(Cat); 40 | println!("{}", foo_animal.shout("BAR")); 41 | let bar_animal = Either::Right::(Dog); 42 | println!("{}", bar_animal.shout("BAR")); 43 | } 44 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/cfg.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Trait { 7 | #[cfg(all())] 8 | fn method(&self); 9 | 10 | #[cfg(any())] 11 | fn extra_method(&self); 12 | } 13 | 14 | pub struct Cat; 15 | 16 | impl Trait for Cat { 17 | fn method(&self) { 18 | println!("meow"); 19 | } 20 | } 21 | 22 | #[derive(Delegate)] 23 | #[delegate(Trait)] 24 | pub struct WrappedCat { 25 | inner: Cat, 26 | } 27 | 28 | pub fn main() { 29 | let foo_animal = WrappedCat { inner: Cat }; 30 | foo_animal.method() 31 | } 32 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/const_generic.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | trait S { 7 | fn handle(&self, x: [u8; N]); 8 | } 9 | 10 | struct Base; 11 | 12 | impl S for Base { 13 | fn handle(&self, x: [u8; N]) { 14 | println!("{x:?}"); 15 | } 16 | } 17 | 18 | const C: usize = 2; 19 | 20 | #[derive(Delegate)] 21 | #[delegate(S<1>)] 22 | #[delegate(S)] 23 | #[delegate(S<{C+1}>)] 24 | struct Wrap(Base); 25 | 26 | fn main() { 27 | let w = Wrap(Base); 28 | w.handle([1]); 29 | w.handle([1, 2]); 30 | w.handle([1, 2, 3]); 31 | } 32 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_remote.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_remote}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | mod wrapped { 27 | use super::{Cat}; 28 | pub struct WrappedAnimals { 29 | pub foo: Cat, 30 | pub bar: A, 31 | baz: u32, // private field 32 | } 33 | 34 | impl WrappedAnimals { 35 | pub fn new(foo: Cat, bar: A) -> Self { 36 | WrappedAnimals{foo, bar, baz: 13} 37 | } 38 | } 39 | } 40 | 41 | use wrapped::*; 42 | 43 | #[delegate_remote] 44 | #[delegate(Shout, target = "bar")] 45 | struct WrappedAnimals { 46 | foo: Cat, 47 | bar: A, 48 | } 49 | 50 | 51 | pub fn main() { 52 | let foo_animal = WrappedAnimals::new(Cat, Cat); 53 | println!("{}", foo_animal.shout("BAR")); 54 | let bar_animal = WrappedAnimals::new(Cat, Dog); 55 | println!("{}", bar_animal.shout("BAR")); 56 | } 57 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_self.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | #[derive(Delegate)] 11 | #[delegate(Shout, target="self")] 12 | pub struct Cat; 13 | 14 | impl Cat { 15 | fn shout(&self, input: &str) -> String { 16 | format!("{} - meow!", input) 17 | } 18 | } 19 | 20 | fn use_shout(x: impl Shout) { 21 | x.shout("BAR"); 22 | } 23 | 24 | pub fn main() { 25 | use_shout(Cat) 26 | } 27 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_method_g_and_at.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | 5 | #[delegatable_trait] 6 | pub trait FnLike { 7 | type Out; 8 | fn apply(&self, input: In) -> Self::Out; 9 | } 10 | 11 | impl R, R> FnLike for F { 12 | type Out = R; 13 | 14 | fn apply(&self, input: In) -> Self::Out { 15 | self(input) 16 | } 17 | } 18 | 19 | pub struct Cat; 20 | 21 | #[delegate_to_methods] 22 | #[delegate(FnLike, target_ref = "to_fn", generics = "X")] 23 | impl Cat { 24 | fn to_fn(&self) -> &fn(String) -> String { 25 | fn ret(x: String) -> String { 26 | x + "meow" 27 | } 28 | &(ret as fn(String) -> String) 29 | } 30 | } 31 | 32 | fn main() { 33 | assert_eq!(Cat.apply(String::new()), "meow") 34 | } 35 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_methods.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { 14 | *self 15 | } 16 | 17 | fn change(&mut self) { 18 | *self += 1 19 | } 20 | } 21 | 22 | struct Wrap(X); 23 | 24 | #[delegate_to_methods] 25 | #[delegate(MyTrait, target_ref = "inner_ref", target_mut = "inner_mut")] 26 | impl Wrap 27 | where 28 | X: DerefMut, 29 | { 30 | fn inner_ref(&self) -> &X::Target { 31 | self.0.deref() 32 | } 33 | 34 | fn inner_mut(&mut self) -> &mut X::Target { 35 | self.0.deref_mut() 36 | } 37 | 38 | fn something_else(&self) -> &str { 39 | "Hello World" 40 | } 41 | 42 | fn one_more(self, x: u32) -> u32 { 43 | x 44 | } 45 | } 46 | 47 | fn main() { 48 | let mut x = Wrap(Box::new(42u32)); 49 | assert_eq!(x.get(), 42); 50 | x.change(); 51 | assert_eq!(x.get(), 43); 52 | } 53 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_methods_dyn_works.rs: -------------------------------------------------------------------------------- 1 | // Ensure that deletage_to_methods can be used as alternative to inhibit_automatic_where_clause for auto-deriving traits for objects. 2 | 3 | extern crate ambassador; 4 | 5 | use ambassador::{delegatable_trait, delegate_to_methods}; 6 | 7 | #[delegatable_trait] 8 | pub trait Shout { 9 | fn shout(&self, input: &str) -> String; 10 | } 11 | 12 | pub struct Cat; 13 | 14 | impl Shout for Cat { 15 | fn shout(&self, input: &str) -> String { 16 | format!("{} - meow!", input) 17 | } 18 | } 19 | 20 | pub struct Dog; 21 | 22 | impl Shout for Dog { 23 | fn shout(&self, input: &str) -> String { 24 | format!("{} - wuff!", input) 25 | } 26 | } 27 | 28 | pub struct WrappedAnimals(pub Box); 29 | 30 | 31 | #[delegate_to_methods] 32 | #[delegate(Shout, target_ref="deref")] 33 | impl WrappedAnimals { 34 | // Note that ` &dyn Shout` itself does not implement `Shout`, 35 | // but for this particular trait all `Shout`'s methods can still be called. 36 | // If Ambassador were to insert `where &dyn Shout : Shout` clause based on method return value, 37 | // that whould stop working, breaking backwards compatiblity. 38 | // 39 | // Such where clause can be generated, but then `inhibit_automatic_where_clause` needs to 40 | // be implemented for `delegate_to_methods` as well. 41 | fn deref(&self) -> &dyn Shout { 42 | &*self.0 43 | } 44 | } 45 | 46 | fn use_it (shouter: T) { 47 | println!("{}", shouter.shout("BAR")); 48 | } 49 | 50 | pub fn main() { 51 | let foo_animal = WrappedAnimals(Box::new(Cat)); 52 | use_it(foo_animal); 53 | } 54 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_methods_in_trait_impl.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_methods}; 4 | use std::ops::DerefMut; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { 14 | *self 15 | } 16 | 17 | fn change(&mut self) { 18 | *self += 1 19 | } 20 | } 21 | 22 | struct Wrap(X); 23 | 24 | trait HasAU32 { 25 | fn get_ref(&self) -> &u32; 26 | fn get_mut_ref(&mut self) -> &mut u32; 27 | } 28 | 29 | #[delegate_to_methods] 30 | #[delegate(MyTrait, target_ref = "get_ref", target_mut = "get_mut_ref")] 31 | impl HasAU32 for Wrap 32 | where 33 | X: DerefMut, 34 | { 35 | fn get_ref(&self) -> &u32 { self.0.deref() } 36 | fn get_mut_ref(&mut self) -> &mut u32 { self.0.deref_mut() } 37 | } 38 | 39 | fn main() { 40 | let mut x = Wrap(Box::new(42u32)); 41 | assert_eq!(x.get(), 42); 42 | x.change(); 43 | assert_eq!(x.get(), 43); 44 | } 45 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_remote_methods.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_remote_methods}; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { 14 | *self 15 | } 16 | 17 | fn change(&mut self) { 18 | *self += 1 19 | } 20 | } 21 | 22 | struct Wrap(X); 23 | 24 | trait HasAU32 { 25 | fn get_ref(&self) -> &u32; 26 | fn get_mut_ref(&mut self) -> &mut u32; 27 | } 28 | 29 | impl> HasAU32 for Wrap { 30 | fn get_ref(&self) -> &u32 { self.0.deref() } 31 | fn get_mut_ref(&mut self) -> &mut u32 { self.0.deref_mut() } 32 | } 33 | 34 | #[delegate_to_remote_methods] 35 | #[delegate(MyTrait, target_ref = "get_ref", target_mut = "get_mut_ref")] 36 | impl> Wrap { 37 | fn get_ref(&self) -> &u32; 38 | fn get_mut_ref(&mut self) -> &mut u32; 39 | } 40 | 41 | fn main() { 42 | let mut x = Wrap(Box::new(42u32)); 43 | assert_eq!(x.get(), 42); 44 | x.change(); 45 | assert_eq!(x.get(), 43); 46 | } 47 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_to_remote_methods_on_remote_type.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_to_remote_methods}; 4 | use std::ops::{Deref, DerefMut}; 5 | 6 | #[delegatable_trait] 7 | trait MyTrait { 8 | fn get(&self) -> u32; 9 | fn change(&mut self); 10 | } 11 | 12 | impl MyTrait for u32 { 13 | fn get(&self) -> u32 { *self } 14 | fn change(&mut self) { *self += 1 } 15 | } 16 | 17 | #[delegate_to_remote_methods] 18 | #[delegate(MyTrait, target_ref = "deref", target_mut = "deref_mut")] 19 | impl Box { 20 | fn deref(&self) -> &X; 21 | fn deref_mut(&mut self) -> &mut X; 22 | } 23 | 24 | fn main() { 25 | let mut x = Box::new(42u32); 26 | assert_eq!(x.get(), 42); 27 | x.change(); 28 | assert_eq!(x.get(), 43); 29 | 30 | let mut y: Box = x; 31 | assert_eq!(y.get(), 43); 32 | y.change(); 33 | assert_eq!(y.get(), 44); 34 | } 35 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/delegate_trait_remote_display.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait_remote, Delegate}; 4 | use std::fmt::Display; 5 | 6 | pub struct Cat; 7 | 8 | impl std::fmt::Display for Cat { 9 | fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { 10 | write!(f, "Foo") 11 | } 12 | } 13 | 14 | #[delegatable_trait_remote] 15 | trait Display { 16 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> Result<(), ::std::fmt::Error>; 17 | } 18 | 19 | #[derive(Delegate)] 20 | #[delegate(Display)] 21 | pub struct WrappedCat(Cat); 22 | 23 | pub fn main() { 24 | let foo_animal = WrappedCat(Cat); 25 | println!("{}", foo_animal); 26 | } 27 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/derive_and_trait_in_modules.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | mod baz { 4 | use ambassador::delegatable_trait; 5 | 6 | #[delegatable_trait] 7 | pub trait Shout { 8 | fn shout(&self, input: &str) -> String; 9 | } 10 | } 11 | 12 | mod bar { 13 | use super::{ambassador_impl_Shout, Shout}; 14 | use ambassador::Delegate; 15 | 16 | pub struct Cat; 17 | 18 | impl Shout for Cat { 19 | fn shout(&self, input: &str) -> String { 20 | format!("{} - meow!", input) 21 | } 22 | } 23 | 24 | pub struct Dog; 25 | 26 | impl Shout for Dog { 27 | fn shout(&self, input: &str) -> String { 28 | format!("{} - wuff!", input) 29 | } 30 | } 31 | 32 | #[derive(Delegate)] 33 | #[delegate(Shout)] 34 | pub enum Animals { 35 | Cat(Cat), 36 | Dog(Dog), 37 | } 38 | } 39 | 40 | use bar::{Animals, Cat}; 41 | use baz::{ambassador_impl_Shout, Shout}; 42 | 43 | pub fn main() { 44 | let foo_animal = Animals::Cat(Cat); 45 | println!("{}", foo_animal.shout("BAR")); 46 | } 47 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/derive_in_module.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::delegatable_trait; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | mod bar { 11 | use super::{ambassador_impl_Shout, Shout}; 12 | use ambassador::Delegate; 13 | 14 | pub struct Cat; 15 | 16 | impl Shout for Cat { 17 | fn shout(&self, input: &str) -> String { 18 | format!("{} - meow!", input) 19 | } 20 | } 21 | 22 | pub struct Dog; 23 | 24 | impl Shout for Dog { 25 | fn shout(&self, input: &str) -> String { 26 | format!("{} - wuff!", input) 27 | } 28 | } 29 | 30 | #[derive(Delegate)] 31 | #[delegate(Shout)] 32 | pub enum Animals { 33 | Cat(Cat), 34 | Dog(Dog), 35 | } 36 | } 37 | 38 | use bar::{Animals, Cat}; 39 | 40 | pub fn main() { 41 | let foo_animal = Animals::Cat(Cat); 42 | println!("{}", foo_animal.shout("BAR")); 43 | } 44 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/double_generics.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | 5 | #[delegatable_trait] 6 | pub trait Target {} 7 | 8 | pub struct SubTarget; 9 | 10 | impl Target for SubTarget {} 11 | 12 | #[derive(Delegate)] 13 | #[delegate(Target, generics = "T, U")] 14 | pub struct SuperTarget(SubTarget); 15 | 16 | pub fn main() {} 17 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/enum_associated_constant.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | use std::collections::{BTreeMap, HashMap}; 5 | 6 | #[delegatable_trait] 7 | pub trait IntoMany { 8 | const N: usize; 9 | } 10 | 11 | impl IntoMany for u32 { 12 | const N: usize = 4; 13 | } 14 | 15 | impl IntoMany for u32 { 16 | const N: usize = 2; 17 | } 18 | 19 | impl IntoMany for u16 { 20 | const N: usize = 2; 21 | } 22 | 23 | impl IntoMany for char { 24 | const N: usize = 4; 25 | } 26 | 27 | #[derive(Delegate)] 28 | #[delegate(IntoMany, generics = "X")] 29 | pub enum CharOrU32 { 30 | Char(char), 31 | U32(u32), 32 | } 33 | 34 | fn main() { 35 | assert_eq!(>::N, 4); 36 | } 37 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/enum_associated_types.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | use std::any::type_name; 5 | 6 | #[delegatable_trait] 7 | pub trait Animal { 8 | type Baby; 9 | } 10 | 11 | pub struct Kitten; 12 | pub struct Cat; 13 | pub struct Puppy; 14 | pub struct Dog; 15 | 16 | impl Animal for Puppy { 17 | type Baby = Puppy; 18 | } 19 | 20 | impl Animal for Dog { 21 | type Baby = Puppy; 22 | } 23 | 24 | impl Animal for Kitten { 25 | type Baby = Kitten; 26 | } 27 | 28 | impl Animal for Cat { 29 | type Baby = Kitten; 30 | } 31 | 32 | 33 | #[derive(Delegate)] 34 | #[delegate(Animal)] 35 | pub enum Either { 36 | A(A), 37 | B(B), 38 | } 39 | 40 | 41 | pub fn main() { 42 | println!("{:?}", type_name::< as Animal>::Baby>()); 43 | } 44 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/gat.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait StreamingIterator { 7 | type Output<'a> 8 | where 9 | Self: 'a; 10 | fn next(&mut self) -> Option>; 11 | } 12 | 13 | struct RepeatMut(T); 14 | 15 | impl StreamingIterator for RepeatMut { 16 | type Output<'a> = &'a mut T where T: 'a; 17 | 18 | fn next(&mut self) -> Option> { 19 | Some(&mut self.0) 20 | } 21 | } 22 | 23 | #[derive(Delegate)] 24 | #[delegate(StreamingIterator)] 25 | pub struct Wrap(X); 26 | 27 | pub fn main() { 28 | let mut x = Wrap(RepeatMut("forever".into())); 29 | let m: &mut String = x.next().unwrap(); 30 | m.push('?'); 31 | assert_eq!(x.next().unwrap(), "forever?"); 32 | } 33 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_enum.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Either { 29 | Left(A), 30 | Right(B), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Either::Left::(Cat); 35 | println!("{}", foo_animal.shout("BAR")); 36 | let bar_animal = Either::Right::(Dog); 37 | println!("{}", bar_animal.shout("BAR")); 38 | } 39 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_method.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | trait StaticFn { 6 | fn call() -> bool; 7 | } 8 | 9 | struct StaticTrue; 10 | struct StaticFalse; 11 | 12 | impl StaticFn for StaticTrue { 13 | fn call() -> bool { 14 | true 15 | } 16 | } 17 | 18 | impl StaticFn for StaticFalse { 19 | fn call() -> bool { 20 | false 21 | } 22 | } 23 | 24 | #[delegatable_trait] 25 | pub trait StaticCall { 26 | fn call(&self) -> bool; 27 | } 28 | 29 | pub struct BaseCaller; 30 | 31 | impl StaticCall for BaseCaller { 32 | fn call(&self) -> bool { 33 | C::call() 34 | } 35 | } 36 | 37 | #[derive(Delegate)] 38 | #[delegate(StaticCall)] 39 | pub struct WrappedCaller(BaseCaller); 40 | 41 | fn main() { 42 | let c = WrappedCaller(BaseCaller); 43 | // Verify that the generic type is correctly passed through without relying on type inference. 44 | assert!(c.call::()); 45 | assert!(!c.call::()); 46 | } 47 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_struct.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout, target = "bar")] 28 | pub struct WrappedAnimals { 29 | foo: Cat, 30 | bar: A, 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = WrappedAnimals { foo: Cat, bar: Cat }; 35 | println!("{}", foo_animal.shout("BAR")); 36 | let bar_animal = WrappedAnimals { foo: Cat, bar: Dog }; 37 | println!("{}", bar_animal.shout("BAR")); 38 | } 39 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_trait_any.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::*; 4 | use std::collections::{BTreeMap, HashMap}; 5 | use std::ops::Index; 6 | 7 | #[delegatable_trait_remote] 8 | pub trait Index { 9 | type Output: ?Sized; 10 | fn index(&self, index: Idx) -> &Self::Output; 11 | } 12 | 13 | #[derive(Delegate)] 14 | #[delegate(Index, generics = "X", where = "X: Copy")] 15 | pub enum SomeMap { 16 | Hash(HashMap), 17 | BTree(BTreeMap), 18 | } 19 | 20 | fn main() { 21 | let m1 = SomeMap::Hash(HashMap::from([("dog".to_string(), "wuff")])); 22 | let m2 = SomeMap::BTree(BTreeMap::from([("cat".to_string(), "meow")])); 23 | assert_eq!(m1["dog"], "wuff"); 24 | assert_eq!(m2["cat"], "meow"); 25 | } 26 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_trait_complex.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | use std::fmt::Display; 5 | 6 | #[delegatable_trait] 7 | pub trait Shout { 8 | fn shout(&self, input: T) -> String; 9 | } 10 | 11 | pub struct Cat; 12 | 13 | impl Shout<(A, B)> for Cat { 14 | fn shout(&self, (a, b): (A, B)) -> String { 15 | format!("{} - meow, {} - purr...", a, b) 16 | } 17 | } 18 | 19 | #[derive(Delegate)] 20 | #[delegate(Shout<(Y, &'a str)>, generics = "'a, Y", target="0")] 21 | pub struct WrappedCat(Cat, ()); 22 | 23 | fn main() { 24 | let c = WrappedCat(Cat, ()); 25 | println!("{}", c.shout((5, "bar"))) 26 | } 27 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_trait_lifetime.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | trait Ref<'a> { 7 | type Target; 8 | fn deref<'b>(&'b self) -> &'a Self::Target; 9 | } 10 | 11 | impl<'a, E> Ref<'a> for &'a E { 12 | type Target = E; 13 | 14 | fn deref<'b>(&'b self) -> &'a Self::Target { 15 | *self 16 | } 17 | } 18 | 19 | #[derive(Delegate)] 20 | #[delegate(Ref<'x>, generics = "'x")] 21 | struct WrapRef<'a, T>(&'a T); 22 | 23 | fn main() { 24 | let x = 5; 25 | let y = WrapRef(&x); 26 | assert_eq!(*y.deref(), 5) 27 | } 28 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_trait_single_type.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | use ambassador::*; 3 | use std::ops::Index; 4 | 5 | #[delegatable_trait_remote] 6 | pub trait Index { 7 | type Output: ?Sized; 8 | fn index(&self, index: Idx) -> &Self::Output; 9 | } 10 | 11 | 12 | #[derive(Delegate)] 13 | #[delegate(Index)] 14 | pub enum TinyVec { 15 | One([E; 1]), 16 | Many(Vec), 17 | } 18 | 19 | fn main() { 20 | let tv1 = TinyVec::One([5]); 21 | let tv2 = TinyVec::Many(vec![4,7,8]); 22 | assert_eq!(tv1[0], 5); 23 | assert_eq!(tv2[1], 7); 24 | } -------------------------------------------------------------------------------- /ambassador/tests/run-pass/generic_tuple_struct.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub struct WrappedAnimal(A); 29 | 30 | pub fn main() { 31 | let foo_animal = WrappedAnimal(Cat); 32 | println!("{}", foo_animal.shout("BAR")); 33 | let bar_animal = WrappedAnimal(Dog); 34 | println!("{}", bar_animal.shout("BAR")); 35 | } 36 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/inhibit_where_clause.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout,automatic_where_clause="false")] 28 | pub struct WrappedAnimals(pub Box); 29 | 30 | 31 | fn use_it (shouter: T) { 32 | println!("{}", shouter.shout("BAR")); 33 | } 34 | 35 | pub fn main() { 36 | let foo_animal = WrappedAnimals(Box::new(Cat)); 37 | use_it(foo_animal); 38 | } 39 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/late_bound_lifetime.rs: -------------------------------------------------------------------------------- 1 | #![deny(late_bound_lifetime_arguments)] 2 | extern crate ambassador; 3 | 4 | use ambassador::{delegatable_trait, Delegate}; 5 | 6 | #[delegatable_trait] 7 | pub trait Shout { 8 | fn shout<'a>(&'a self) { 9 | println!("SHOUT!") 10 | } 11 | } 12 | 13 | pub struct A; 14 | impl Shout for A {} 15 | 16 | #[derive(Delegate)] 17 | #[delegate(Shout, target = "0")] 18 | pub struct B(A); 19 | 20 | fn main() { 21 | B(A).shout() 22 | } 23 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/method_mut_ref_self.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&mut self) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&mut self) -> String { 14 | "meow!".to_owned() 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout)] 20 | pub struct WrappedCat(Cat); 21 | 22 | #[derive(Delegate)] 23 | #[delegate(Shout)] 24 | pub enum Animals { 25 | Cat(Cat), 26 | Wrapped(WrappedCat), 27 | } 28 | 29 | pub fn main() { 30 | let mut foo_animal = Animals::Cat(Cat); 31 | println!("{}", foo_animal.shout()); 32 | } 33 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/method_ref_self.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self) -> String { 14 | "meow!".to_owned() 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self) -> String { 22 | "meow!".to_owned() 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Animals { 29 | Cat(Cat), 30 | Dog(Dog), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Animals::Cat(Cat); 35 | println!("{}", foo_animal.shout()); 36 | } 37 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/method_self.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(self) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(self) -> String { 14 | "meow!".to_owned() 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(self) -> String { 22 | "meow!".to_owned() 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Animals { 29 | Cat(Cat), 30 | Dog(Dog), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Animals::Cat(Cat); 35 | println!("{}", foo_animal.shout()); 36 | } 37 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/method_single_arg.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Animal { 29 | Cat(Cat), 30 | Dog(Dog), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Animal::Cat(Cat); 35 | println!("{}", foo_animal.shout("BAR")); 36 | } 37 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/muti-feature.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, delegate_remote, Delegate}; 4 | use std::borrow::Borrow; 5 | use std::cmp::{Eq, Ord}; 6 | use std::collections::{BTreeMap, HashMap}; 7 | use std::hash::{BuildHasher, Hash}; 8 | 9 | #[delegatable_trait] 10 | pub trait Map { 11 | type K; 12 | type V; 13 | } 14 | 15 | #[delegatable_trait] 16 | pub trait Get: Map { 17 | fn get(&self, k: &Q) -> Option<&Self::V>; 18 | } 19 | 20 | impl Map for HashMap { 21 | type K = K; 22 | type V = V; 23 | } 24 | 25 | impl Map for BTreeMap { 26 | type K = K; 27 | type V = V; 28 | } 29 | 30 | // No automatic where clause provided for target = "self" 31 | #[delegate_remote] 32 | #[delegate(Get, generics = "X", target = "self", where = "K: Hash + Eq + Borrow, S: BuildHasher, X: Hash + Eq + ?Sized")] 33 | struct HashMap(); 34 | 35 | #[delegate_remote] 36 | #[delegate(Get, generics = "X", target = "self", where = "K: Ord + Borrow, X: Ord + ?Sized")] 37 | struct BTreeMap(); 38 | 39 | #[derive(Delegate)] 40 | #[delegate(Map)] 41 | #[delegate(Get, generics = "X", where = "X: ?Sized, A: Map, B: Map")] //auto where clause misses required on super trait 42 | pub enum Either { 43 | Left(A), 44 | Right(B), 45 | } 46 | 47 | pub fn main() { 48 | let my_map: Either, BTreeMap<&'static str, u32>> = 49 | Either::Left([("a", 1)].into()); 50 | println!("{:?}", my_map.get("a")); 51 | } 52 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/single_trait_single_method.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self) -> String { 14 | "meow!".to_owned() 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self) -> String { 22 | "meow!".to_owned() 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Animals { 29 | Cat(Cat), 30 | Dog(Dog), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Animals::Cat(Cat); 35 | println!("{}", foo_animal.shout()); 36 | } 37 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/single_variant.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Dog; 11 | 12 | impl Shout for Dog { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - wuff!", input) 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout)] 20 | pub enum Animal { 21 | Dog(Dog), 22 | } 23 | 24 | pub fn main() { 25 | let foo_animal = Animal::Dog(Dog); 26 | println!("{}", foo_animal.shout("BAR")); 27 | } 28 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/struct_field_target.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | #[derive(Delegate)] 21 | #[delegate(Shout, target = "inner")] 22 | pub struct WrappedCat { 23 | inner: Cat, 24 | other: Dog, 25 | } 26 | 27 | pub fn main() { 28 | let foo_animal = WrappedCat { 29 | inner: Cat, 30 | other: Dog, 31 | }; 32 | println!("{}", foo_animal.shout("BAR")); 33 | } 34 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/struct_single_field.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout)] 20 | pub struct WrappedCat { 21 | inner: Cat, 22 | } 23 | 24 | pub fn main() { 25 | let foo_animal = WrappedCat { inner: Cat }; 26 | println!("{}", foo_animal.shout("BAR")); 27 | } 28 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/struct_single_field_target.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout, target = "inner")] 20 | pub struct WrappedCat { 21 | inner: Cat, 22 | } 23 | 24 | pub fn main() { 25 | let foo_animal = WrappedCat { inner: Cat }; 26 | println!("{}", foo_animal.shout("BAR")); 27 | } 28 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/taxonomy.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | use std::any::type_name; 5 | 6 | #[delegatable_trait] 7 | pub trait Taxonomy { 8 | type Res; 9 | } 10 | 11 | pub struct Cat; 12 | pub struct Dog; 13 | pub struct Alligator; 14 | 15 | pub struct Class; 16 | pub struct Mammal; 17 | pub struct Reptile; 18 | 19 | pub struct Kingdom; 20 | pub struct Animal; 21 | 22 | pub trait Base {} 23 | 24 | impl Base for Cat {} 25 | impl Taxonomy for Cat { 26 | type Res = Mammal; 27 | } 28 | 29 | impl Base for Dog {} 30 | impl Taxonomy for Dog { 31 | type Res = Mammal; 32 | } 33 | 34 | impl Base for Alligator {} 35 | impl Taxonomy for Alligator { 36 | type Res = Reptile; 37 | } 38 | 39 | impl Taxonomy for Mammal { 40 | type Res = Animal; 41 | } 42 | 43 | impl Taxonomy for Reptile { 44 | type Res = Animal; 45 | } 46 | 47 | impl> Taxonomy for E 48 | where 49 | >::Res: Taxonomy, 50 | { 51 | type Res = <>::Res as Taxonomy>::Res; 52 | } 53 | 54 | #[derive(Delegate)] 55 | #[delegate(Taxonomy, generics = "X")] 56 | pub enum Either { 57 | A(A), 58 | B(B), 59 | } 60 | 61 | pub fn main() { 62 | assert_eq!( 63 | type_name::< as Taxonomy>::Res>(), 64 | type_name::() 65 | ); 66 | assert_eq!( 67 | type_name::< as Taxonomy>::Res>(), 68 | type_name::() 69 | ); 70 | } 71 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/tuple_struct_single_field.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | #[derive(Delegate)] 19 | #[delegate(Shout)] 20 | pub struct WrappedCat(Cat); 21 | 22 | pub fn main() { 23 | let foo_animal = WrappedCat(Cat); 24 | println!("{}", foo_animal.shout("BAR")); 25 | } 26 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/tuple_struct_target.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout, target = "1")] 28 | pub struct WrappedAnimals(Cat, Dog); 29 | 30 | pub fn main() { 31 | let foo_animal = WrappedAnimals(Cat, Dog); 32 | println!("{}", foo_animal.shout("BAR")); 33 | } 34 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/two_enums.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout)] 28 | pub enum Either { 29 | Left(A), 30 | Right(B), 31 | } 32 | 33 | #[derive(Delegate)] 34 | #[delegate(Shout)] 35 | pub enum Either3 { 36 | First(A), 37 | Second(B), 38 | Third(C), 39 | } 40 | 41 | pub fn main() { 42 | let foo_animal = Either::Left::(Cat); 43 | println!("{}", foo_animal.shout("BAR")); 44 | let bar_animal = Either3::Second::(Dog); 45 | println!("{}", bar_animal.shout("BAR")); 46 | } 47 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/where_clause.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout, where = "A: Shout, B: Shout")] 28 | pub enum Either { 29 | Left(A), 30 | Right(B), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Either::Left::(Cat); 35 | println!("{}", foo_animal.shout("BAR")); 36 | let bar_animal = Either::Right::(Dog); 37 | println!("{}", bar_animal.shout("BAR")); 38 | } 39 | -------------------------------------------------------------------------------- /ambassador/tests/run-pass/where_clause_split.rs: -------------------------------------------------------------------------------- 1 | extern crate ambassador; 2 | 3 | use ambassador::{delegatable_trait, Delegate}; 4 | 5 | #[delegatable_trait] 6 | pub trait Shout { 7 | fn shout(&self, input: &str) -> String; 8 | } 9 | 10 | pub struct Cat; 11 | 12 | impl Shout for Cat { 13 | fn shout(&self, input: &str) -> String { 14 | format!("{} - meow!", input) 15 | } 16 | } 17 | 18 | pub struct Dog; 19 | 20 | impl Shout for Dog { 21 | fn shout(&self, input: &str) -> String { 22 | format!("{} - wuff!", input) 23 | } 24 | } 25 | 26 | #[derive(Delegate)] 27 | #[delegate(Shout, where = "A: Shout", where = "B: Shout")] 28 | pub enum Either { 29 | Left(A), 30 | Right(B), 31 | } 32 | 33 | pub fn main() { 34 | let foo_animal = Either::Left::(Cat); 35 | println!("{}", foo_animal.shout("BAR")); 36 | let bar_animal = Either::Right::(Dog); 37 | println!("{}", bar_animal.shout("BAR")); 38 | } 39 | --------------------------------------------------------------------------------