1use crate::paths::{MediaType, Parameter, RequestBody, Response, Responses};
2use crate::reference_or::ReferenceOr;
3use crate::security::SecurityScheme;
4use crate::ApiErrorComponent;
5use crate::Schema;
6
7use schemars::schema::{ArrayValidation, InstanceType, SchemaObject, SingleOrVec};
8use std::collections::BTreeMap;
9
10pub trait ApiComponent {
11 fn content_type() -> String {
12 "application/json".to_string()
13 }
14
15 fn required() -> bool {
16 true
17 }
18
19 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)>;
22
23 fn raw_schema() -> Option<ReferenceOr<Schema>> {
24 None
25 }
26
27 fn schema() -> Option<(String, ReferenceOr<Schema>)>;
28
29 fn securities() -> BTreeMap<String, SecurityScheme> {
30 Default::default()
31 }
32
33 fn security_requirement_name() -> Option<String> {
34 None
35 }
36
37 fn request_body() -> Option<RequestBody> {
38 Self::schema().map(|(name, _)| RequestBody {
39 content: BTreeMap::from_iter(vec![(
40 Self::content_type(),
41 MediaType {
42 schema: Some(ReferenceOr::Reference {
43 _ref: format!("#/components/schemas/{}", name),
44 }),
45 ..Default::default()
46 },
47 )]),
48 required: Some(Self::required()),
49 ..Default::default()
50 })
51 }
52
53 fn error_responses() -> Vec<(String, Response)> {
54 vec![]
55 }
56
57 fn error_schemas() -> BTreeMap<String, (String, ReferenceOr<Schema>)> {
58 BTreeMap::default()
59 }
60
61 fn responses(_content_type: Option<String>) -> Option<Responses> {
62 None
63 }
64
65 fn parameters() -> Vec<Parameter> {
66 vec![]
67 }
68}
69
70impl<T> ApiComponent for Option<T>
71where
72 T: ApiComponent,
73{
74 fn required() -> bool {
75 false
76 }
77
78 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)> {
79 T::child_schemas()
80 }
81
82 fn raw_schema() -> Option<ReferenceOr<Schema>> {
83 T::raw_schema()
84 }
85
86 fn schema() -> Option<(String, ReferenceOr<Schema>)> {
87 T::schema()
88 }
89
90 fn securities() -> BTreeMap<String, SecurityScheme> {
91 T::securities()
92 }
93
94 fn security_requirement_name() -> Option<String> {
95 T::security_requirement_name()
96 }
97}
98
99impl<T> ApiComponent for Vec<T>
100where
101 T: ApiComponent,
102{
103 fn required() -> bool {
104 true
105 }
106
107 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)> {
108 let mut schemas = T::schema()
109 .into_iter()
110 .collect::<Vec<(String, ReferenceOr<Schema>)>>();
111 schemas.append(&mut T::child_schemas());
112 schemas
113 }
114
115 fn raw_schema() -> Option<ReferenceOr<Schema>> {
116 T::raw_schema()
117 }
118
119 fn schema() -> Option<(String, ReferenceOr<Schema>)> {
120 T::schema().map(|(name, schema)| {
121 let _ref = match schema {
122 ReferenceOr::Reference { _ref } => _ref,
123 ReferenceOr::Object(_) => format!("#/components/schemas/{}", name),
124 };
125
126 (
127 name,
128 ReferenceOr::Object(Schema::Object(SchemaObject {
129 instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Array))),
130 array: Some(Box::new(ArrayValidation {
131 items: Some(Schema::new_ref(_ref).into()),
132 ..Default::default()
133 })),
134 ..Default::default()
135 })),
136 )
137 })
138 }
139}
140
141impl<T, E> ApiComponent for Result<T, E>
142where
143 T: ApiComponent,
144 E: ApiErrorComponent,
145{
146 fn required() -> bool {
147 T::required()
148 }
149
150 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)> {
151 T::child_schemas()
152 }
153
154 fn raw_schema() -> Option<ReferenceOr<Schema>> {
155 T::raw_schema()
156 }
157
158 fn schema() -> Option<(String, ReferenceOr<Schema>)> {
159 T::schema()
160 }
161
162 fn error_responses() -> Vec<(String, Response)> {
164 E::error_responses()
165 }
166
167 fn error_schemas() -> BTreeMap<String, (String, ReferenceOr<Schema>)> {
169 E::schemas_by_status_code()
170 }
171
172 fn responses(content_type: Option<String>) -> Option<Responses> {
173 T::responses(content_type)
174 }
175}
176
177#[cfg(test)]
178mod test {
179 use crate::reference_or::ReferenceOr;
180 use crate::ApiComponent;
181 use assert_json_diff::assert_json_eq;
182 use schemars::schema::{InstanceType, ObjectValidation, Schema, SchemaObject, SingleOrVec};
183 use schemars::{Map, Set};
184 use serde_json::json;
185
186 #[test]
187 #[allow(dead_code)]
188 fn api_component_schema_vec() {
189 struct TestChild {
190 surname: String,
191 }
192
193 impl ApiComponent for TestChild {
194 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)> {
195 vec![]
196 }
197
198 fn schema() -> Option<(String, ReferenceOr<Schema>)> {
199 Some((
200 "TestChild".to_string(),
201 ReferenceOr::Object(Schema::Object(SchemaObject {
202 object: Some(Box::new(ObjectValidation {
203 required: Set::from_iter(vec!["surname".to_string()]),
204 properties: Map::from_iter(vec![(
205 "surname".to_string(),
206 Schema::Object(SchemaObject {
207 instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
208 ..Default::default()
209 }),
210 )]),
211 ..Default::default()
212 })),
213 ..Default::default()
214 })),
215 ))
216 }
217 }
218
219 struct Test {
220 name: String,
221 surname: TestChild,
222 }
223
224 impl ApiComponent for Test {
225 fn child_schemas() -> Vec<(String, ReferenceOr<Schema>)> {
226 <TestChild as ApiComponent>::schema().into_iter().collect()
227 }
228
229 fn schema() -> Option<(String, ReferenceOr<Schema>)> {
230 Some((
231 "Test".to_string(),
232 ReferenceOr::Object(Schema::Object(SchemaObject {
233 object: Some(Box::new(ObjectValidation {
234 required: Set::from_iter(vec!["name".to_string(), "surname".to_string()]),
235 properties: Map::from_iter(vec![
236 (
237 "name".to_string(),
238 Schema::Object(SchemaObject {
239 instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
240 ..Default::default()
241 }),
242 ),
243 (
244 "surname".to_string(),
245 Schema::new_ref("#/components/schemas/TestChild".to_string()),
246 ),
247 ]),
248 ..Default::default()
249 })),
250 ..Default::default()
251 })),
252 ))
253 }
254 }
255
256 let schema = <Vec<Test> as ApiComponent>::schema();
257 assert!(schema.is_some());
258
259 let json =
260 serde_json::to_value(schema.expect("Missing schema").1).expect("Unable to serialize as Json");
261 assert_json_eq!(
262 json,
263 json!({
264 "type": "array",
265 "items": {
266 "$ref": "#/components/schemas/Test"
267 }
268 })
269 );
270
271 let child_schema = <Vec<Test> as ApiComponent>::child_schemas();
272 assert_eq!(child_schema.len(), 2);
273 let first_child_schema = child_schema.first().cloned();
274 assert!(first_child_schema.is_some());
275
276 let json = serde_json::to_value(first_child_schema.expect("Missing child schema").1)
277 .expect("Unable to serialize as Json");
278 assert_json_eq!(
279 json,
280 json!({
281 "properties": {
282 "name": {
283 "type": "string"
284 },
285 "surname": {
286 "$ref": "#/components/schemas/TestChild"
287 }
288 },
289 "required": [
290 "name",
291 "surname"
292 ]
293 })
294 );
295
296 let last_child_schema = child_schema.last().cloned();
297 assert!(last_child_schema.is_some());
298
299 let json = serde_json::to_value(last_child_schema.expect("Missing child schema").1)
300 .expect("Unable to serialize as Json");
301 assert_json_eq!(
302 json,
303 json!( {
304 "properties": {
305 "surname": {
306 "type": "string"
307 }
308 },
309 "required": [
310 "surname"
311 ]
312 })
313 );
314 }
315}