2 个版本
0.1.1 | 2022年4月17日 |
---|---|
0.1.0 | 2022年3月16日 |
在 Rust 模式 中排名第 712
40KB
224 行
trait-enumizer
从特质生成枚举,提供它们之间的转换器。
特性
- 基于指定的特质(或内建实现)生成枚举,其中每个变体对应一个方法。参数对应于变体的字段;返回值对应于某个通道的发送者。
- 生成具有适当
match
的调用函数,这些函数在内部生成。这使得枚举可以“应用于”实现该特质的对象。 - 生成代理,允许通过原始特质的方法调用(如果可能)或类似原始的 API 获取枚举值的序列。代理还有助于处理“通道化”返回值。
- 处理返回值。
- 处理异步(仅限内建实现)。
- 我尝试使 crate 与
no_std
兼容,但尚未测试。
该库可以用作同步机制,也可以用作构建actor或远程过程调用的构建块。
库的主要部分是 enumizer
属性宏。它应该附加到特质或内建实现。它将输入特质或实现复制到 proc macro 输出(不带伪 derive-helpers 属性),然后还生成以下项
- 代表方法的枚举
- 零个或多个“调用函数”
- 零个或多个“代理结构体”
参数
#[trait_enumizer::enumizer(...)]
接受以下参数
name=<ident>
- 设置枚举名称。必需。pub
,pub_crate
- 分别将所有生成的项标记为pub
或pub(crate)
returnval=<macro_class_name>
- 启用更复杂的模式,其中处理返回值。会影响其他生成项(调用函数和代理)的API。输入宏用作临时代替GAT特质的特殊化。请参阅专门的README部分以获取更多信息。enum_attr
- 将自定义属性(例如enum_attr[derive(serde_derive::Serialize)]
)注入枚举声明中。可以重复。您需要使用方括号来使用它。inherent_impl
- 以固有实现为基础枚举,而不是以特质为基础。call_fn()
- 请见下文。proxy()
- 请见下文。
属性语法示例
#[trait_enumizer::enumizer(pub_crate, name=NewEnumName, call_fn(name=call_me,ref_mut,allow_panic), proxy(name=NewEnumNameProxy, Fn,unwrapping_impl))]
调用函数(call_fn
)
当您使用 call_fn()
参数时,会生成调用函数。它们使用以下子参数
name=<ident>
- 内置方法的名称。必需。ref
或ref_mut
(别名mut_ref
)或once
(别名move
) - 通过引用、可变引用或值接受对象。必需。allow_panic
- 允许在函数内部生成panic!()
调用。async
- 生成async fn
。使用send_async
伪方法从returnval
宏类代替send
。extra_arg_type(<type>)
- 向try_call
函数添加附加参数。该参数将出现在所有macro_class_name!(send(...))
回调中。
这些函数用于将枚举值“转换为”方法调用。调用函数作为生成枚举的固有实现函数生成。第一个参数是 self
。第二个参数是您指定的特质(在 inherent_impl
模式下跳过特质)的实现(或值的引用)。第三个参数在指定 extra_arg_type()
时是必需的。它传递给 returnval 的 send
(或 send_async
)伪方法,以便对返回值进行自定义处理。
示例
#[enumizer(name=QqqEnum,pub,call_fn(name=the_call,ref_mut))]
trait Qqq {
...
}
生成
enum QqqEnum { ... }
impl QqqEnum {
pub fn the_call<I: Qqq>(self, o: &mut I);
}
代理
当您使用 proxy()
参数时,会生成代理。它们使用以下子参数
Fn
、FnMut
、FnOnce
- 设置代理将携带的闭包的类型。必需。name=<ident>
- 生成结构的名称。必需的。resultified_trait=<ident>
- 除了实现try_*
函数外,还生成“resultified”特质。infallible_impl
- 如果您的 sink 函数不会出错,则使代理也实现原始特质。unwrapping_impl
- 使代理也实现原始特质,在需要的地方使用unwrap
。unwrapping_and_panicking_impl
- 强制代理实现原始特质,在由于所有权要求而失败的地方使用panic!()
调用。extra_field_type(...)
- 向代理结构添加额外的第二个字段。该字段将用作macro_class_name!(create(...))
和macro_class_name!(recv(...))
回调的额外参数。async
- 期望用户指定的闭包返回Future<Output=Result>
而不是仅仅返回Result
,并在适当的地方使用.await
。
代理是一个具有公共字段的泛型元组结构。该字段应实现 Fn
、FnMut
或 FnOnce
。如果指定了 extra_field_type()
,则创建第二个字段(也是公共的)。有两个泛型参数:错误类型(您选择它)和闭包类型。代理允许将方法调用“转换”为枚举值(这些值被传递到您的闭包)。默认情况下,所有输入方法都被重命名,以“try_”开头。通常,它们返回 Result<(), YourErrorType>
,但在 returnval
模式下,其中一些可能返回 Result<Result<T, SendError>, YourErrorType>
。存在异步模式,它将您的函数升级为返回 Future
,并将所有 try_*
方法转换为 async
。您还可以要求 Enumizer 生成代理实现“resultified”特质(当然,除非 async
)。async
还会影响 returnval
宏的使用。
您还可以要求 Enumizer 使代理实现原始特质(当然,除非 async
)。有两种策略:不可失败(如果未使用返回值,并且您的 Fn
通过使用 std::convert::Infallible
退出错误处理)和 unwrapping。
您可以将非异步原始方法转换为异步代理,反之亦然。
示例(简化版)
#[enumizer(name=QqqEnum,proxy(FnMut,name=QqqProxy))]
trait Qqq {
fn foo(&self,x : i32);
}
生成
enum QqqEnum { Foo{x:i32} }
struct QqqProxy<E,F>(pub F)
where F: FnMut(QqqEnum) -> Result<(), E>;
impl<E,F> QqqProxy where ... {
fn try_foo(&mut self, x: i32) -> Result<(), E>{
(self.0)(QqqEnum::Foo{x})
}
}
伪衍生的辅助工具
伪派生助手是此库处理的属性。您应该在输入特性和 impl 中使用它们,在方法签名之前或签名内的参数之前。其他(未知)属性将未经修改地传递。
#[enumizer_enum_attr[...]]
- 将指定的属性转发到生成的枚举。例如:#[enumizer_enum_attr[serde(rename="qqq")]]
。可以附加到函数(成为枚举变体)或函数参数(成为枚举变体字段)。#[enumizer_return_attr[...]]
- 在returnval
模式下,将自定义属性附加到枚举的ret
字段。#[enumizer_to_owned]
- 对于引用参数类型,使用所有者值而不是尝试将引用放入枚举(可能不起作用,除非'static
)。
Returnval伪特异
如果您想让 Enumizer 处理返回值,您需要一个某种类型的通道。Enumizer 在通道选择方面非常灵活。有一些内置的“类”用于一些流行的通道类型,您可能还需要自己实现一个通道类。
您将通道类指定为 returnval
参数的值,例如 returnval=trait_enumizer::flume_class
。在 Enumizer 设计的早期,通道类是使用 GAT 的特质,但现在它们是特殊的基于 macro_rules
的宏。
以下是通道类的 API
macro_rules! my_channelclass {
(Sender<$T:ty>) => { /* type of the `ret` field in enum variant */ };
(SendError) => { /* Error type when failed to send to a channel. Must not depend on T */ };
(RecvError) => { /* Error type when failed to receive from a channel */ };
(create::<$T:ty>()) => {
/* Expression returning (tx, rx) channel pair. `tx` must be of type `Sender`. */
/* Used by proxies */
};
(send::<$T:ty>($channel:expr, $msg:expr /*, $extraarg:expr */)) => {
/* Expression used to send to the channel (for `call_fn`). You may need to map error type here */
};
(recv::<$T:ty>($channel:expr /*, $extrafield:expr */)) => {
/* Expression use to recv value from the channel (for proxy) */
};
(send_async::<$T:ty>($channel:expr, $msg:expr)) => {
/* Expression to send to channel from async `call_fn`s. Should include `.await` and error mapping */
};
(recv_async::<$T:ty>($channel:expr)) => {
/* Expression to receive from cahnnel in async proxies. Should include `.await`. */
};
}
建议您根据自己的实现基于内置的通道类(例如 flume_class
)或使用 RPC 示例作为更复杂通道类的模板。
尽管 returnval 机制使用“通道”术语,但 Sender
不需要是实际的通道。它们可能是某些内部 ID,而真实的通道则作为附加参数提供。
当 Enumizer 遇到具有返回值的方法时,相应的枚举变体将获得一个名为 ret
(一个硬编码的标识符)的附加字段。该字段的类型由通道类控制,并且可能取决于返回值的类型。与该附加字段的全部交互都通过通道类的伪方法进行。
测试(也作为文档使用)
要了解该软件包的工作原理,您可以查看一些测试文件。对于大多数示例,都有一个相应的“手动”示例,显示了相同测试的展开版本(有时略有简化)。
请注意,这些链接在 Docs.rs 上可能已损坏,请使用 Github 上的 README 代替。
simple_derive.rs
- 简单演示。simple_manual.rs
- 上面的演示的扩展实现。mutable_derive.rs
,mutable_manual.rs
,move_derive.rs
,move_manual.rs
- 与之前相同,但针对可变和仅一次的情况。mixed.rs
- 展示了一些其他功能,也使用了线程来演示如何与通道交互(以flume为例)。还展示了将自定义派生注入到生成的枚举中。returnval_derive.rs
,returnval_manual_generic.rs
- 处理返回值的触发器模式。returnval_manual_flume.rs
- 原始版本,没有通道抽象,硬编码为flume。rpc.rs
- 使用serde_json和flume演示了自定义returnval=
类的先进用法,以进行远程过程调用。两个主要的Flume通道模拟了套接字,中间的Flume通道将返回值路由回调用者。toowned_manual
,toowned_derive
- 扩展(手动)和自动派生的#[enumizer_to_owned]
功能演示。inherent_derive
- 演示了inherent_impl
模式。async_derive.rs
,async_manual.rs
,async_returnval_derive.rs
,async_returnval_manual.rs
,async_rpc.rs
,async_rpc.rs
- 上面的测试的异步版本。channelclasses_showcase.rs
- 各种内置通道类。
Cargo功能
大多数功能启用相应的通道类。默认启用的std
crate功能,除了启用stdmpsc通道类外,还通过::std::borrow::ToOwned
特征启用enumizer_to_owned
。alloc
功能通过使用::alloc::borrow::ToOwned
来启用替代的enumizer_to_owned
模式(但您需要自己声明extern crate alloc;
)。std
取代了alloc
。
另请参阅
依赖关系
~1.2–2.9MB
~59K SLoC