Skip to main content

allframe_core/router/
schema.rs

1//! JSON Schema generation for OpenAPI documentation
2//!
3//! This module provides types and traits for generating JSON Schema
4//! from Rust types. This is used to automatically document request
5//! and response types in OpenAPI specifications.
6
7use serde_json::{json, Value};
8
9/// Trait for types that can be converted to JSON Schema
10///
11/// This trait enables automatic schema generation for OpenAPI documentation.
12/// Implement this trait for your request/response types to enable
13/// automatic API documentation.
14pub trait ToJsonSchema {
15    /// Generate a JSON Schema for this type
16    fn schema() -> Value;
17
18    /// Get the schema type name (for referencing in OpenAPI)
19    fn schema_name() -> Option<String> {
20        None
21    }
22}
23
24// Implement ToJsonSchema for primitive types
25
26impl ToJsonSchema for String {
27    fn schema() -> Value {
28        json!({
29            "type": "string"
30        })
31    }
32}
33
34impl ToJsonSchema for &str {
35    fn schema() -> Value {
36        json!({
37            "type": "string"
38        })
39    }
40}
41
42impl ToJsonSchema for i32 {
43    fn schema() -> Value {
44        json!({
45            "type": "integer",
46            "format": "int32"
47        })
48    }
49}
50
51impl ToJsonSchema for i64 {
52    fn schema() -> Value {
53        json!({
54            "type": "integer",
55            "format": "int64"
56        })
57    }
58}
59
60impl ToJsonSchema for u32 {
61    fn schema() -> Value {
62        json!({
63            "type": "integer",
64            "format": "uint32",
65            "minimum": 0
66        })
67    }
68}
69
70impl ToJsonSchema for u64 {
71    fn schema() -> Value {
72        json!({
73            "type": "integer",
74            "format": "uint64",
75            "minimum": 0
76        })
77    }
78}
79
80impl ToJsonSchema for f32 {
81    fn schema() -> Value {
82        json!({
83            "type": "number",
84            "format": "float"
85        })
86    }
87}
88
89impl ToJsonSchema for f64 {
90    fn schema() -> Value {
91        json!({
92            "type": "number",
93            "format": "double"
94        })
95    }
96}
97
98impl ToJsonSchema for bool {
99    fn schema() -> Value {
100        json!({
101            "type": "boolean"
102        })
103    }
104}
105
106// Implement for Option<T>
107impl<T: ToJsonSchema> ToJsonSchema for Option<T> {
108    fn schema() -> Value {
109        let mut schema = T::schema();
110        if let Value::Object(ref mut map) = schema {
111            map.insert("nullable".to_string(), Value::Bool(true));
112        }
113        schema
114    }
115}
116
117// Implement for Vec<T>
118impl<T: ToJsonSchema> ToJsonSchema for Vec<T> {
119    fn schema() -> Value {
120        json!({
121            "type": "array",
122            "items": T::schema()
123        })
124    }
125}
126
127#[cfg(test)]
128mod tests {
129    use super::*;
130
131    #[test]
132    fn test_string_schema() {
133        let schema = String::schema();
134        assert_eq!(schema["type"], "string");
135    }
136
137    #[test]
138    fn test_str_schema() {
139        let schema = <&str>::schema();
140        assert_eq!(schema["type"], "string");
141    }
142
143    #[test]
144    fn test_i32_schema() {
145        let schema = i32::schema();
146        assert_eq!(schema["type"], "integer");
147        assert_eq!(schema["format"], "int32");
148    }
149
150    #[test]
151    fn test_i64_schema() {
152        let schema = i64::schema();
153        assert_eq!(schema["type"], "integer");
154        assert_eq!(schema["format"], "int64");
155    }
156
157    #[test]
158    fn test_u32_schema() {
159        let schema = u32::schema();
160        assert_eq!(schema["type"], "integer");
161        assert_eq!(schema["format"], "uint32");
162        assert_eq!(schema["minimum"], 0);
163    }
164
165    #[test]
166    fn test_u64_schema() {
167        let schema = u64::schema();
168        assert_eq!(schema["type"], "integer");
169        assert_eq!(schema["format"], "uint64");
170        assert_eq!(schema["minimum"], 0);
171    }
172
173    #[test]
174    fn test_f32_schema() {
175        let schema = f32::schema();
176        assert_eq!(schema["type"], "number");
177        assert_eq!(schema["format"], "float");
178    }
179
180    #[test]
181    fn test_f64_schema() {
182        let schema = f64::schema();
183        assert_eq!(schema["type"], "number");
184        assert_eq!(schema["format"], "double");
185    }
186
187    #[test]
188    fn test_bool_schema() {
189        let schema = bool::schema();
190        assert_eq!(schema["type"], "boolean");
191    }
192
193    #[test]
194    fn test_option_string_schema() {
195        let schema = Option::<String>::schema();
196        assert_eq!(schema["type"], "string");
197        assert_eq!(schema["nullable"], true);
198    }
199
200    #[test]
201    fn test_option_i32_schema() {
202        let schema = Option::<i32>::schema();
203        assert_eq!(schema["type"], "integer");
204        assert_eq!(schema["nullable"], true);
205    }
206
207    #[test]
208    fn test_vec_string_schema() {
209        let schema = Vec::<String>::schema();
210        assert_eq!(schema["type"], "array");
211        assert_eq!(schema["items"]["type"], "string");
212    }
213
214    #[test]
215    fn test_vec_i32_schema() {
216        let schema = Vec::<i32>::schema();
217        assert_eq!(schema["type"], "array");
218        assert_eq!(schema["items"]["type"], "integer");
219        assert_eq!(schema["items"]["format"], "int32");
220    }
221
222    #[test]
223    fn test_nested_vec_schema() {
224        let schema = Vec::<Vec<String>>::schema();
225        assert_eq!(schema["type"], "array");
226        assert_eq!(schema["items"]["type"], "array");
227        assert_eq!(schema["items"]["items"]["type"], "string");
228    }
229
230    #[test]
231    fn test_option_vec_schema() {
232        let schema = Option::<Vec<String>>::schema();
233        assert_eq!(schema["type"], "array");
234        assert_eq!(schema["nullable"], true);
235        assert_eq!(schema["items"]["type"], "string");
236    }
237
238    #[test]
239    fn test_vec_option_schema() {
240        let schema = Vec::<Option<String>>::schema();
241        assert_eq!(schema["type"], "array");
242        assert_eq!(schema["items"]["type"], "string");
243        assert_eq!(schema["items"]["nullable"], true);
244    }
245}