vld/primitives/
json_value.rs1use serde_json::Value;
2
3use crate::error::{IssueCode, PathSegment, VldError};
4use crate::schema::VldSchema;
5
6#[derive(Clone)]
8pub struct ZJsonValue {
9 require_object: bool,
10 require_array: bool,
11 required_keys: Vec<String>,
12 max_depth: Option<usize>,
13 custom_type_error: Option<String>,
14}
15
16impl ZJsonValue {
17 pub fn new() -> Self {
19 Self {
20 require_object: false,
21 require_array: false,
22 required_keys: Vec::new(),
23 max_depth: None,
24 custom_type_error: None,
25 }
26 }
27
28 pub fn type_error(mut self, msg: impl Into<String>) -> Self {
31 self.custom_type_error = Some(msg.into());
32 self
33 }
34
35 pub fn object(mut self) -> Self {
37 self.require_object = true;
38 self.require_array = false;
39 self
40 }
41
42 pub fn array(mut self) -> Self {
44 self.require_array = true;
45 self.require_object = false;
46 self
47 }
48
49 pub fn require_key(mut self, key: impl Into<String>) -> Self {
51 self.required_keys.push(key.into());
52 self
53 }
54
55 pub fn max_depth(mut self, depth: usize) -> Self {
57 self.max_depth = Some(depth);
58 self
59 }
60}
61
62impl Default for ZJsonValue {
63 fn default() -> Self {
64 Self::new()
65 }
66}
67
68fn depth(value: &Value) -> usize {
69 match value {
70 Value::Array(arr) => 1 + arr.iter().map(depth).max().unwrap_or(0),
71 Value::Object(map) => 1 + map.values().map(depth).max().unwrap_or(0),
72 _ => 1,
73 }
74}
75
76impl VldSchema for ZJsonValue {
77 type Output = Value;
78
79 fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
80 if self.require_object && !value.is_object() {
81 return Err(crate::primitives::type_error(
82 &self.custom_type_error,
83 "object",
84 value,
85 ));
86 }
87 if self.require_array && !value.is_array() {
88 return Err(crate::primitives::type_error(
89 &self.custom_type_error,
90 "array",
91 value,
92 ));
93 }
94 if !self.required_keys.is_empty() {
95 let obj = value.as_object().ok_or_else(|| {
96 crate::primitives::type_error(&self.custom_type_error, "object", value)
97 })?;
98 for k in &self.required_keys {
99 if !obj.contains_key(k) {
100 return Err(VldError::from_code(IssueCode::MissingField)
101 .with_prefix(PathSegment::Field(k.clone())));
102 }
103 }
104 }
105 if let Some(max_depth) = self.max_depth {
106 if depth(value) > max_depth {
107 return Err(VldError::from_code_with_value(
108 IssueCode::JsonTooDeep { max_depth },
109 value,
110 ));
111 }
112 }
113 Ok(value.clone())
114 }
115}
116
117#[cfg(feature = "openapi")]
118impl crate::openapi::JsonSchema for ZJsonValue {
119 fn json_schema(&self) -> serde_json::Value {
120 let mut schema = serde_json::json!({});
121 if self.require_object || !self.required_keys.is_empty() {
123 schema["type"] = serde_json::json!("object");
124 if !self.required_keys.is_empty() {
125 schema["required"] = serde_json::json!(self.required_keys);
126 }
127 } else if self.require_array {
128 schema["type"] = serde_json::json!("array");
129 }
130 if let Some(depth) = self.max_depth {
131 schema["x-maxDepth"] = serde_json::json!(depth);
132 }
133 schema
134 }
135 fn is_required(&self) -> bool {
137 self.require_object || self.require_array || !self.required_keys.is_empty()
138 }
139}
140
141#[cfg(feature = "openapi")]
142impl crate::openapi::CollectNestedSchemas for ZJsonValue {}