1- # Maybe-Async Procedure Macro
1+ ![ Maintenance] ( https://img.shields.io/badge/maintenance-activly--developed-brightgreen.svg )
2+
3+ # maybe-async
24
35** Why bother writing similar code twice for blocking and async code?**
46
@@ -24,16 +26,16 @@ those `async` and `await` when you need a blocking code.
2426
2527These procedural macros can be applied to the following codes:
2628- trait item declaration
27- - trait implmentation
29+ - trait implementation
2830- function definition
2931- struct definition
3032
3133** RECOMMENDATION** : Enable ** resolver ver2** in your crate, which is
3234introduced 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 .
3436
3537
36- ## Motivation
38+ ### Motivation
3739
3840The async/await language feature alters the async world of rust.
3941Comparing with the map/and_then style, now the async code really resembles
@@ -42,9 +44,9 @@ sync version code.
4244In many crates, the async and sync version of crates shares the same API,
4345but the minor difference that all async code must be awaited prevent the
4446unification 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 .
4648
47- ## Macros in Detail
49+ ### Macros in Detail
4850
4951` maybe-async ` offers 4 set of attribute macros: ` maybe_async ` ,
5052` 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
7173 maybe_async = " 0.2"
7274 ```
7375
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
7577 an `is_sync` feature gate. In this way, `maybe_async` is the same as
7678 `must_be_sync`:
7779
@@ -80,19 +82,31 @@ blocking code except for async/await keywords. And use feature gate
8082 maybe_async = { version = " 0.2" , features = [" is_sync" ] }
8183 ```
8284
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.
8797
98+ - `#[maybe_async(AFIT)]`
99+
100+ AFIT is acronym for **a**sync **f**unction **i**n **t**rait, stabilized from rust 1.74
88101
89102- `must_be_async`
90103
91- **Keep async**. Add `async_trait` attribute macro for trait declaration
92- or implementation to bring async fn support in traits.
104+ **Keep async**.
93105
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)]`
96110
97111- `must_be_sync`
98112
@@ -102,56 +116,68 @@ blocking code except for async/await keywords. And use feature gate
102116
103117- `sync_impl`
104118
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.
107121
108122 Although most of the API are almost the same, there definitely come to a
109123 point when the async and sync version should differ greatly. For
110124 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.
112126
113127 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.
115129
116130- `async_impl`
117131
118132 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.
123134
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)]`
124139
125140- `test`
126141
127142 Handy macro to unify async and sync **unit and e2e test** code.
128143
129144 You can specify the condition to compile to sync test code
130145 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
132147 condition is specified,the test code only compiles when sync condition
133148 is met.
134149
135150 ```rust
136- # [maybe_async::test(
151+ # #[maybe_async::maybe_async]
152+ # async fn async_fn() -> bool {
153+ # true
154+ # }
155+
156+ # #[maybe_async::test(
137157 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+ )
140166 )]
141167 async fn test_async_fn() {
142168 let res = async_fn().await;
143169 assert_eq!(res, true);
144170 }
145171 ```
146172
147- # # What's Under the Hook
173+ # ## What's Under the Hook
148174
149175`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
151177`maybe_async` macro and conditionally compiles codes under `async_impl` and
152178`sync_impl`.
153179
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
155181gate set or not.
156182
157183```rust
@@ -252,19 +278,21 @@ fn maybe_async_fn() -> Result<(), ()> {
252278}
253279```
254280
255- ## Examples
281+ ### Examples
256282
257- ### rust client for services
283+ #### rust client for services
258284
259285When implementing rust client for any services, like awz3. The higher level
260286API 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.
262288
263289The example ` service_client ` is a proof of concept that ` maybe_async ` can
264290actually free us from writing almost the same code for sync and async. We
265291can toggle between a sync AWZ3 client and async one by ` is_sync ` feature
266292gate when we add ` maybe-async ` to dependency.
267293
268294
269- # License
270- MIT
295+ ## License
296+ MIT
297+
298+ License: MIT
0 commit comments