6个版本
0.2.4 | 2024年6月14日 |
---|---|
0.2.3 | 2022年8月3日 |
0.2.1 | 2022年7月18日 |
0.2.0 | 2022年6月24日 |
0.1.1 | 2022年6月21日 |
#76 in 过程宏
13,423每月下载量
在 18 个crate中使用 (17 直接)
145KB
2.5K SLoC
Maybe-Async-Cfg过程宏
为什么要在同步和异步代码中重复编写类似的代码呢?
当在crate中实现API的同步和异步版本时,这两个版本的大多数API几乎相同,只是异步版本中包含一些异步/等待关键字。
maybe-async-cfg
通过 过程宏 帮助统一异步和同步实现。
- 使用正常的
async
、await
编写异步代码,当您需要阻塞代码时,让maybe_async_cfg
处理那些async
和await
。 - 添加
maybe
属性并指定基于功能的条件,根据这些条件生成同步或异步代码。 - 如果需要,使用
only_if
(或remove_if
) 保持指定版本的代码。
可以将 maybe
过程宏应用于以下代码
- 使用声明
- trait声明
- trait实现
- 函数定义
- struct和enum定义
- 模块
建议:在您的crate中启用 resolver ver2,这是Rust 1.51中引入的。如果不启用,则与具有冲突版本(一个异步和一个阻塞)的依赖项的crate可能会编译失败。
动机
异步/等待语言功能改变了Rust的异步世界。与map/and_then样式相比,现在的异步代码真的类似于同步版本代码。
在许多crate中,异步和同步版本的crate共享相同的API,但由于所有异步代码都必须等待,这阻止了异步和同步代码的统一。换句话说,我们被迫分别编写异步和同步实现。
宏的详细信息
要使用maybe-async-cfg
,我们必须知道哪些代码块仅在同步实现中使用,哪些在异步中使用。这两个版本的实现应该共享相同的函数签名,除了async/await关键字。
使用maybe
宏来处理在异步和同步版本中相同(除了async/await关键字)的代码。在宏参数中指定代码的异步和/或同步版本应该出现的条件(基于特性)。
-
属性宏
maybe
提供了一种统一的方式,根据特性在需要时提供同步和异步转换,启用您的crate,并采用
async first
策略。[dependencies] maybe_async_cfg = "0.2" [features] use_sync = [] use_async = []
在此示例及所有后续示例中,我们使用两个特性。但您可以使用任何方便的条件,例如,将
feature="use_sync"
替换为not(feature="use_async")
。请随意使用,maybe-async-cfg
不会以任何方式分析条件,只是原样替换它们。在代码的不同版本(同步或异步)中需要更改的所有项之前添加
maybe
属性。想要保留异步代码?指定当您的代码应该是异步时的条件(基于特性)的
async
参数。想将异步代码转换为同步?指定生成同步代码时的条件
sync
参数。#[maybe_async_cfg::maybe( idents(Foo), sync(feature="use_sync"), async(feature="use_async") )] struct Struct { f: Foo, }
转换后
#[cfg(feature="use_sync")] struct StructSync { f: FooSync, } #[cfg(feature="use_async")] struct StructAsync { f: FooAsync, }
-
过程宏
content
content
宏允许您为许多maybe
宏指定通用参数。在content
宏内部使用所需的参数的default
属性。maybe_async_cfg::content!{ #![maybe_async_cfg::default( idents(Foo, Bar), )] #[maybe_async_cfg::maybe( sync(feature="use_sync"), async(feature="use_async") )] struct Struct { f: Foo, } #[maybe_async_cfg::maybe( sync(feature="use_sync"), async(feature="use_async") )] async fn func(b: Bar) { todo!() } } // content!
转换后
#[cfg(feature="use_sync")] struct StructSync { f: FooSync, } #[cfg(feature="use_async")] struct StructAsync { f: FooAsync, } #[cfg(feature="use_sync")] fn func_sync(b: BarSync) { todo!() } #[cfg(feature="use_async")] async fn func_async(b: BarAsync) { todo!() }
文档测试
在编写文档测试时,您可以标记它们仅适用于相应的代码版本。为此,在文档测试属性中指定only_if(
VARIANT_KEY)
。然后在其他所有代码版本中,此文档测试将被替换为空字符串。
#[maybe_async_cfg::maybe(
idents(Foo),
sync(feature="use_sync"),
async(feature="use_async")
)]
/// This is a structure.
/// ```rust, only_if(sync)
/// let s = StructSync{ f: FooSync::new() };
/// ```
/// ```rust, only_if(async)
/// let s = StructAsync{ f: FooAsync::new().await };
/// ```
struct Struct {
f: Foo,
}
转换后
#[cfg(feature="use_sync")]
/// This is a structure.
/// ```rust, only_if(sync)
/// let s = StructSync{ f: FooSync::new() };
/// ```
///
struct StructSync {
f: FooSync,
}
#[cfg(feature="use_async")]
/// This is a structure.
///
/// ```rust, only_if(async)
/// let s = StructAsync{ f: FooAsync::new().await };
/// ```
struct StructAsync {
f: FooAsync,
}
示例
服务客户端的rust实现
当实现任何服务的rust客户端时,例如awz3。异步和同步版本的API几乎相同,例如创建或删除存储桶、检索对象等。
示例service_client
是maybe-async-cfg
可以真正让我们摆脱编写几乎相同的同步和异步代码的证明。当我们将maybe-async-cfg
添加到依赖项时,可以通过添加is_sync
特性门来在同步AWZ3客户端和异步客户端之间切换。
致谢
这个crate是基于以下这些优秀的crate重新设计的分支
谢谢!
许可证
MIT
依赖项
~1.2–1.7MB
~40K SLoC