5个版本
0.1.5 | 2024年1月17日 |
---|---|
0.1.4 | 2023年11月21日 |
0.1.3 | 2023年6月20日 |
0.1.2 | 2023年6月15日 |
0.1.0 | 2023年6月14日 |
#122 在 过程宏 中
在 7 个Crates中使用(通过 wagon-macros)
58KB
623 行
extendable-data
一个Rust宏,允许您指定可以“扩展”或继承的数据。在这里,我所说的数据具体指的是 enum
、struct
和 union
。
为什么不使用组合/特质/其他方法
这个项目开始是因为我正在使用非常不错的 logos 库,并且我想定义2个具有一些相同基本标记的词法分析器,但需要进行扩展。我没有找到一种合适的方法来做这件事,而不只是简单地复制粘贴我需要的枚举的部分,所以我开始用宏滥用来修补它。然后我将用于枚举的方法扩展到也支持结构体和联合体。
如何使用
简单地,定义您想要使用的基枚举(这里我们使用 A
)。然后向它添加 #[extendable_data]
属性。这将自动生成一个新的名为 extend_from_A
的宏(或者您可以在属性参数中指定名称)。现在,使用这个新的宏为扩展枚举 B
。
示例
use extendable_data::extendable_data;
#[extendable_data(extend_a)]
enum A {
One,
Two,
Three
}
在一个设置为true的 proc-macro
的crate中(我们在这里使用 X
),然后
use X::extend_a;
#[extend_a]
enum B {
Four,
Five,
Six
}
|
V
enum B {
One,
Two,
Three,
Four,
Five,
Six
}
任何用于枚举 A
和 B
的定义中使用的属性和泛型都会合并并复制过来。对于名称和可见性,只使用 B
的那些,并且直接复制过来。
A
编写的任何文档都会用于结果过程宏,而 B
的任何文档都会用于结果数据结构。
筛选
您可以在使用 extend_from_*
宏时提供一组字段进行筛选。它的工作原理如下
#[extend_a(filter(Two))]
enum B {
Four
}
|
V
enum B {
One,
Three,
Four
}
您还可以使用筛选来指定要从原始数据结构中移除的特定属性。(例如,如果这个新版本不再应该使用 derive
)。
如果您正在使用 merge_on_conflict
参数(见下文)并且想从一个字段中删除一个属性,您必须通过再次写入该字段来强制冲突解决。
#[extendable_data]
enum A {
#[attr]
One,
Two
}
...
#[extend_from_A(filter(attr))]
enum B {
One
}
|
V
enum B {
One,
Two
}
冲突解决
默认情况下,如果发生名称冲突(即,在 A
和 B
中都有字段 X
),则 B
中的字段将完全覆盖 A
中的字段。您可以选择将 merge_on_conflict
参数传递给 extend_from_*
,以便库尝试合并字段。合并仅在 enum
中可能,因为 Rust 中不支持嵌套的结构体和联合体。
结构体
与枚举和联合体不同,并不是所有结构体类型都有意义地组合在一起。因此,做出了以下设计决策:
- 将两个命名结构体合并仅生成一个新的命名结构体。
- 将两个未命名结构体合并仅生成一个新的未命名结构体。
- 将两个单元结构体合并仅生成一个新的单元结构体。
- 将单元结构体与其他任何类型合并都将生成另一种类型的结构体(例如,一个单元与一个命名合并将生成一个命名),无论哪个是“父”。
技术上,您可以允许合并命名和未命名的结构体,或者在合并单元结构体时让父更相关,但前者会比这个库已经做的更促进丑陋的编码习惯,后者似乎是一个更不常见的用例。
依赖项
~300–760KB
~18K SLoC