Skip to main content

wae_schema/
lib.rs

1//! WAE Schema - Schema 定义与验证模块
2//!
3//! 提供统一的 Schema 定义能力,支持:
4//! - 数据结构 Schema 定义
5//! - Schema 验证
6//! - OpenAPI 文档生成
7#![warn(missing_docs)]
8
9use serde::{Deserialize, Serialize};
10
11/// Schema 生成 trait
12///
13/// 实现此 trait 的类型可以自动生成 OpenAPI Schema。
14pub trait ToSchema {
15    /// 生成 Schema 定义
16    fn schema() -> Schema;
17}
18
19/// Schema 类型定义
20#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
21pub enum SchemaType {
22    /// 字符串类型
23    String,
24    /// 整数类型
25    Integer,
26    /// 浮点数类型
27    Number,
28    /// 布尔类型
29    Boolean,
30    /// 数组类型
31    Array,
32    /// 对象类型
33    Object,
34    /// 空值类型
35    Null,
36}
37
38impl Default for SchemaType {
39    fn default() -> Self {
40        Self::Object
41    }
42}
43
44/// Schema 定义
45#[derive(Debug, Clone, Serialize, Deserialize)]
46pub struct Schema {
47    /// Schema 标题
48    pub title: Option<String>,
49    /// Schema 描述
50    pub description: Option<String>,
51    /// Schema 类型
52    #[serde(rename = "type")]
53    pub schema_type: SchemaType,
54    /// 属性定义(仅 Object 类型)
55    pub properties: Option<std::collections::BTreeMap<String, Schema>>,
56    /// 必需属性列表
57    pub required: Option<Vec<String>>,
58    /// 数组元素类型(仅 Array 类型)
59    pub items: Option<Box<Schema>>,
60    /// 枚举值
61    #[serde(rename = "enum")]
62    pub enum_values: Option<Vec<serde_json::Value>>,
63    /// 默认值
64    pub default: Option<serde_json::Value>,
65    /// 示例值
66    pub example: Option<serde_json::Value>,
67    /// 是否可为空
68    #[serde(default)]
69    pub nullable: bool,
70    /// 只读
71    #[serde(default)]
72    pub read_only: bool,
73    /// 只写
74    #[serde(default)]
75    pub write_only: bool,
76    /// 最小值(Number/Integer)
77    pub minimum: Option<f64>,
78    /// 最大值(Number/Integer)
79    pub maximum: Option<f64>,
80    /// 最小长度(String)
81    pub min_length: Option<usize>,
82    /// 最大长度(String)
83    pub max_length: Option<usize>,
84    /// 正则模式(String)
85    pub pattern: Option<String>,
86    /// 格式(String)
87    pub format: Option<String>,
88}
89
90impl Default for Schema {
91    fn default() -> Self {
92        Self {
93            title: None,
94            description: None,
95            schema_type: SchemaType::Object,
96            properties: None,
97            required: None,
98            items: None,
99            enum_values: None,
100            default: None,
101            example: None,
102            nullable: false,
103            read_only: false,
104            write_only: false,
105            minimum: None,
106            maximum: None,
107            min_length: None,
108            max_length: None,
109            pattern: None,
110            format: None,
111        }
112    }
113}
114
115impl Schema {
116    /// 创建字符串 Schema
117    pub fn string() -> Self {
118        Self { schema_type: SchemaType::String, ..Default::default() }
119    }
120
121    /// 创建整数 Schema
122    pub fn integer() -> Self {
123        Self { schema_type: SchemaType::Integer, ..Default::default() }
124    }
125
126    /// 创建数字 Schema
127    pub fn number() -> Self {
128        Self { schema_type: SchemaType::Number, ..Default::default() }
129    }
130
131    /// 创建布尔 Schema
132    pub fn boolean() -> Self {
133        Self { schema_type: SchemaType::Boolean, ..Default::default() }
134    }
135
136    /// 创建数组 Schema
137    pub fn array(items: Schema) -> Self {
138        Self { schema_type: SchemaType::Array, items: Some(Box::new(items)), ..Default::default() }
139    }
140
141    /// 创建对象 Schema
142    pub fn object() -> Self {
143        Self { schema_type: SchemaType::Object, properties: Some(std::collections::BTreeMap::new()), ..Default::default() }
144    }
145
146    /// 设置标题
147    pub fn title(mut self, title: impl Into<String>) -> Self {
148        self.title = Some(title.into());
149        self
150    }
151
152    /// 设置描述
153    pub fn description(mut self, desc: impl Into<String>) -> Self {
154        self.description = Some(desc.into());
155        self
156    }
157
158    /// 添加属性
159    pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
160        let properties = self.properties.get_or_insert_with(std::collections::BTreeMap::new);
161        properties.insert(name.into(), schema);
162        self
163    }
164
165    /// 设置必需属性
166    pub fn required(mut self, fields: Vec<&str>) -> Self {
167        self.required = Some(fields.into_iter().map(String::from).collect());
168        self
169    }
170
171    /// 设置枚举值
172    pub fn enum_values(mut self, values: Vec<serde_json::Value>) -> Self {
173        self.enum_values = Some(values);
174        self
175    }
176
177    /// 设置默认值
178    pub fn with_default(mut self, value: serde_json::Value) -> Self {
179        self.default = Some(value);
180        self
181    }
182
183    /// 设置示例值
184    pub fn example(mut self, value: serde_json::Value) -> Self {
185        self.example = Some(value);
186        self
187    }
188
189    /// 设置格式
190    pub fn format(mut self, format: impl Into<String>) -> Self {
191        self.format = Some(format.into());
192        self
193    }
194
195    /// 设置最小长度
196    pub fn min_length(mut self, len: usize) -> Self {
197        self.min_length = Some(len);
198        self
199    }
200
201    /// 设置最大长度
202    pub fn max_length(mut self, len: usize) -> Self {
203        self.max_length = Some(len);
204        self
205    }
206
207    /// 设置最小值
208    pub fn minimum(mut self, min: f64) -> Self {
209        self.minimum = Some(min);
210        self
211    }
212
213    /// 设置最大值
214    pub fn maximum(mut self, max: f64) -> Self {
215        self.maximum = Some(max);
216        self
217    }
218
219    /// 设置可为空
220    pub fn nullable(mut self, nullable: bool) -> Self {
221        self.nullable = nullable;
222        self
223    }
224
225    /// 设置只读
226    pub fn read_only(mut self, read_only: bool) -> Self {
227        self.read_only = read_only;
228        self
229    }
230
231    /// 设置只写
232    pub fn write_only(mut self, write_only: bool) -> Self {
233        self.write_only = write_only;
234        self
235    }
236
237    /// 转换为 JSON Schema
238    pub fn to_json_schema(&self) -> serde_json::Value {
239        serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
240    }
241}
242
243/// Schema 构建器
244pub struct SchemaBuilder {
245    schema: Schema,
246}
247
248impl SchemaBuilder {
249    /// 创建新的构建器
250    pub fn new() -> Self {
251        Self { schema: <Schema as Default>::default() }
252    }
253
254    /// 创建字符串构建器
255    pub fn string() -> Self {
256        Self { schema: Schema::string() }
257    }
258
259    /// 创建整数构建器
260    pub fn integer() -> Self {
261        Self { schema: Schema::integer() }
262    }
263
264    /// 创建数字构建器
265    pub fn number() -> Self {
266        Self { schema: Schema::number() }
267    }
268
269    /// 创建布尔构建器
270    pub fn boolean() -> Self {
271        Self { schema: Schema::boolean() }
272    }
273
274    /// 创建数组构建器
275    pub fn array(items: Schema) -> Self {
276        Self { schema: Schema::array(items) }
277    }
278
279    /// 创建对象构建器
280    pub fn object() -> Self {
281        Self { schema: Schema::object() }
282    }
283
284    /// 设置标题
285    pub fn title(mut self, title: impl Into<String>) -> Self {
286        self.schema.title = Some(title.into());
287        self
288    }
289
290    /// 设置描述
291    pub fn description(mut self, desc: impl Into<String>) -> Self {
292        self.schema.description = Some(desc.into());
293        self
294    }
295
296    /// 添加属性
297    pub fn property(mut self, name: impl Into<String>, schema: Schema) -> Self {
298        let properties = self.schema.properties.get_or_insert_with(std::collections::BTreeMap::new);
299        properties.insert(name.into(), schema);
300        self
301    }
302
303    /// 设置必需属性
304    pub fn required(mut self, fields: Vec<&str>) -> Self {
305        self.schema.required = Some(fields.into_iter().map(String::from).collect());
306        self
307    }
308
309    /// 构建 Schema
310    pub fn build(self) -> Schema {
311        self.schema
312    }
313}
314
315impl Default for SchemaBuilder {
316    fn default() -> Self {
317        Self::new()
318    }
319}
320
321/// OpenAPI 文档信息
322#[derive(Debug, Clone, Serialize, Deserialize)]
323pub struct OpenApiInfo {
324    /// API 标题
325    pub title: String,
326    /// API 版本
327    pub version: String,
328    /// API 描述
329    pub description: Option<String>,
330    /// 服务条款 URL
331    pub terms_of_service: Option<String>,
332    /// 联系信息
333    pub contact: Option<Contact>,
334    /// 许可信息
335    pub license: Option<License>,
336}
337
338impl Default for OpenApiInfo {
339    fn default() -> Self {
340        Self {
341            title: "API".to_string(),
342            version: "1.0.0".to_string(),
343            description: None,
344            terms_of_service: None,
345            contact: None,
346            license: None,
347        }
348    }
349}
350
351/// 联系信息
352#[derive(Debug, Clone, Serialize, Deserialize)]
353pub struct Contact {
354    /// 联系人姓名
355    pub name: Option<String>,
356    /// 联系人邮箱
357    pub email: Option<String>,
358    /// 联系人 URL
359    pub url: Option<String>,
360}
361
362/// 许可信息
363#[derive(Debug, Clone, Serialize, Deserialize)]
364pub struct License {
365    /// 许可证名称
366    pub name: String,
367    /// 许可证 URL
368    pub url: Option<String>,
369}
370
371/// OpenAPI 文档
372#[derive(Debug, Clone, Serialize, Deserialize)]
373pub struct OpenApiDoc {
374    /// OpenAPI 版本
375    pub openapi: String,
376    /// API 信息
377    pub info: OpenApiInfo,
378    /// 路径定义
379    pub paths: std::collections::BTreeMap<String, PathItem>,
380    /// 组件定义
381    pub components: Option<Components>,
382    /// 服务器列表
383    pub servers: Option<Vec<Server>>,
384}
385
386impl Default for OpenApiDoc {
387    fn default() -> Self {
388        Self {
389            openapi: "3.1.0".to_string(),
390            info: OpenApiInfo::default(),
391            paths: std::collections::BTreeMap::new(),
392            components: None,
393            servers: None,
394        }
395    }
396}
397
398impl OpenApiDoc {
399    /// 创建新的 OpenAPI 文档
400    pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
401        Self { info: OpenApiInfo { title: title.into(), version: version.into(), ..Default::default() }, ..Default::default() }
402    }
403
404    /// 设置描述
405    pub fn description(mut self, desc: impl Into<String>) -> Self {
406        self.info.description = Some(desc.into());
407        self
408    }
409
410    /// 添加路径
411    pub fn path(mut self, path: impl Into<String>, item: PathItem) -> Self {
412        self.paths.insert(path.into(), item);
413        self
414    }
415
416    /// 添加 Schema 组件
417    pub fn schema(mut self, name: impl Into<String>, schema: Schema) -> Self {
418        let components = self.components.get_or_insert_with(Components::default);
419        components.schemas.insert(name.into(), schema);
420        self
421    }
422
423    /// 添加服务器
424    pub fn server(mut self, url: impl Into<String>, description: Option<String>) -> Self {
425        let servers = self.servers.get_or_insert_with(Vec::new);
426        servers.push(Server { url: url.into(), description });
427        self
428    }
429
430    /// 转换为 JSON
431    pub fn to_json(&self) -> serde_json::Value {
432        serde_json::to_value(self).unwrap_or(serde_json::Value::Null)
433    }
434
435    /// 转换为 YAML(需要启用 yaml feature)
436    #[cfg(feature = "yaml")]
437    pub fn to_yaml(&self) -> Result<String, serde_yaml::Error> {
438        serde_yaml::to_string(self)
439    }
440}
441
442/// 路径项
443#[derive(Debug, Clone, Serialize, Deserialize)]
444pub struct PathItem {
445    /// GET 操作
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub get: Option<Operation>,
448    /// POST 操作
449    #[serde(skip_serializing_if = "Option::is_none")]
450    pub post: Option<Operation>,
451    /// PUT 操作
452    #[serde(skip_serializing_if = "Option::is_none")]
453    pub put: Option<Operation>,
454    /// DELETE 操作
455    #[serde(skip_serializing_if = "Option::is_none")]
456    pub delete: Option<Operation>,
457    /// PATCH 操作
458    #[serde(skip_serializing_if = "Option::is_none")]
459    pub patch: Option<Operation>,
460    /// 路径描述
461    #[serde(skip_serializing_if = "Option::is_none")]
462    pub description: Option<String>,
463    /// 路径摘要
464    #[serde(skip_serializing_if = "Option::is_none")]
465    pub summary: Option<String>,
466}
467
468impl Default for PathItem {
469    fn default() -> Self {
470        Self { get: None, post: None, put: None, delete: None, patch: None, description: None, summary: None }
471    }
472}
473
474impl PathItem {
475    /// 创建新的路径项
476    pub fn new() -> Self {
477        Self::default()
478    }
479
480    /// 设置 GET 操作
481    pub fn get(mut self, op: Operation) -> Self {
482        self.get = Some(op);
483        self
484    }
485
486    /// 设置 POST 操作
487    pub fn post(mut self, op: Operation) -> Self {
488        self.post = Some(op);
489        self
490    }
491
492    /// 设置 PUT 操作
493    pub fn put(mut self, op: Operation) -> Self {
494        self.put = Some(op);
495        self
496    }
497
498    /// 设置 DELETE 操作
499    pub fn delete(mut self, op: Operation) -> Self {
500        self.delete = Some(op);
501        self
502    }
503
504    /// 设置 PATCH 操作
505    pub fn patch(mut self, op: Operation) -> Self {
506        self.patch = Some(op);
507        self
508    }
509}
510
511/// 操作定义
512#[derive(Debug, Clone, Serialize, Deserialize)]
513pub struct Operation {
514    /// 操作标签
515    #[serde(skip_serializing_if = "Option::is_none")]
516    pub tags: Option<Vec<String>>,
517    /// 操作摘要
518    #[serde(skip_serializing_if = "Option::is_none")]
519    pub summary: Option<String>,
520    /// 操作描述
521    #[serde(skip_serializing_if = "Option::is_none")]
522    pub description: Option<String>,
523    /// 操作 ID
524    #[serde(skip_serializing_if = "Option::is_none")]
525    pub operation_id: Option<String>,
526    /// 参数列表
527    #[serde(skip_serializing_if = "Option::is_none")]
528    pub parameters: Option<Vec<Parameter>>,
529    /// 请求体
530    #[serde(skip_serializing_if = "Option::is_none")]
531    pub request_body: Option<RequestBody>,
532    /// 响应定义
533    pub responses: std::collections::BTreeMap<String, Response>,
534}
535
536impl Operation {
537    /// 创建新操作
538    pub fn new() -> Self {
539        Self {
540            tags: None,
541            summary: None,
542            description: None,
543            operation_id: None,
544            parameters: None,
545            request_body: None,
546            responses: std::collections::BTreeMap::new(),
547        }
548    }
549
550    /// 设置摘要
551    pub fn summary(mut self, summary: impl Into<String>) -> Self {
552        self.summary = Some(summary.into());
553        self
554    }
555
556    /// 设置描述
557    pub fn description(mut self, desc: impl Into<String>) -> Self {
558        self.description = Some(desc.into());
559        self
560    }
561
562    /// 设置操作 ID
563    pub fn operation_id(mut self, id: impl Into<String>) -> Self {
564        self.operation_id = Some(id.into());
565        self
566    }
567
568    /// 添加标签
569    pub fn tag(mut self, tag: impl Into<String>) -> Self {
570        let tags = self.tags.get_or_insert_with(Vec::new);
571        tags.push(tag.into());
572        self
573    }
574
575    /// 添加参数
576    pub fn parameter(mut self, param: Parameter) -> Self {
577        let params = self.parameters.get_or_insert_with(Vec::new);
578        params.push(param);
579        self
580    }
581
582    /// 设置请求体
583    pub fn request_body(mut self, body: RequestBody) -> Self {
584        self.request_body = Some(body);
585        self
586    }
587
588    /// 添加响应
589    pub fn response(mut self, status: impl Into<String>, resp: Response) -> Self {
590        self.responses.insert(status.into(), resp);
591        self
592    }
593}
594
595impl Default for Operation {
596    fn default() -> Self {
597        Self::new()
598    }
599}
600
601/// 参数定义
602#[derive(Debug, Clone, Serialize, Deserialize)]
603pub struct Parameter {
604    /// 参数名称
605    pub name: String,
606    /// 参数位置
607    #[serde(rename = "in")]
608    pub location: ParameterLocation,
609    /// 参数描述
610    #[serde(skip_serializing_if = "Option::is_none")]
611    pub description: Option<String>,
612    /// 是否必需
613    #[serde(default)]
614    pub required: bool,
615    /// 参数 Schema
616    #[serde(skip_serializing_if = "Option::is_none")]
617    pub schema: Option<Schema>,
618}
619
620/// 参数位置
621#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
622#[serde(rename_all = "lowercase")]
623pub enum ParameterLocation {
624    /// 路径参数
625    Path,
626    /// 查询参数
627    Query,
628    /// 请求头参数
629    Header,
630    /// Cookie 参数
631    Cookie,
632}
633
634impl Parameter {
635    /// 创建路径参数
636    pub fn path(name: impl Into<String>) -> Self {
637        Self { name: name.into(), location: ParameterLocation::Path, description: None, required: true, schema: None }
638    }
639
640    /// 创建查询参数
641    pub fn query(name: impl Into<String>) -> Self {
642        Self { name: name.into(), location: ParameterLocation::Query, description: None, required: false, schema: None }
643    }
644
645    /// 创建请求头参数
646    pub fn header(name: impl Into<String>) -> Self {
647        Self { name: name.into(), location: ParameterLocation::Header, description: None, required: false, schema: None }
648    }
649
650    /// 设置描述
651    pub fn description(mut self, desc: impl Into<String>) -> Self {
652        self.description = Some(desc.into());
653        self
654    }
655
656    /// 设置必需
657    pub fn required(mut self, required: bool) -> Self {
658        self.required = required;
659        self
660    }
661
662    /// 设置 Schema
663    pub fn schema(mut self, schema: Schema) -> Self {
664        self.schema = Some(schema);
665        self
666    }
667}
668
669/// 请求体定义
670#[derive(Debug, Clone, Serialize, Deserialize)]
671pub struct RequestBody {
672    /// 请求体描述
673    #[serde(skip_serializing_if = "Option::is_none")]
674    pub description: Option<String>,
675    /// 内容定义
676    pub content: std::collections::BTreeMap<String, MediaType>,
677    /// 是否必需
678    #[serde(default)]
679    pub required: bool,
680}
681
682impl RequestBody {
683    /// 创建 JSON 请求体
684    pub fn json(schema: Schema) -> Self {
685        let mut content = std::collections::BTreeMap::new();
686        content.insert("application/json".to_string(), MediaType { schema: Some(schema), example: None });
687        Self { description: None, content, required: true }
688    }
689
690    /// 设置描述
691    pub fn description(mut self, desc: impl Into<String>) -> Self {
692        self.description = Some(desc.into());
693        self
694    }
695}
696
697/// 媒体类型定义
698#[derive(Debug, Clone, Serialize, Deserialize)]
699pub struct MediaType {
700    /// Schema
701    #[serde(skip_serializing_if = "Option::is_none")]
702    pub schema: Option<Schema>,
703    /// 示例
704    #[serde(skip_serializing_if = "Option::is_none")]
705    pub example: Option<serde_json::Value>,
706}
707
708/// 响应定义
709#[derive(Debug, Clone, Serialize, Deserialize)]
710pub struct Response {
711    /// 响应描述
712    pub description: String,
713    /// 内容定义
714    #[serde(skip_serializing_if = "Option::is_none")]
715    pub content: Option<std::collections::BTreeMap<String, MediaType>>,
716}
717
718impl Response {
719    /// 创建新响应
720    pub fn new(description: impl Into<String>) -> Self {
721        Self { description: description.into(), content: None }
722    }
723
724    /// 添加 JSON 响应
725    pub fn json(mut self, schema: Schema) -> Self {
726        let content = self.content.get_or_insert_with(std::collections::BTreeMap::new);
727        content.insert("application/json".to_string(), MediaType { schema: Some(schema), example: None });
728        self
729    }
730}
731
732/// 组件定义
733#[derive(Debug, Clone, Serialize, Deserialize, Default)]
734pub struct Components {
735    /// Schema 定义
736    #[serde(skip_serializing_if = "std::collections::BTreeMap::is_empty")]
737    pub schemas: std::collections::BTreeMap<String, Schema>,
738}
739
740/// 服务器定义
741#[derive(Debug, Clone, Serialize, Deserialize)]
742pub struct Server {
743    /// 服务器 URL
744    pub url: String,
745    /// 服务器描述
746    #[serde(skip_serializing_if = "Option::is_none")]
747    pub description: Option<String>,
748}
749
750impl ToSchema for String {
751    fn schema() -> Schema {
752        Schema::string()
753    }
754}
755
756impl ToSchema for i64 {
757    fn schema() -> Schema {
758        Schema::integer()
759    }
760}
761
762impl ToSchema for i32 {
763    fn schema() -> Schema {
764        Schema::integer()
765    }
766}
767
768impl ToSchema for i16 {
769    fn schema() -> Schema {
770        Schema::integer()
771    }
772}
773
774impl ToSchema for i8 {
775    fn schema() -> Schema {
776        Schema::integer()
777    }
778}
779
780impl ToSchema for u64 {
781    fn schema() -> Schema {
782        Schema::integer()
783    }
784}
785
786impl ToSchema for u32 {
787    fn schema() -> Schema {
788        Schema::integer()
789    }
790}
791
792impl ToSchema for u16 {
793    fn schema() -> Schema {
794        Schema::integer()
795    }
796}
797
798impl ToSchema for u8 {
799    fn schema() -> Schema {
800        Schema::integer()
801    }
802}
803
804impl ToSchema for f64 {
805    fn schema() -> Schema {
806        Schema::number()
807    }
808}
809
810impl ToSchema for f32 {
811    fn schema() -> Schema {
812        Schema::number()
813    }
814}
815
816impl ToSchema for bool {
817    fn schema() -> Schema {
818        Schema::boolean()
819    }
820}
821
822impl<T: ToSchema> ToSchema for Vec<T> {
823    fn schema() -> Schema {
824        Schema::array(T::schema())
825    }
826}
827
828impl<T: ToSchema> ToSchema for Option<T> {
829    fn schema() -> Schema {
830        T::schema().nullable(true)
831    }
832}
833
834impl ToSchema for serde_json::Value {
835    fn schema() -> Schema {
836        <Schema as Default>::default()
837    }
838}