typeshaper 0.1.4

TypeScript utility-type idioms (Omit, Pick, Merge, Partial…) for Rust structs — one-line type algebra expressions
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
# typeshaper

`typeshaper` 让你用一行表达式从已有结构体派生新的结构体类型——省略字段、挑选字段、合并两个结构体、将所有字段置为可选,或恢复为必填。生成的类型自动获得转换 impl,可链式组合。

## 你有没有写过这样的代码

```rust
pub struct User {
    pub id: u64,
    pub name: String,
    pub email: String,
    pub password_hash: String,
    pub role: String,
    pub active: bool,
    pub created_at: i64,
}
```

然后 API 层要用,不能把 `password_hash` 暴露出去,于是你复制了一份:

```rust
pub struct UserPublic {
    pub id: u64,
    pub name: String,
    pub email: String,
    // password_hash 不要
    pub role: String,
    pub active: bool,
    pub created_at: i64,
}

impl From<User> for UserPublic {
    fn from(u: User) -> Self {
        Self {
            id: u.id,
            name: u.name,
            email: u.email,
            role: u.role,
            active: u.active,
            created_at: u.created_at,
        }
    }
}
```

然后搜索功能只需要 id 和 name,又复制一份:

```rust
pub struct UserSummary {
    pub id: u64,
    pub name: String,
}
// ... 又一个 From ...
```

然后增量更新接口要求所有字段可选,又复制一份:

```rust
pub struct UserPatch {
    pub id: Option<u64>,
    pub name: Option<String>,
    pub email: Option<String>,
    pub password_hash: Option<String>,
    // ...
}
// ... 又一个 From ...
```

`User` 加了一个字段,`UserPublic`、`UserPatch`、`UserSummary` 全部都要跟着改——改结构体、改 `From`、改可能遗漏的测试。

这只是 `User`。你还有 `Order`、`Product`、`Article`、`Comment`……

---

## 换一种写法

```toml
[dependencies]
typeshaper = "0.1"
```

```rust
use typeshaper::{TypeshaperExt, typeshaper, typex};

#[typeshaper]
#[derive(Debug, Clone)]
pub struct User {
    pub id: u64,
    pub name: String,
    pub email: String,
    pub password_hash: String,
    pub role: String,
    pub active: bool,
    pub created_at: i64,
}

// 去掉两个字段
typex!(#[derive(Debug, Clone)] UserPublic  = User - [password_hash, created_at]);

// 只保留两个字段
typex!(#[derive(Debug, Clone)] UserSummary = User & [id, name]);

// 所有字段变可选
typex!(#[derive(Debug, Clone)] UserPatch   = User?);
```

转换直接用:

```rust
let user: User = /* 从数据库来的 */;

let public:  UserPublic  = user.clone().project();  // 自动去掉 password_hash、created_at
let summary: UserSummary = user.clone().project();  // 只有 id 和 name
let patch    = UserPatch::from(user);               // 所有字段变 Option
```

`User` 加字段,三行 `typex!()` 不用改,新字段自动跟着走。

---

## 故事还没完:两个来源合并成一个

订单快照需要同时包含用户信息和地址信息:

```rust
#[typeshaper]
pub struct Address {
    pub street: String,
    pub city: String,
    pub country: String,
}

// 把 User 和 Address 合进一个新类型
typex!(#[derive(Debug, Clone)] OrderSnapshot = User + Address);

let snapshot = OrderSnapshot::from((user, address));
```

只需要"User 有而 Address 没有"的字段:

```rust
typex!(#[derive(Debug, Clone)] UserOnly = User % Address);  // Diff
```

---

## 表达式可以链式组合

```rust
// 先去掉 password_hash,再把剩余字段全部变可选
typex!(#[derive(Debug)] UserSafePatch = User - [password_hash]?);

// 去掉 password_hash 之后,只留摘要字段
typex!(#[derive(Debug)] UserSafeDto = User - [password_hash] & [id, name, email]);

// 括号控制结合方向:先 Partial 再 Required(等价于恢复非可选)
typex!(#[derive(Debug)] UserRestored = (User - [password_hash])?!);
```

---

## 增量更新的完整循环

```rust
// 可选版本用于更新接口
typex!(#[derive(Debug, Clone)] UserPatch    = User?);

// 验证通过后恢复为必填版本
typex!(#[derive(Debug, Clone)] UserVerified = UserPatch!);

// ---

let patch = UserPatch {
    name: Some("alice".into()),
    email: Some("new@example.com".into()),
    // 其他字段留 None,表示"不更新"
    ..Default::default()
};

// 如果所有字段都已填写,可以恢复为强类型
match UserVerified::try_from(patch) {
    Ok(verified) => { /* 提交 */ }
    Err(e)       => { /* 告知哪个字段缺失 */ }
}
```

---

## 跨 crate:模型定义在一个地方

领域层定义模型,API 层按需派生,不复制结构体:

```rust
// core-crate/src/lib.rs
#[typeshaper(export)]          // export 多生成一个伴生宏
#[derive(Debug, Clone)]
pub struct User { /* ... */ }
// 自动生成并导出:pub macro typeshaper_import_User!()
```

```rust
// api-crate/src/lib.rs
use core_crate::{User, typeshaper_import_User};

typeshaper_import_User!();  // 把 User 的字段元数据注册到本 crate

// 之后完全和本地 #[typeshaper] 类型一样用
typex!(#[derive(Debug, Clone)] UserPublic = User - [password_hash, created_at]);
typex!(#[derive(Debug, Clone)] UserPatch  = User?);
```

---

## 泛型支持

当源结构体带有类型参数时,在 `typex!()` 中必须**显式**声明——同时写在目标名称和源节点上。这是有意为之:隐式继承在多个类型参数来自不同源类型时会产生错误的结构体。

### 基本类型参数

```rust
#[typeshaper]
#[derive(Debug, Clone)]
pub struct Wrapper<T> {
    pub inner: T,
    pub label: String,
    pub count: usize,
}

// <T> 同时出现在目标名称和源节点上
typex!(#[derive(Debug, Clone)] WrapperNoLabel<T>  = Wrapper<T> - [label]);
typex!(#[derive(Debug, Clone)] WrapperPartial<T>  = Wrapper<T>?);
typex!(#[derive(Debug, Clone)] WrapperRequired<T> = WrapperPartial<T>!);

let w = Wrapper { inner: 42u32, label: "hi".into(), count: 3 };
let no_label: WrapperNoLabel<u32> = w.project();
```

### 多类型参数(Merge)

合并两个泛型类型时,目标声明所有参数,每个源节点使用自己的参数:

```rust
#[typeshaper]
pub struct Person<T> { pub name: T, pub age: u8 }

#[typeshaper]
pub struct Addr<U> { pub city: U, pub zip: String }

// T 来自 Person,U 来自 Addr,目标同时声明两者
typex!(#[derive(Debug)] PersonWithAddr<T, U> = Person<T> + Addr<U>);

let full = PersonWithAddr::from((person, addr));
```

### 内联 trait bound 与 where 子句

```rust
typex!(PrintableValue<T: std::fmt::Display + Clone> = Printable<T> - [note]);

typex!(ConstrainedData<T> where T: Clone + PartialEq = Constrained<T> - [meta]);
```

### 生命周期参数

```rust
#[typeshaper]
pub struct Borrowed<'a> { pub name: &'a str, pub value: u32 }

typex!(BorrowedName<'a> = Borrowed<'a> & [name]);
```

### 跨 crate 泛型类型

泛型元数据已编码在伴生宏中,导入时自动还原,用法与本 crate 完全相同:

```rust
// core-crate
#[typeshaper(export)]
pub struct GenericModel<T> { pub id: u64, pub payload: T, pub hidden: bool }
```

```rust
// app-crate
typeshaper_import_GenericModel!();

typex!(#[derive(Debug)] ModelPublic<T> = GenericModel<T> - [hidden]);
typex!(#[derive(Debug)] ModelDraft<T>  = GenericModel<T>?);
```

> **编译期守卫**:忘记写类型参数会在编译时报错:
> ```
> typex!(Bad = Wrapper - [label]);
> //          ^^^^^^^ error: type `Wrapper` has generic parameters;
> //                  declare them explicitly, e.g. `Target<T> = Wrapper<T>`
> ```

---

## 参考文档

### 安装

```toml
[dependencies]
typeshaper = "0.1"
```

### 前置标注:`#[typeshaper]`

在源结构体上添加一次,字段信息写入编译期注册表,结构体本身原样保留。
支持两种形式:

| 形式 | 作用 |
|------|------|
| `#[typeshaper]` | 本 crate 内使用 |
| `#[typeshaper(export)]` | 本 crate 内使用 + 生成 `typeshaper_import_T!()` 供其他 crate 调用 |

```rust
#[typeshaper]
#[derive(Debug, Clone, PartialEq)]
pub struct User {
    pub id: u64,
    pub name: String,
    pub age: u8,
    pub email: String,
}
```

`#[typeshaper]` 可以叠放在任意其他属性之上,不影响结构体的任何已有行为。

---

### 操作符速查

| 语法 | 名称 | 含义 | 生成的 impl |
|------|------|------|-------------|
| `T - [f1, f2]` | **Omit** | 移除列出的字段 | `TypeshaperInto<Target> for T` |
| `T & [f1, f2]` | **Pick** | 只保留列出的字段 | `TypeshaperInto<Target> for T` |
| `A + B` | **Merge** | 合并 A 和 B 的全部字段(不允许重名) | `From<(A, B)> for Target` |
| `T?` | **Partial** | 所有字段变为 `Option<_>` | `From<T> for Target` |
| `T!` | **Required** | 还原 Partial 的 `Option<_>` | `TryFrom<T> for Target`(源无 `Option` 字段时为 `From<T>`|
| `A % B` | **Diff** | A 有而 B 没有的字段(按字段名****类型同时匹配) | `TypeshaperInto<Target> for A` |

**组合规则**

操作符左结合,可加括号改变结合方向:

```rust
// User - [age] & [id, name]  等价于  (User - [age]) & [id, name]
typex!(Dto      = User - [age] & [id, name]);

// 括号让右侧先求值
typex!(Full     = User + (Badge - [label]));

// 后缀链式
typex!(Draft    = User - [password_hash]?);
typex!(Roundtrip = (User - [password_hash])?!);
```

---

### `typex!()` 语法

```
typex!( [#[attr...]]  TargetName[<Params>] [where ...]  =  Expr );
```

- **属性**(可选):写在 `TargetName` 前,原样附加到生成的结构体,支持叠放多个。`typex!()` 不会自动添加任何 `#[derive]`,全部由调用方声明。
- **TargetName**:生成的结构体名称,同时注册到编译期注册表,可继续作为后续 `typex!()` 的输入。
- **`<Params>`**(可选):目标类型的显式泛型或生命周期参数——当 `Expr` 中任意源类型是泛型时必须填写。支持内联 bound(`T: Clone + Debug`)和单独 `where` 子句两种写法。
- **Expr**:类型代数表达式,见上表。涉及泛型源类型的节点必须携带匹配的类型参数:`Source<T>``Source<'a>` 等。

```rust
typex!(
    #[derive(Debug, Clone, PartialEq)]
    #[serde(rename_all = "camelCase")]
    UserPublicDto = User & [id, name, email]
);
```

---

### 转换方法

`TypeshaperExt` trait 为所有类型自动实现,通过类型推断选择目标:

```rust
let public: UserPublic = user.project();   // 等价于 user.typeshaper_into()
```

`Merge` 使用元组 `From`,`Partial` 使用 `From`,`Required` 使用 `TryFrom`:

```rust
let snapshot = OrderSnapshot::from((user, address));
let draft    = UserPatch::from(user);
let verified = UserVerified::try_from(draft)?;
```

---

### 跨 crate 详细用法

**导出端**

```rust
// core-crate/src/lib.rs
use typeshaper::typeshaper;

#[typeshaper(export)]
#[derive(Debug, Clone)]
pub struct User {
    pub id: u64,
    pub name: String,
    pub role: String,
    pub active: bool,
}
// 编译后自动导出:pub macro typeshaper_import_User!()
```

**导入端**

```rust
// app-crate/src/lib.rs
use typeshaper::typex;
use core_crate::{User, typeshaper_import_User};

typeshaper_import_User!();  // 仅一次,写在模块顶层

typex!(#[derive(Debug, Clone)] UserPublic = User - [role, active]);
typex!(#[derive(Debug, Clone)] UserPatch  = User?);
```

多个类型分别调用各自的伴生宏,跨 crate Merge / Diff 同样支持:

```rust
use core_crate::{Address, typeshaper_import_Address};

typeshaper_import_User!();
typeshaper_import_Address!();

typex!(#[derive(Debug, Clone)] OrderSnapshot = User + Address);
typex!(#[derive(Debug, Clone)] UserOnly      = User % Address);
```

| | 本 crate 内 | 跨 crate |
|---|---|---|
| 源类型标注 | `#[typeshaper]` | `#[typeshaper(export)]` |
| 调用方前置步骤 || `typeshaper_import_T!()` |
| `typex!()` 语法 | 完全相同 | 完全相同 |

---

### 已支持的操作

- [x] Omit — `T - [fields]`
- [x] Pick — `T & [fields]`
- [x] Merge — `A + B`
- [x] Partial — `T?`
- [x] Required — `T!`
- [x] Diff — `A % B`
- [x] 表达式组合与链式操作
- [x] 属性透传
- [x] 跨 crate 导出 / 导入
- [x] 泛型、生命周期与 trait bound — 要求显式声明类型参数