6个版本
0.2.0 | 2024年3月11日 |
---|---|
0.1.4 | 2024年2月9日 |
0.1.3 | 2023年12月21日 |
#180 in 过程宏
每月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