1use super::fields::FieldInfo;
4use super::options::MetadataOptions;
5use super::response::MetadataResponse;
6use async_trait::async_trait;
7use reinhardt_core::exception::Result;
8use reinhardt_http::Request;
9use std::collections::HashMap;
10
11#[async_trait]
13pub trait BaseMetadata: Send + Sync {
14 async fn determine_metadata(
16 &self,
17 request: &Request,
18 options: &MetadataOptions,
19 ) -> Result<MetadataResponse>;
20}
21
22#[derive(Debug, Clone)]
27pub struct SimpleMetadata {
28 pub include_actions: bool,
30}
31
32impl SimpleMetadata {
33 pub fn new() -> Self {
44 Self {
45 include_actions: true,
46 }
47 }
48 pub fn with_actions(mut self, include: bool) -> Self {
62 self.include_actions = include;
63 self
64 }
65 pub fn convert_serializer_fields(
97 &self,
98 serializer_fields: &HashMap<String, super::options::SerializerFieldInfo>,
99 ) -> HashMap<String, FieldInfo> {
100 use super::inferencer::SchemaInferencer;
101
102 let inferencer = SchemaInferencer::new();
103 let mut fields = HashMap::new();
104
105 for (field_name, serializer_field) in serializer_fields {
106 let mut field_info = inferencer.infer_from_type_name(&serializer_field.type_name);
108
109 field_info.required = !serializer_field.is_optional;
111
112 field_info.read_only = Some(serializer_field.is_read_only);
114 fields.insert(field_name.clone(), field_info);
118 }
119
120 fields
121 }
122
123 pub fn determine_actions(
148 &self,
149 allowed_methods: &[String],
150 fields: &HashMap<String, FieldInfo>,
151 ) -> HashMap<String, HashMap<String, FieldInfo>> {
152 let mut actions = HashMap::new();
153
154 for method in allowed_methods {
155 let method_upper = method.to_uppercase();
156 if method_upper == "POST" || method_upper == "PUT" || method_upper == "PATCH" {
157 actions.insert(method_upper, fields.clone());
158 }
159 }
160
161 actions
162 }
163}
164
165impl Default for SimpleMetadata {
166 fn default() -> Self {
167 Self::new()
168 }
169}
170
171#[async_trait]
172impl BaseMetadata for SimpleMetadata {
173 async fn determine_metadata(
174 &self,
175 _request: &Request,
176 options: &MetadataOptions,
177 ) -> Result<MetadataResponse> {
178 let mut response = MetadataResponse {
179 name: options.name.clone(),
180 description: options.description.clone(),
181 renders: Some(options.renders.clone()),
182 parses: Some(options.parses.clone()),
183 actions: None,
184 };
185
186 if self.include_actions {
187 let fields = if let Some(serializer_fields) = &options.serializer_fields {
189 self.convert_serializer_fields(serializer_fields)
190 } else {
191 HashMap::new()
192 };
193
194 let actions = self.determine_actions(&options.allowed_methods, &fields);
195 if !actions.is_empty() {
196 response.actions = Some(actions);
197 }
198 }
199
200 Ok(response)
201 }
202}
203
204#[cfg(test)]
205mod tests {
206 use super::*;
207 use crate::metadata::fields::FieldInfoBuilder;
208 use crate::metadata::types::{ChoiceInfo, FieldType};
209 use bytes::Bytes;
210 use hyper::{HeaderMap, Method, Version};
211
212 fn create_test_request() -> Request {
213 Request::builder()
214 .method(Method::OPTIONS)
215 .uri("/users/")
216 .version(Version::HTTP_11)
217 .headers(HeaderMap::new())
218 .body(Bytes::new())
219 .build()
220 .unwrap()
221 }
222
223 #[tokio::test]
226 async fn test_base_metadata_trait_requires_implementation() {
227 let metadata = SimpleMetadata::new();
229 let request = create_test_request();
230 let options = MetadataOptions::default();
231
232 let result = metadata.determine_metadata(&request, &options).await;
234 assert!(result.is_ok());
235 }
236
237 #[tokio::test]
240 async fn test_metadata_basic_response() {
241 let metadata = SimpleMetadata::new();
242 let request = create_test_request();
243 let options = MetadataOptions {
244 name: "Example".to_string(),
245 description: "Example view.".to_string(),
246 allowed_methods: vec!["GET".to_string()],
247 renders: vec!["application/json".to_string(), "text/html".to_string()],
248 parses: vec![
249 "application/json".to_string(),
250 "application/x-www-form-urlencoded".to_string(),
251 "multipart/form-data".to_string(),
252 ],
253 serializer_fields: None,
254 };
255
256 let response = metadata
257 .determine_metadata(&request, &options)
258 .await
259 .unwrap();
260
261 assert_eq!(response.name, "Example");
262 assert_eq!(response.description, "Example view.");
263 assert_eq!(
264 response.renders,
265 Some(vec![
266 "application/json".to_string(),
267 "text/html".to_string()
268 ])
269 );
270 assert_eq!(
271 response.parses,
272 Some(vec![
273 "application/json".to_string(),
274 "application/x-www-form-urlencoded".to_string(),
275 "multipart/form-data".to_string(),
276 ])
277 );
278 }
279
280 #[tokio::test]
283 async fn test_actions_with_fields() {
284 let metadata = SimpleMetadata::new();
285
286 let mut fields = HashMap::new();
287
288 fields.insert(
290 "choice_field".to_string(),
291 FieldInfoBuilder::new(FieldType::Choice)
292 .required(true)
293 .read_only(false)
294 .label("Choice field")
295 .choices(vec![
296 ChoiceInfo {
297 value: "red".to_string(),
298 display_name: "red".to_string(),
299 },
300 ChoiceInfo {
301 value: "green".to_string(),
302 display_name: "green".to_string(),
303 },
304 ChoiceInfo {
305 value: "blue".to_string(),
306 display_name: "blue".to_string(),
307 },
308 ])
309 .build(),
310 );
311
312 fields.insert(
314 "integer_field".to_string(),
315 FieldInfoBuilder::new(FieldType::Integer)
316 .required(true)
317 .read_only(false)
318 .label("Integer field")
319 .min_value(1.0)
320 .max_value(1000.0)
321 .build(),
322 );
323
324 fields.insert(
326 "char_field".to_string(),
327 FieldInfoBuilder::new(FieldType::String)
328 .required(false)
329 .read_only(false)
330 .label("Char field")
331 .min_length(3)
332 .max_length(40)
333 .build(),
334 );
335
336 let mut nested_children = HashMap::new();
338 nested_children.insert(
339 "a".to_string(),
340 FieldInfoBuilder::new(FieldType::Integer)
341 .required(true)
342 .read_only(false)
343 .label("A")
344 .build(),
345 );
346 nested_children.insert(
347 "b".to_string(),
348 FieldInfoBuilder::new(FieldType::Integer)
349 .required(true)
350 .read_only(false)
351 .label("B")
352 .build(),
353 );
354
355 fields.insert(
356 "nested_field".to_string(),
357 FieldInfoBuilder::new(FieldType::NestedObject)
358 .required(true)
359 .read_only(false)
360 .label("Nested field")
361 .children(nested_children)
362 .build(),
363 );
364
365 let options = MetadataOptions {
366 name: "Example".to_string(),
367 description: "Example view.".to_string(),
368 allowed_methods: vec!["POST".to_string()],
369 renders: vec!["application/json".to_string()],
370 parses: vec!["application/json".to_string()],
371 serializer_fields: None,
372 };
373
374 let actions = metadata.determine_actions(&options.allowed_methods, &fields);
375
376 assert!(actions.contains_key("POST"));
377 let post_fields = &actions["POST"];
378 assert!(post_fields.contains_key("choice_field"));
379 assert!(post_fields.contains_key("integer_field"));
380 assert!(post_fields.contains_key("char_field"));
381 assert!(post_fields.contains_key("nested_field"));
382
383 let choice_field = &post_fields["choice_field"];
385 assert_eq!(choice_field.field_type, FieldType::Choice);
386 assert!(choice_field.required);
387 assert_eq!(choice_field.read_only, Some(false));
388 assert_eq!(choice_field.choices.as_ref().unwrap().len(), 3);
389
390 let integer_field = &post_fields["integer_field"];
392 assert_eq!(integer_field.field_type, FieldType::Integer);
393 assert_eq!(integer_field.min_value, Some(1.0));
394 assert_eq!(integer_field.max_value, Some(1000.0));
395
396 let nested_field = &post_fields["nested_field"];
398 assert_eq!(nested_field.field_type, FieldType::NestedObject);
399 assert!(nested_field.children.is_some());
400 let children = nested_field.children.as_ref().unwrap();
401 assert!(children.contains_key("a"));
402 assert!(children.contains_key("b"));
403 }
404
405 #[tokio::test]
406 async fn test_simple_metadata() {
407 let metadata = SimpleMetadata::new();
408 let request = create_test_request();
409 let options = MetadataOptions {
410 name: "User List".to_string(),
411 description: "List all users".to_string(),
412 allowed_methods: vec!["GET".to_string(), "POST".to_string()],
413 renders: vec!["application/json".to_string()],
414 parses: vec!["application/json".to_string()],
415 serializer_fields: None,
416 };
417
418 let response = metadata
419 .determine_metadata(&request, &options)
420 .await
421 .unwrap();
422
423 assert_eq!(response.name, "User List");
424 assert_eq!(response.description, "List all users");
425 assert_eq!(response.renders, Some(vec!["application/json".to_string()]));
426 assert_eq!(response.parses, Some(vec!["application/json".to_string()]));
427 }
428
429 #[tokio::test]
432 async fn test_metadata_with_serializer_inspection() {
433 use crate::metadata::options::SerializerFieldInfo;
434
435 let metadata = SimpleMetadata::new();
436 let request = create_test_request();
437
438 let mut serializer_fields = HashMap::new();
440 serializer_fields.insert(
441 "username".to_string(),
442 SerializerFieldInfo {
443 name: "username".to_string(),
444 type_name: "String".to_string(),
445 is_optional: false,
446 is_read_only: false,
447 is_write_only: false,
448 },
449 );
450 serializer_fields.insert(
451 "email".to_string(),
452 SerializerFieldInfo {
453 name: "email".to_string(),
454 type_name: "String".to_string(),
455 is_optional: false,
456 is_read_only: false,
457 is_write_only: false,
458 },
459 );
460 serializer_fields.insert(
461 "age".to_string(),
462 SerializerFieldInfo {
463 name: "age".to_string(),
464 type_name: "i32".to_string(),
465 is_optional: true,
466 is_read_only: false,
467 is_write_only: false,
468 },
469 );
470
471 let options = MetadataOptions {
472 name: "User Create".to_string(),
473 description: "Create a new user".to_string(),
474 allowed_methods: vec!["POST".to_string()],
475 renders: vec!["application/json".to_string()],
476 parses: vec!["application/json".to_string()],
477 serializer_fields: Some(serializer_fields),
478 };
479
480 let response = metadata
481 .determine_metadata(&request, &options)
482 .await
483 .unwrap();
484
485 assert_eq!(response.name, "User Create");
487 assert_eq!(response.description, "Create a new user");
488
489 assert!(response.actions.is_some());
491 let actions = response.actions.unwrap();
492 assert!(actions.contains_key("POST"));
493
494 let post_fields = &actions["POST"];
496 assert_eq!(post_fields.len(), 3);
497
498 assert!(post_fields.contains_key("username"));
500 let username_field = &post_fields["username"];
501 assert_eq!(username_field.field_type, FieldType::String);
502 assert!(username_field.required);
503 assert_eq!(username_field.read_only, Some(false));
504
505 assert!(post_fields.contains_key("email"));
507 let email_field = &post_fields["email"];
508 assert_eq!(email_field.field_type, FieldType::String);
509 assert!(email_field.required);
510
511 assert!(post_fields.contains_key("age"));
513 let age_field = &post_fields["age"];
514 assert_eq!(age_field.field_type, FieldType::Integer);
515 assert!(!age_field.required); assert_eq!(age_field.read_only, Some(false));
517 }
518}