#枚举 #结构体 #联合体 #过程宏 #可扩展 #结构体

extendable-data

一组Rust宏,允许您指定可以“扩展”或继承的数据

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

MIT 许可证

58KB
623

extendable-data

一个Rust宏,允许您指定可以“扩展”或继承的数据。在这里,我所说的数据具体指的是 enumstructunion

为什么不使用组合/特质/其他方法

这个项目开始是因为我正在使用非常不错的 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
}

任何用于枚举 AB 的定义中使用的属性和泛型都会合并并复制过来。对于名称和可见性,只使用 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
}

冲突解决

默认情况下,如果发生名称冲突(即,在 AB 中都有字段 X),则 B 中的字段将完全覆盖 A 中的字段。您可以选择将 merge_on_conflict 参数传递给 extend_from_*,以便库尝试合并字段。合并仅在 enum 中可能,因为 Rust 中不支持嵌套的结构体和联合体。

结构体

与枚举和联合体不同,并不是所有结构体类型都有意义地组合在一起。因此,做出了以下设计决策:

  • 将两个命名结构体合并仅生成一个新的命名结构体。
  • 将两个未命名结构体合并仅生成一个新的未命名结构体。
  • 将两个单元结构体合并仅生成一个新的单元结构体。
  • 将单元结构体与其他任何类型合并都将生成另一种类型的结构体(例如,一个单元与一个命名合并将生成一个命名),无论哪个是“父”。

技术上,您可以允许合并命名和未命名的结构体,或者在合并单元结构体时让父更相关,但前者会比这个库已经做的更促进丑陋的编码习惯,后者似乎是一个更不常见的用例。

依赖项

~300–760KB
~18K SLoC