#derive #openapi #patch #structs #view #macro-derive #struct

restructed

快速派生结构体的子集

6个版本

0.2.0 2024年3月11日
0.1.4 2024年2月9日
0.1.3 2023年12月21日

#180 in 过程宏

Download history 66/week @ 2024-03-18 3/week @ 2024-03-25 6/week @ 2024-04-01

每月256次下载

无许可证

57KB
1K SLoC

restructed


快速轻松地创建现有类型的派生模型,无需反复重复。

  • 减少需要编写的结构体数量
  • 允许在生成的结构体上派生
  • 允许从一个派生生成多个结构体
  • 自动生成从原始结构体到生成结构体的From<T>特质

计划的新功能可在此处的github上找到。下面是当前用法和功能的示例。

用法

restructed添加到项目的Cargo.toml

restructed = "0.1"

或者在该项目目录中运行以下命令

cargo add restructed

添加导入,并在目标结构体上派生

#[derive(restructed::Models)]
struct User {
    id: i32,
    username: String
}

然后为要创建的每个模型添加属性。

#[derive(restructed::Models)]
#[view(UserId, fields(id))]        // <-- Simple subset of the deriving structs field
#[patch(UserUpdatables, omit(id))] // <-- Wraps all fields with a Option type inside a new struct
struct User {
    id: i32,
    username: String,
}

继续阅读,了解可用的模型及其一般分解和参数。

模型

这些是可应用于相应属性(即#[attr(args...)]))的参数。

#[视图]

生成原始/父级派生Model子集的模型。用于创建RESTful API或数据库视图等。

参数名称 描述 必需? 类型/枚举 示例
name 要生成的结构体的名称 是+第一 标识符 MyStruct
fields 在原始结构体中要包含的字段名称 列表(标识符) fields(field1,field2, ...)
omit 原结构中要排除的字段名 列表(标识符) omit(field1,field2, ...)
派生 在新生成的结构上要派生的事物 列表(路径) 派生(调试, thiserror::错误)
preset 要应用的行为和/或默认值 无/写/读 preset= "所有"
attributes_with 在结构体和字段级别继承的属性 无/oai/deriveless/所有 attributes_with= "所有"
#[derive(Clone, restructed::Models)]
#[view(UserProfile, omit(id, password))]
struct User {
    id: i32, // Not in `UserProfile`
    display_name: String,
    bio: String,
    extra: Option<String>,
    password: String, // Not in `UserProfile`
}

#[补丁]

一个创建数据子集的模型,除了每个字段的类型都包裹在 Option<t> 或指定的替代类型选项实现中。对于创建RESTful API补丁方法类型或仅希望更新已明确给出的字段(即使是为了删除)的数据库表补丁很有用。

参数名称 描述 必需? 类型/枚举 示例
name 要生成的结构体的名称 是+第一 标识符 MyStruct
fields 在原始结构体中要包含的字段名称 列表(标识符) fields(field1,field2, ...)
omit 原结构中要排除的字段名 列表(标识符) omit(field1,field2, ...)
派生 在新生成的结构上要派生的事物 列表(路径) 派生(调试, thiserror::错误)
preset 要应用的行为和/或默认值 无/写/读 preset= "所有"
attributes_with 在结构体和字段级别继承的属性 无/oai/deriveless/所有 attributes_with= "所有"
option 替代 Option<T> 来包裹字段 Option/MaybeUndefined option=MaybeUndefined
#[derive(Clone, restructed::Models)]
#[patch(UserUpdate, fields(display_name, bio, extra, password))]
struct User {
    id: i32, // Not in `UserUpdate`
    display_name: String, // Option<String> in `UserUpdate`
    bio: String, // Option<String> in `UserUpdate`
    extra: Option<String>, // Option<Option<String>> in `UserUpdate` (If this isn't desired, see *option* arg and the *openapi* crate feature)
    password: String, // Not in `UserProfile`
}

#[model]

不是模型,用于定义应用于所有模型的基本默认参数集。作为接受参数的接口以应用更广泛的参数,生成任何模型本身。

有两种可能的参数

base

一个列表,其中包含应用于所有生成的参数的非可覆盖参数。它不会阻止您在单个模型中稍后使用它们,但也不会允许您单独取消效果。

例如 #[model(base(...)]

参数名称 描述 必需? 类型/枚举 示例
fields 在原始结构体中要包含的字段名称 列表(标识符) fields(field1,field2, ...)
omit 原结构中要排除的字段名 列表(标识符) omit(field1,field2, ...)
派生 在新生成的结构上要派生的事物 列表(路径) 派生(调试, thiserror::错误)

defaults

在此列表中给出的参数应用于所有模型,其中未给出参数。这意味着,如果先写下 #[model(defaults(...))] 然后稍后写入 #[view(omit(b))],则早期写入的 fields(a, b) 将不会应用,因为这两个参数与 base 参数不同,是相互排斥的。

例如 #[model(defaults(...))]

参数名称 描述 必需? 类型/枚举 示例
fields 在原始结构体中要包含的字段名称 列表(标识符) fields(field1,field2, ...)
omit 原结构中要排除的字段名 列表(标识符) omit(field1,field2, ...)
派生 在新生成的结构上要派生的事物 列表(路径) 派生(调试, thiserror::错误)
preset 要应用的行为和/或默认值 无/写/读 preset= "所有"
attributes_with 在结构体和字段级别继承的属性 无/oai/deriveless/所有 attributes_with= "所有"

示例

#[derive(Clone, restructed::Models)]
#[model(base(derive(Debug)))] // All models now *MUST* derive Debug (despite parent)
#[view(UserView)]
#[patch(UserPatch)]
struct User {
    id: i32,
    display_name: String,
    bio: String,
    extra: Option<String>,
    password: String,
}

fn debug_models() {
  let user = User {
    id: 1,
    display_name: "Dude".to_string(),
    bio: "Too long didn't read".to_string(),
    extra: None,
    password: "ezpz".to_string(),
  };

  let view: UserView = user.clone().into(); // Automatically gen from model
  print!("A view of a user {:?}", view);

  let patch: UserPatch = user.clone().into(); // Automatically gen from model
  print!("A patch of a user {:?}", patch);
}

参数行为

preset

使用预置的字符串字面值,预置是一组默认设置,用于应用到一个模型上。以下是一个预置中包含的参数列表。[例如 preset = "none"]

  • none - 不执行任何操作,是默认行为 [默认]

  • write ['openapi' 功能标志] - 设计为仅显示可以写入的属性。

    • omit - 作为基础应用,删除具有 #[oai(read_only)] 属性的字段,你的字段/omit 在之后应用
    • option 仅补丁 - 参数默认为 MaybeUndefined
  • read ['openapi' 功能标志] - 设计为仅显示始终可以读取的属性。

    • omit - 作为基础应用,删除具有 #[oai(write_only)] 属性的字段,你的字段/omit 在之后应用
    • option 仅补丁 - 参数默认为 MaybeUndefined

attributes_with

在结构体和字段级别继承的属性的字符串字面值。以下是值列表。 [例如 attributes_with = "none"]

  • none - 不包含任何属性 [默认]
  • oai ['openapi' 功能标志] - 包含所有 Poem 的 OpenAPI 属性
  • deriveless - 包含所有属性,但省略了 derive 属性
  • all - 包含所有属性

已知限制

  • 通用结构体和枚举 - 目前,这个库 不支持 在需要通用的结构体上推导模型(例如,在 Struct<T> 上推导)。我并不需要这个功能,但是欢迎贡献!

库功能

链接指向其他相关功能的相关 crates GitHub 页面。

Poem OpenAPI

启用使用 poem-openapi crate 中的 Option<T> 从源结构体包装 MaybeUndefined<T>patch 模型中的 oai(...) 属性也可以显式复制到生成的结构体中,这意味着你保留了所有的验证器等。

use restructed::Models;

#[derive(poem_openapi::Object, Models)]
#[oai(skip_serializing_if_is_none, rename_all = "camelCase")]
#[model(base(derive(poem_openapi::Object, Debug)), defaults(preset = "read"))]
#[patch(UserUpdate, preset = "write")]
#[view(UserProfile)]
#[view(UserNames, fields(username, name, surname))]
pub struct User {
    #[oai(read_only)]
    pub id: u32,
    // profile
    #[oai(validator(min_length = 3, max_length = 16, pattern = r"^[a-zA-Z0-9_]*$"))] // oai attributes carry over with `preset = write/write` or attributes_with="oai"
    pub username: String,
    #[oai(validator(min_length = 5, max_length = 1024), write_only)]
    pub password: String,
    #[oai(validator(min_length = 2, max_length = 16, pattern = r"^[a-zA-Z\s]*$"))]
    pub name: Option<String>,
    #[oai(validator(min_length = 2, max_length = 16, pattern = r"^[a-zA-Z\s]*$"))]
    pub surname: Option<String>, // in patch modeels, this is `MaybeUndefined` type with default with preset `read` or `write` (or option = MaybeUndefined)
    #[oai(read_only)]
    pub joined: u64,
}

依赖关系

~0.4–0.8MB
~19K SLoC