├── .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 |
7 |
8 |
9 |
10 |
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 | //!
8 | //!
9 | //!
10 | //!
11 | //!
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