maybe_async_cfg/
lib.rs

1//!
2//! # Maybe-Async-Cfg Procedure Macro
3//!
4//! **Why bother writing similar code twice for blocking and async code?**
5//!
6//! [![Build Status](https://github.com/nvksv/maybe-async-cfg/actions/workflows/rust.yml/badge.svg?branch=main)](https://github.com/nvksv/maybe-async-cfg/actions)
7//! [![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
8//! [![Latest Version](https://img.shields.io/crates/v/maybe-async-cfg.svg)](https://crates.io/crates/maybe-async-cfg)
9//! [![maybe-async](https://docs.rs/maybe-async-cfg/badge.svg)](https://docs.rs/maybe-async-cfg)
10//!
11//! When implementing both sync and async versions of API in a crate, most API of the two version
12//! are almost the same except for some async/await keyword.
13//!
14//! `maybe-async-cfg` help unifying async and sync implementation by **procedural macro**.
15//! - Write async code with normal `async`, `await`, and let `maybe_async_cfg` handles those `async`
16//! and `await` when you need a blocking code.
17//! - Add `maybe` attributes and specify feature-based conditions under which sync or async code 
18//! should be generated.
19//! - Use `only_if` (or `remove_if`) to keep code in specified version if necessary.
20//!
21//! The `maybe` procedural macro can be applied to the following codes:
22//! - use declaration
23//! - trait declaration
24//! - trait implementation
25//! - function definition
26//! - struct and enum definition
27//! - modules
28//!
29//! **RECOMMENDATION**: Enable **resolver ver2** in your crate, which is introduced in Rust 1.51. If
30//! not, two crates in dependency with conflict version (one async and another blocking) can fail
31//! compilation.
32//!
33//!
34//! ## Motivation
35//!
36//! The async/await language feature alters the async world of rust. Comparing with the map/and_then
37//! style, now the async code really resembles sync version code.
38//!
39//! In many crates, the async and sync version of crates shares the same API, but the minor
40//! difference that all async code must be awaited prevent the unification of async and sync code.
41//! In other words, we are forced to write an async and an sync implementation respectively.
42//!
43//!
44//! ## Macros in Detail
45//!
46//! To use `maybe-async-cfg`, we must know which block of codes is only used on sync implementation,
47//! and which on async. These two versions of the implementation should share the same function
48//! signatures except for async/await keywords.
49//!
50//! Use `maybe` macro for code that is the same in both async and sync versions except for
51//! async/await keywords. Specify in the macro parameters the conditions (based on features) under
52//! which async and/or sync versions of the code should appear.
53//!
54//! - attribute macro **`maybe`**
55//!
56//!     Offers a unified way to provide sync and async conversion on demand depending on features,
57//! enabled for your crate, with **async first** policy.
58//!
59//!     ```toml
60//!     [dependencies]
61//!     maybe_async_cfg = "0.2"
62//!
63//!     [features]
64//!     use_sync = []
65//!     use_async = []
66//!     ```
67//!
68//!     In this and all the following examples, we use two features. But you can use any conditions
69//! that are convenient for you, for example, replacing `feature="use_sync"` with
70//! `not(feature="use_async")` everywhere. Feel free, `maybe-async-cfg` does not analyze the
71//! conditions in any way, just substituting them as is.
72//!
73//!     Add the `maybe` attribute before all the items that need to be changed in different versions
74//! of the code (sync or async).
75//!
76//!     Want to keep async code? Specify the `async` parameter with the condition (based on
77//! features) when your code should be async.
78//!
79//!     Wanna convert async code to sync? Specify the `sync` parameter with the condition when the
80//! sync code should be generated.
81//!
82//!     ```rust
83//!     #[maybe_async_cfg::maybe(
84//!         idents(Foo),
85//!         sync(feature="use_sync"),
86//!         async(feature="use_async")
87//!     )]
88//!     struct Struct {
89//!         f: Foo,
90//!     }
91//!     ```
92//!     After conversion:
93//!     ```rust
94//!     #[cfg(feature="use_sync")]
95//!     struct StructSync {
96//!         f: FooSync,
97//!     }
98//!     #[cfg(feature="use_async")]
99//!     struct StructAsync {
100//!         f: FooAsync,
101//!     }
102//!     ```
103//!
104//! - procedural macro **`content`**
105//!
106//!     The `content` macro allows you to specify common parameters for many `maybe` macros. Use the
107//! internal `default` attribute with the required parameters inside the `content` macro.
108//!
109//!     ```rust
110//!     maybe_async_cfg::content!{
111//!     #![maybe_async_cfg::default(
112//!         idents(Foo, Bar),
113//!     )]
114//!
115//!     #[maybe_async_cfg::maybe(
116//!         sync(feature="use_sync"), 
117//!         async(feature="use_async")
118//!     )]
119//!     struct Struct {
120//!         f: Foo,
121//!     }
122//!
123//!     #[maybe_async_cfg::maybe(
124//!         sync(feature="use_sync"), 
125//!         async(feature="use_async")
126//!     )]
127//!     async fn func(b: Bar) {
128//!         todo!()
129//!     }
130//!     } // content!
131//!     ```
132//!     After conversion:
133//!     ```rust
134//!     #[cfg(feature="use_sync")]
135//!     struct StructSync {
136//!         f: FooSync,
137//!     }
138//!     #[cfg(feature="use_async")]
139//!     struct StructAsync {
140//!         f: FooAsync,
141//!     }
142//!
143//!     #[cfg(feature="use_sync")]
144//!     fn func_sync(b: BarSync) {
145//!         todo!()
146//!     }
147//!     #[cfg(feature="use_async")]
148//!     async fn func_async(b: BarAsync) {
149//!         todo!()
150//!     }
151//!     ```
152//!
153//! ## Doctests
154//!     
155//! When writing doctests, you can mark them as applicable only in the corresponding code version. 
156//! To do this, specify `only_if(`_VARIANT_KEY_`)` in the doctest attributes. Then in all other
157//! versions of the code, this doctest will be replaced with an empty string.
158//! 
159//! ```rust
160//! #[maybe_async_cfg::maybe(
161//!     idents(Foo),
162//!     sync(feature="use_sync"),
163//!     async(feature="use_async")
164//! )]
165//! /// This is a structure. 
166//! /// ```rust, only_if(sync)
167//! /// let s = StructSync{ f: FooSync::new() };
168//! /// ```
169//! /// ```rust, only_if(async)
170//! /// let s = StructAsync{ f: FooAsync::new().await };
171//! /// ```
172//! struct Struct {
173//!     f: Foo,
174//! }
175//! ```
176//! After conversion:
177//! ```rust
178//! #[cfg(feature="use_sync")]
179//! /// This is a structure. 
180//! /// ```rust, only_if(sync)
181//! /// let s = StructSync{ f: FooSync::new() };
182//! /// ```
183//! ///
184//! struct StructSync {
185//! f: FooSync,
186//! }
187//! #[cfg(feature="use_async")]
188//! /// This is a structure. 
189//! ///
190//! /// ```rust, only_if(async)
191//! /// let s = StructAsync{ f: FooAsync::new().await };
192//! /// ```
193//! struct StructAsync {
194//!     f: FooAsync,
195//! }
196//! ```
197//!
198//! ## Examples
199//!
200//! ### rust client for services
201//!
202//! When implementing rust client for any services, like awz3. The higher level API of async and
203//! sync version is almost the same, such as creating or deleting a bucket, retrieving an object and
204//! etc.
205//!
206//! The example `service_client` is a proof of concept that `maybe_async_cfg` can actually free us
207//! from writing almost the same code for sync and async. We can toggle between a sync AWZ3 client
208//! and async one by `is_sync` feature gate when we add `maybe-async-cfg` to dependency.
209//!
210//! 
211//! ## Acknowledgements
212//! 
213//! This crate is a redesigned fork of these wonderful crates:
214//! 
215//! - [fMeow/maybe-async-rs](https://github.com/fMeow/maybe-async-rs)
216//! 
217//! - [marioortizmanero/maybe-async-rs](https://github.com/marioortizmanero/maybe-async-rs)
218//! 
219//! Thanks!
220//!
221//! 
222//! # License
223//! MIT
224#![deny(missing_docs)]
225#![deny(rustdoc::missing_crate_level_docs)]
226
227// note: the `rustdoc::missing_doc_code_examples` lint is unstable
228//#![deny(rustdoc::missing_doc_code_examples)]
229
230use manyhow::manyhow;
231use proc_macro::TokenStream;
232
233mod macros;
234mod params;
235mod utils;
236mod visit_ext;
237mod visitor_async;
238mod visitor_content;
239
240#[cfg(feature = "doctests")]
241mod doctests;
242
243mod debug;
244
245const DEFAULT_CRATE_NAME: &'static str = "maybe_async_cfg";
246const MACRO_MAYBE_NAME: &'static str = "maybe";
247const MACRO_ONLY_IF_NAME: &'static str = "only_if";
248const MACRO_REMOVE_IF_NAME: &'static str = "remove_if";
249const MACRO_NOOP_NAME: &'static str = "noop";
250const MACRO_REMOVE_NAME: &'static str = "remove";
251const MACRO_DEFAULT_NAME: &'static str = "default";
252
253const STANDARD_MACROS: &'static [&'static str] = &[
254    "dbg",
255    "print",
256    "println",
257    "assert",
258    "assert_eq",
259    "assert_ne",
260];
261
262/// Marks the code that can be presented in several versions. 
263/// 
264/// ### The `maybe` macro has the following parameters:
265///
266/// - `disable` 
267/// 
268///     The macro with `disable` parameter will do nothing, like `noop`. Use it to write and debug 
269/// initial async code.
270///
271/// - `prefix` 
272/// 
273///     The name of `maybe-async-cfg` crate. If not set, `"maybe_async_cfg"` will be used.
274///
275/// - `sync`, `async` 
276/// 
277///     Defines versions of the code: the item to which the attribute `maybe` refers will be 
278/// replaced with multiple copies (one for each version), which will be modified according to 
279/// the version kind and its parameters.
280///
281///     For the `sync` version, the item will be converted from async to sync code by deleting
282/// the `async` and `await` keywords. The types `Future<Output=XXX>` will also be replaced with just
283/// `XXX`. For the  `async` version, the item will be left async.
284///
285///     In any case, the item will be converted according to all the parameters described below. For
286/// functions, structs/enums and traits, the name will be changed as if it is mentioned in the
287/// `idents` list (if it is not explicitly specified there and if `keep_self` is not present).
288///
289/// - All other parameters will be passed to all versions (with merging).
290///
291///     Therefore, those parts of the version parameters that match in all versions can be specified
292/// here. For example, this is the expected behavior for the `idents` list.
293///
294/// ### Every version has the following parameters:
295///
296/// - `disable` 
297/// 
298///     Ignore this version entirely.
299/// 
300/// - `key`
301///
302///     Defines unique name of the version to use it in `only_if`/`remove_if` conditions. If 
303/// omitted, `sync`/`async` will be used.
304///
305///     ```rust
306///     #[maybe_async_cfg::maybe(
307///         sync(key="foo", feature="use_sync"),
308///         async(key="bar", feature="use_async"),
309///     )]
310///     struct Struct {
311///         f: usize,
312///         
313///         // This field will only be present in sync version
314///         #[maybe_async_cfg::only_if(key="foo")]
315///         sync_only_field: bool,
316///     }
317///     ```
318///     After conversion:
319///     ```rust 
320///     #[cfg(feature="use_sync")]
321///     struct StructSync {
322///         f: usize,
323///         sync_only_field: bool,
324///     }
325///     #[cfg(feature="use_async")]
326///     struct StructAsync {
327///         f: usize,
328///     }
329///     ```
330/// 
331/// - `cfg`
332/// 
333///     Defines the condition (based on features), under which the current version should appear.
334/// 
335///     Note: conditions like `feature = "..."`, `not(...)`, `all(...)`, `any(...)` will be 
336/// processed correctly, even if the `cfg(...)` was omitted.
337///
338///     ```rust
339///     #[maybe_async_cfg::maybe(
340///         sync(cfg(feature="use_sync")),
341///         async(feature="use_async")
342///     )]
343///     struct Struct {
344///         f: Foo,
345///     }
346///     ```
347///     After conversion:
348///     ```rust
349///     #[cfg(feature="use_sync")]
350///     struct StructSync {
351///         f: Foo,
352///     }
353///     #[cfg(feature="use_async")]
354///     struct StructAsync {
355///         f: Foo,
356///     }
357///     ```
358///  
359/// - `idents` 
360/// 
361///     Defines a list of identifiers that should be renamed depending on the version of the code.
362///
363///     Each identifier can have the following clarifying parameters:
364///
365///     - `snake`, `fn`, `mod`
366///
367///         means that this is the snake-case name of the function or module and it should be 
368/// converted by adding the suffixes `"_sync"`/`"_async"` (otherwise, the suffixes 
369/// `"Sync"`/`"Async"` will be used).
370///
371///     - `use`  
372/// 
373///         in `use` lists, using this identifier will result in renaming via the `as` expression, 
374/// rather than a simple replacement as is. In other cases, a simple replacement will be used.
375///
376///     - `keep`
377///
378///         this identifier will not be converted anywhere
379///
380///     - `sync`, `async`
381///
382///         specifies the name that will be used in the corresponding version of the code. Overrides
383/// the standard scheme of suffixes used by default. If the parameter value is omitted, 
384/// the identifier will not be renamed in this case.
385///
386///     ```rust
387///     #[maybe_async_cfg::maybe(
388///         idents(
389///             Foo,
390///             Bar,
391///             baz(fn),
392///             Qux(use),
393///             waldo(sync, async="async_waldo"),
394///             xyzzy(fn, use, sync="xizzy_the_sync_func"),
395///         ),
396///         sync(feature="use_sync"),
397///         async(feature="use_async"),
398///     )]
399///     async fn func() {
400///         struct Foo {}
401///         use extcrate::{
402///             Bar,
403///             baz,
404///             Qux,
405///             waldo::{
406///                 plugh,
407///                 xyzzy
408///             }
409///         };
410///         let _ = baz( Foo {}, Bar::new() ).await;
411///         let _ = xizzy( Qux::flob(b).await );
412///     }
413///     ```
414///     After conversion:
415///     ```rust
416///     #[cfg(feature="use_sync")]
417///     fn func_sync() {
418///         struct FooSync {}
419///         use extcrate::{
420///             BarSync,
421///             baz_sync,
422///             Qux as QuxSync,
423///             waldo::{
424///                 plugh,
425///                 xyzzy as xizzy_the_sync_func
426///             }
427///         };         
428///         let _ = baz_sync( FooSync {}, BarSync::new() );
429///         let _ = xizzy_the_sync_func( QuxSync::flob() );
430///     }
431///     #[cfg(feature="use_async")]
432///     async fn func_async() {
433///         struct FooAsync {}
434///         use extcrate::{
435///             BarAsync,
436///             baz_async,
437///             Qux as QuxAsync,
438///             async_waldo::{
439///                 plugh,
440///                 xyzzy as xyzzy_async
441///             }
442///         };
443///         let _ = baz_async( FooAsync {}, BarAsync::new() ).await;
444///         let _ = xyzzy_async( QuxAsync::flob().await );     
445///     }
446///     ```
447///
448/// - `keep_self`
449///
450///     Do not change name of item to which attribute `maybe` refers.
451///
452/// - `self`
453/// 
454///     Defines the name that will be assigned to the item in this variant.
455/// 
456/// - `send`
457///
458///     If `send = "Send"` or `send = "true"` is present, the attribute
459/// `#[async_trait::async_trait]` will be added before the async code. If `send = "?Send"` or
460/// `send = "false"` then `#[async_trait::async_trait(?Send)]` will be added.  
461/// 
462/// - `drop_attrs`
463///
464///     Remove any attributes with specified names.
465///
466///     ```rust
467///     #[maybe_async_cfg::maybe(
468///         sync(feature="use_sync", drop_attrs(attr)),
469///         async(feature="use_async"),
470///     )]
471///     struct Struct {
472///         f: usize,
473///
474///         // This attribute will be removed in sync version
475///         #[attr(param)]
476///         field1: bool,
477///     }
478///     ```
479///     After conversion:
480///     ```rust
481///     #[cfg(feature="use_sync")]
482///     struct StructSync {
483///         f: usize,
484///         field1: bool,
485///     }
486///     #[cfg(feature="use_async")]
487///     struct StructAsync {
488///         f: usize,
489///         #[attr(param)]
490///         field1: bool,
491///     }
492///     ```
493///
494/// - `replace_features`
495///
496///     Replace one feature name with another.
497///
498///     ```rust
499///     #[maybe_async_cfg::maybe(
500///         sync(feature="use_sync", replace_feature("secure", "secure_sync")),
501///         async(feature="use_async"),
502///     )]
503///     struct Struct {
504///         f: usize,
505///         // In sync version "secure" feature will be replaced with "secure_sync" feature
506///         #[cfg(feature="secure")]
507///         field: bool,
508///     }
509///     ```
510///     After conversion:
511///     ```rust
512///     #[cfg(feature="use_sync")]
513///     struct StructSync {
514///         f: usize,
515///         #[cfg(feature="secure_sync")]
516///         field: bool,
517///     }
518///     #[cfg(feature="use_async")]
519///     struct StructAsync {
520///         f: usize,
521///         #[cfg(feature="secure")]
522///         field: bool,
523///     }
524///     ```
525///
526/// - `inner`, `outer`
527///
528///     Adds some attributes to the generated code. Inner attributes will appear below attribute 
529/// `#[cfg(...)]`, outer attributes will appear above it.
530/// 
531///     Note: if the version parameter is not parsed as a parameter of some other type, it will be 
532/// interpreted as an inner attribute.
533/// 
534///     Useful for testing: just write `test` in version parameters.
535///
536///     ```rust
537///     #[maybe_async_cfg::maybe(
538///         sync(feature="secure_sync", test, "resource(path = \"/foo/bar\")", outer(xizzy)),
539///         async(feature="secure_sync", inner(baz(qux), async_attributes::test)),
540///     )]
541///     async fn test_func() {
542///         todo!()
543///     }
544///     ```
545///     After conversion:
546///     ```rust
547///     #[xizzy]
548///     #[cfg(feature="use_sync")]
549///     #[test]
550///     #[resource(path = "/foo/bar")]
551///     fn test_func_sync() {
552///         todo!()
553///     }
554///     #[cfg(feature="use_async")]
555///     #[baz(qux)]
556///     #[async_attributes::test]
557///     async fn test_func_async() {
558///         todo!()
559///     }
560///     ```
561/// 
562/// - In other cases, the following rules apply:
563///     
564///     - name-value pairs (`xxx = "yyy"`) with a name other than `key`, `prefix`, `send` and
565/// `feature` will produce an error.
566///     
567///     - `feature = "..."`, `not(...)`, `all(...)`, `any(...)` will be interpreted as condition for
568/// current version (as wrapped in `cfg(...)`).
569/// 
570///     - all another parameters will be interpreted as inner attribute for current version (as 
571/// wrapped in `inner(...)`).
572/// 
573/// ### Formal syntax
574/// 
575/// > _ParametersList_ :\
576/// > &nbsp;&nbsp;&nbsp;&nbsp;_Parameter_ (`,` _Parameter_)<sup>\*</sup>
577/// > 
578/// > _Parameter_ :\
579/// > &nbsp;&nbsp;&nbsp;&nbsp;`disable`\
580/// > &nbsp;&nbsp;|&nbsp;`keep_self`\
581/// > &nbsp;&nbsp;|&nbsp;`prefix` `=` _STRING_LITERAL_\
582/// > &nbsp;&nbsp;|&nbsp;(`sync` | `async`) `(` _VersionParametersList_ `)`\
583/// > &nbsp;&nbsp;|&nbsp;`idents` `(` _IdentsList_ `)`\
584/// >
585/// > _VersionParametersList_ :\
586/// > &nbsp;&nbsp;&nbsp;&nbsp;_VersionParameter_ (`,` _VersionParameter_)<sup>\*</sup>
587/// > 
588/// > _VersionParameter_ :\
589/// > &nbsp;&nbsp;&nbsp;&nbsp;`disable`\
590/// > &nbsp;&nbsp;|&nbsp;`keep_self`\
591/// > &nbsp;&nbsp;|&nbsp;`key` `=` _STRING_LITERAL_\
592/// > &nbsp;&nbsp;|&nbsp;`feature` `=` _STRING_LITERAL_\
593/// > &nbsp;&nbsp;|&nbsp;`self` `=` _STRING_LITERAL_\
594/// > &nbsp;&nbsp;|&nbsp;`send` `=` (`""` | `"Send"` | `"true"` | `"?Send"` | `"false"`)\
595/// > &nbsp;&nbsp;|&nbsp;(`cfg` | `any` | `all` | `not`) `(` _ANY_CFG_CONDITION_ `)`\
596/// > &nbsp;&nbsp;|&nbsp;`idents` `(` _IdentsList_ `)`\
597/// > &nbsp;&nbsp;|&nbsp;(`outer` | `inner`) `(` _AttributesList_ `)`\
598/// > &nbsp;&nbsp;|&nbsp;`replace_feature` `(` _STRING_LITERAL_ `,` _STRING_LITERAL_ `)`\
599/// > &nbsp;&nbsp;|&nbsp;`drop_attrs` `(` _IdentifiersList_ `)`\
600/// > &nbsp;&nbsp;|&nbsp;_Attribute_
601/// >
602/// > _Path_ :\
603/// > &nbsp;&nbsp;&nbsp;&nbsp;_IDENTIFIER_ (`::` _IDENTIFIER_)<sup>\+</sup>
604/// >
605/// > _IdentifiersList_ :\
606/// > &nbsp;&nbsp;&nbsp;&nbsp;_IDENTIFIER_ (`,` _IDENTIFIER_)<sup>\*</sup>
607/// >
608/// > _IdentsList_ :\
609/// > &nbsp;&nbsp;&nbsp;&nbsp;_Ident_ (`,` _Ident_)<sup>\*</sup>
610/// >
611/// > _Ident_ :\
612/// > &nbsp;&nbsp;&nbsp;&nbsp;_IDENTIFIER_ (`(` _IdentParametersList_ `)`)<sup>\?</sup>
613/// > 
614/// > _IdentParametersList_ :\
615/// > &nbsp;&nbsp;&nbsp;&nbsp;_IdentParameter_ (`,` _IdentParameter_)<sup>\*</sup>
616/// >
617/// > _IdentParameter_ :\
618/// > &nbsp;&nbsp;&nbsp;&nbsp;`keep`\
619/// > &nbsp;&nbsp;|&nbsp;`use`\
620/// > &nbsp;&nbsp;|&nbsp;(`snake` | `fn` | `mod` )\
621/// > &nbsp;&nbsp;|&nbsp;`use`\
622/// > &nbsp;&nbsp;|&nbsp;(`sync` | `async` | _IDENTIFIER_) (`=` _STRING_LITERAL_)<sup>\?</sup>
623/// >
624/// > _AttributesList_ :\
625/// > &nbsp;&nbsp;&nbsp;&nbsp;_Attribute_ (`,` _Attribute_)<sup>\*</sup>
626/// >
627/// > _Attribute_ :\
628/// > &nbsp;&nbsp;&nbsp;&nbsp;(_IDENTIFIER_ | _Path_) (`(` _ANY_VALID_ARGS_ `)`)<sup>\?</sup>\
629/// > &nbsp;&nbsp;|&nbsp;_STRING_LITERAL_
630#[manyhow]
631#[proc_macro_attribute]
632pub fn maybe(args: TokenStream, input: TokenStream) -> syn::Result<TokenStream> {
633    macros::maybe(args, input)
634}
635
636/// Marks conditional content that should only be used in the specified version of the code.
637#[manyhow]
638#[proc_macro_attribute]
639pub fn only_if(_: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
640    Ok(body)
641}
642
643/// Marks conditional content that should be used in all versions of the code except the specified 
644/// one.
645#[manyhow]
646#[proc_macro_attribute]
647pub fn remove_if(_: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
648    Ok(body)
649}
650
651/// Does nothing (leaves content intact).
652#[manyhow]
653#[proc_macro_attribute]
654pub fn noop(_: TokenStream, body: TokenStream) -> syn::Result<TokenStream> {
655    Ok(body)
656}
657
658/// Removes marked content.
659#[manyhow]
660#[proc_macro_attribute]
661pub fn remove(_: TokenStream, _: TokenStream) -> syn::Result<TokenStream> {
662    Ok(TokenStream::new())
663}
664
665/// A wrapper for code with common `maybe` parameters
666/// 
667/// The `content` macro allows you to specify common parameters for many `maybe` macros. Use the
668/// internal `default` attribute with the required parameters inside the `content` macro.
669///
670/// ```rust
671/// maybe_async_cfg::content!{
672/// #![maybe_async_cfg::default(
673///     idents(Foo, Bar),
674/// )]
675///
676/// #[maybe_async_cfg::maybe(sync(feature="use_sync"), async(feature="use_async"))]
677/// struct Struct {
678///     f: Foo,
679/// }
680///
681/// #[maybe_async_cfg::maybe(sync(feature="use_sync"), async(feature="use_async"))]
682/// async fn func(b: Bar) {
683///     todo!()
684/// }
685/// } // content!
686/// ```
687/// After conversion:
688/// ```rust
689/// #[cfg(feature="use_sync")]
690/// struct StructSync {
691///     f: FooSync,
692/// }
693/// #[cfg(feature="use_async")]
694/// struct StructAsync {
695///     f: FooAsync,
696/// }
697///
698/// #[cfg(feature="use_sync")]
699/// fn func_sync(b: BarSync) {
700///     todo!()
701/// }
702/// #[cfg(feature="use_async")]
703/// async fn func_async(b: BarAsync) {
704///     todo!()
705/// }
706/// ```
707#[manyhow]
708#[proc_macro]
709pub fn content(body: TokenStream) -> syn::Result<TokenStream> {
710    macros::content(body)
711}