1
- # Maybe-Async Procedure Macro
1
+ ![ Maintenance] ( https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg )
2
+
3
+ # maybe-async
2
4
3
5
** Why bother writing similar code twice for blocking and async code?**
4
6
@@ -24,16 +26,16 @@ those `async` and `await` when you need a blocking code.
24
26
25
27
These procedural macros can be applied to the following codes:
26
28
- trait item declaration
27
- - trait implmentation
29
+ - trait implementation
28
30
- function definition
29
31
- struct definition
30
32
31
33
** RECOMMENDATION** : Enable ** resolver ver2** in your crate, which is
32
34
introduced in Rust 1.51. If not, two crates in dependency with conflict
33
- version (one async and another blocking) can fail complilation .
35
+ version (one async and another blocking) can fail compilation .
34
36
35
37
36
- ## Motivation
38
+ ### Motivation
37
39
38
40
The async/await language feature alters the async world of rust.
39
41
Comparing with the map/and_then style, now the async code really resembles
@@ -42,9 +44,9 @@ sync version code.
42
44
In many crates, the async and sync version of crates shares the same API,
43
45
but the minor difference that all async code must be awaited prevent the
44
46
unification of async and sync code. In other words, we are forced to write
45
- an async and an sync implementation repectively .
47
+ an async and a sync implementation respectively .
46
48
47
- ## Macros in Detail
49
+ ### Macros in Detail
48
50
49
51
` maybe-async ` offers 4 set of attribute macros: ` maybe_async ` ,
50
52
` sync_impl ` /` async_impl ` , ` must_be_sync ` /` must_be_async ` , and ` test ` .
@@ -71,7 +73,7 @@ blocking code except for async/await keywords. And use feature gate
71
73
maybe_async = " 0.2"
72
74
```
73
75
74
- Wanna convert async code to sync? Add `maybe_async` to dependencies with
76
+ Want to convert async code to sync? Add `maybe_async` to dependencies with
75
77
an `is_sync` feature gate. In this way, `maybe_async` is the same as
76
78
`must_be_sync`:
77
79
@@ -80,19 +82,31 @@ blocking code except for async/await keywords. And use feature gate
80
82
maybe_async = { version = " 0.2" , features = [" is_sync" ] }
81
83
```
82
84
83
- Not all async traits need futures that are `dyn Future + Send`.
84
- To avoid having "Send" and "Sync" bounds placed on the async trait
85
- methods, invoke the maybe_async macro as # [maybe_async(?Send)] on both
86
- the trait and the impl blocks.
85
+ There are three usage variants for `maybe_async` attribute macros:
86
+ - `#[maybe_async]` or `#[maybe_async(Send)]`
87
+
88
+ In this mode, `#[async_trait::async_trait]` is added to trait declarations and trait implementations
89
+ to support async fn in traits.
90
+
91
+ - `#[maybe_async(?Send)]`
92
+
93
+ Not all async traits need futures that are `dyn Future + Send`.
94
+ In this mode, `#[async_trait::async_trait(?Send)]` is added to trait declarations and trait implementations,
95
+ to avoid having "Send" and "Sync" bounds placed on the async trait
96
+ methods.
87
97
98
+ - `#[maybe_async(AFIT)]`
99
+
100
+ AFIT is acronym for **a**sync **f**unction **i**n **t**rait, stabilized from rust 1.74
88
101
89
102
- `must_be_async`
90
103
91
- **Keep async**. Add `async_trait` attribute macro for trait declaration
92
- or implementation to bring async fn support in traits.
104
+ **Keep async**.
93
105
94
- To avoid having "Send" and "Sync" bounds placed on the async trait
95
- methods, invoke the maybe_async macro as # [must_be_async(?Send)].
106
+ There are three usage variants for `must_be_async` attribute macros:
107
+ - `#[must_be_async]` or `#[must_be_async(Send)]`
108
+ - `#[must_be_async(?Send)]`
109
+ - `#[must_be_async(AFIT)]`
96
110
97
111
- `must_be_sync`
98
112
@@ -102,56 +116,68 @@ blocking code except for async/await keywords. And use feature gate
102
116
103
117
- `sync_impl`
104
118
105
- An sync implementation should on compile on blocking implementation and
106
- must simply disappear when we want async version.
119
+ A sync implementation should compile on blocking implementation and
120
+ must simply disappear when we want async version.
107
121
108
122
Although most of the API are almost the same, there definitely come to a
109
123
point when the async and sync version should differ greatly. For
110
124
example, a MongoDB client may use the same API for async and sync
111
- verison , but the code to actually send reqeust are quite different.
125
+ version , but the code to actually send reqeust are quite different.
112
126
113
127
Here, we can use `sync_impl` to mark a synchronous implementation, and a
114
- sync implementation shoule disappear when we want async version.
128
+ sync implementation should disappear when we want async version.
115
129
116
130
- `async_impl`
117
131
118
132
An async implementation should on compile on async implementation and
119
- must simply disappear when we want sync version.
120
-
121
- To avoid having "Send" and "Sync" bounds placed on the async trait
122
- methods, invoke the maybe_async macro as # [async_impl(?Send)].
133
+ must simply disappear when we want sync version.
123
134
135
+ There are three usage variants for `async_impl` attribute macros:
136
+ - `#[async_impl]` or `#[async_impl(Send)]`
137
+ - `#[async_impl(?Send)]`
138
+ - `#[async_impl(AFIT)]`
124
139
125
140
- `test`
126
141
127
142
Handy macro to unify async and sync **unit and e2e test** code.
128
143
129
144
You can specify the condition to compile to sync test code
130
145
and also the conditions to compile to async test code with given test
131
- macro, e.x. `tokio::test`, `async_std::test` and etc. When only sync
146
+ macro, e.x. `tokio::test`, `async_std::test`, etc. When only sync
132
147
condition is specified,the test code only compiles when sync condition
133
148
is met.
134
149
135
150
```rust
136
- # [maybe_async::test(
151
+ # #[maybe_async::maybe_async]
152
+ # async fn async_fn() -> bool {
153
+ # true
154
+ # }
155
+
156
+ # #[maybe_async::test(
137
157
feature =" is_sync" ,
138
- async(all(not(feature="is_sync"), feature =" async_std" ), async_std::test),
139
- async(all(not(feature="is_sync"), feature =" tokio" ), tokio::test)
158
+ async(
159
+ all(not(feature="is_sync"), feature =" async_std" ),
160
+ async_std::test
161
+ ),
162
+ async(
163
+ all(not(feature="is_sync"), feature =" tokio" ),
164
+ tokio::test
165
+ )
140
166
)]
141
167
async fn test_async_fn() {
142
168
let res = async_fn().await;
143
169
assert_eq!(res, true);
144
170
}
145
171
```
146
172
147
- # # What's Under the Hook
173
+ # ## What's Under the Hook
148
174
149
175
`maybe-async` compiles your code in different way with the `is_sync` feature
150
- gate. It remove all `await` and `async` keywords in your code under
176
+ gate. It removes all `await` and `async` keywords in your code under
151
177
`maybe_async` macro and conditionally compiles codes under `async_impl` and
152
178
`sync_impl`.
153
179
154
- Here is an detailed example on what's going on whe the `is_sync` feature
180
+ Here is a detailed example on what's going on whe the `is_sync` feature
155
181
gate set or not.
156
182
157
183
```rust
@@ -252,19 +278,21 @@ fn maybe_async_fn() -> Result<(), ()> {
252
278
}
253
279
```
254
280
255
- ## Examples
281
+ ### Examples
256
282
257
- ### rust client for services
283
+ #### rust client for services
258
284
259
285
When implementing rust client for any services, like awz3. The higher level
260
286
API of async and sync version is almost the same, such as creating or
261
- deleting a bucket, retrieving an object and etc.
287
+ deleting a bucket, retrieving an object, etc.
262
288
263
289
The example ` service_client ` is a proof of concept that ` maybe_async ` can
264
290
actually free us from writing almost the same code for sync and async. We
265
291
can toggle between a sync AWZ3 client and async one by ` is_sync ` feature
266
292
gate when we add ` maybe-async ` to dependency.
267
293
268
294
269
- # License
270
- MIT
295
+ ## License
296
+ MIT
297
+
298
+ License: MIT
0 commit comments