1use serde_json::Value;
2use std::marker::PhantomData;
3
4use crate::error::VldError;
5use crate::schema::VldSchema;
6
7pub type NestedSchemaEntry = (&'static str, fn() -> serde_json::Value);
11
12pub type NestedSchemaCollectFn = fn(&mut Vec<NestedSchemaEntry>);
16
17pub struct NestedSchema<T, F>
20where
21 F: Fn(&Value) -> Result<T, VldError>,
22{
23 parse_fn: F,
24 #[cfg_attr(not(feature = "openapi"), allow(dead_code))]
25 pub(crate) name: Option<&'static str>,
26 #[cfg_attr(not(feature = "openapi"), allow(dead_code))]
28 pub(crate) json_schema_fn: Option<fn() -> serde_json::Value>,
29 #[cfg_attr(not(feature = "openapi"), allow(dead_code))]
31 pub(crate) collect_nested_fn: Option<NestedSchemaCollectFn>,
32 _phantom: PhantomData<T>,
33}
34
35impl<T, F> NestedSchema<T, F>
36where
37 F: Fn(&Value) -> Result<T, VldError>,
38{
39 pub fn new(f: F) -> Self {
41 Self {
42 parse_fn: f,
43 name: None,
44 json_schema_fn: None,
45 collect_nested_fn: None,
46 _phantom: PhantomData,
47 }
48 }
49
50 pub fn new_named(
52 f: F,
53 name: &'static str,
54 json_schema_fn: Option<fn() -> serde_json::Value>,
55 collect_nested_fn: Option<NestedSchemaCollectFn>,
56 ) -> Self {
57 Self {
58 parse_fn: f,
59 name: Some(name),
60 json_schema_fn,
61 collect_nested_fn,
62 _phantom: PhantomData,
63 }
64 }
65}
66
67impl<T, F> VldSchema for NestedSchema<T, F>
68where
69 F: Fn(&Value) -> Result<T, VldError>,
70{
71 type Output = T;
72 fn parse_value(&self, value: &Value) -> Result<T, VldError> {
73 (self.parse_fn)(value)
74 }
75}
76
77#[cfg(feature = "openapi")]
78impl<T, F> crate::openapi::JsonSchema for NestedSchema<T, F>
79where
80 F: Fn(&Value) -> Result<T, VldError>,
81{
82 fn json_schema(&self) -> Value {
83 match self.name {
84 Some(name) => serde_json::json!({ "$ref": format!("#/components/schemas/{}", name) }),
85 None => serde_json::json!({ "type": "object" }),
87 }
88 }
89}
90
91#[cfg(feature = "openapi")]
92impl<T, F> crate::openapi::CollectNestedSchemas for NestedSchema<T, F>
93where
94 F: Fn(&Value) -> Result<T, VldError>,
95{
96 fn collect_nested_schemas(&self, out: &mut Vec<NestedSchemaEntry>) {
97 if let (Some(name), Some(f)) = (self.name, self.json_schema_fn) {
98 if out.iter().any(|(n, _)| *n == name) {
100 return;
101 }
102 out.push((name, f));
103 if let Some(collect) = self.collect_nested_fn {
104 collect(out);
105 }
106 }
107 }
108}