#definition #generate #structs #proc-macro #single #multiple #variant

structout

通过常见定义生成结构体的进程宏

6个版本 (3个重大变更)

0.6.0 2020年10月26日
0.5.0 2020年10月23日
0.3.0 2020年10月20日
0.1.2 2020年10月16日

#900 in 进程宏

BSD-3-Clause

25KB
578

structout

用法

此库允许通过进程宏从单个定义生成多个结构体。

generate!(
  attributes
  visibility <...> where ... {
    field: type,
    ...
  } => {
    OutputStruct => [action(arg), ...]
  }
)
  • (可选) attributes 将应用于 所有 变体。
  • (可选) visibility 将应用于 所有 变体。
  • (可选) <...> 是类型参数(即泛型);如果未使用,则不应包含。
  • (可选) where ... 表示类型约束。
  • { field: type, ... } 是常用的 结构体体,将用于生成新的结构体。
  • { OutputStruct => [action(arg), ...] } 是输出配置,其中每个条目映射到一个新生成的结构体;进一步
    • OutputStruct 是结构体的名称
    • [action(arg), ...] 是构建此特定变体的操作列表。

其中“操作”可以是以下之一

  • omit(fields_names) 从此结构体定义中省略字段。
  • include(fields_names) 优先于 omit。在结构体定义中包含字段。
  • attr(args) 在结构体定义之前插入一个属性。
  • as_tuple() 将结构体输出为一个元组结构体。
  • upsert(fields) 将根据指定的类型进行更新或插入字段(即在存在具有相同标识符的字段定义的情况下替换字段定义,否则插入一个新的字段)。

实际应用

use structout::generate;

generate!(
  {
    foo: u32,
    bar: u64,
    baz: String
  } => {
    WithoutFoo => [omit(foo)],
    WithoutBar => [omit(bar)],
  }
);

上面的代码应该展开为两个结构体

struct WithoutFoo {
    bar: u64,
    baz: String
}
struct WithoutBar {
    foo: u32,
    baz: String
}

如果添加了两个泛型参数,它们应有效地在变体之间分割,而无需使用 PhantomData。

generate!(
  <S, C> where S: Sized, C: Copy {
    foo: S,
    bar: G
  } => {
    OnlyBar => [omit(foo)],
    OnlyFoo => [omit(bar)],
  }
);

上面的代码应该展开为

struct OnlyBar<C>
where
    C: Copy,
{
    bar: G,
}
struct OnlyFoo<S>
where
    S: Sized,
{
    foo: S,
}

有关完整 API 的使用示例,请参阅 测试模块

开发

测试

测试围绕快照测试(insta)进行。它通过运行 cargo expandcargo-expand),获取其输出,然后使用 cargo insta reviewcargo insta)进行审查来实现。

请参阅 测试模块,了解其实际实现方式。

动机

这个库解决了为单个定义生成多个结构体的需求。考虑以下代码

struct Human {
  id: u32,
  age: u32,
  username: String,
  name: String,
  surname: String
}

// suppose this is what you would get from an API
struct HumanEditableParts {
  name: String,
  surname: String
}

HumanEditableParts 手动重复了一些字段,并且这些字段需要保持同步。

在 Rust 中,据说可以通过“结构体组合”来避免这种模式,即。

struct HumanEditableParts {
  name: String,
  surname: String1
}

struct Human {
  id: u32,
  age: u32,
  username: String,
  editable_parts: HumanEditableParts
}

但这并不总是可行的,也不总是令人愉快的事情。

依赖项

~1.5MB
~36K SLoC