1use serde_json::Value;
2use std::collections::HashSet;
3
4use crate::error::{IssueCode, PathSegment, SizeOrigin, VldError};
5use crate::schema::VldSchema;
6
7pub struct ZSet<T: VldSchema> {
21 element: T,
22 min_size: Option<usize>,
23 max_size: Option<usize>,
24}
25
26impl<T: VldSchema> ZSet<T> {
27 pub fn new(element: T) -> Self {
29 Self {
30 element,
31 min_size: None,
32 max_size: None,
33 }
34 }
35
36 pub fn min_size(mut self, n: usize) -> Self {
38 self.min_size = Some(n);
39 self
40 }
41
42 pub fn max_size(mut self, n: usize) -> Self {
44 self.max_size = Some(n);
45 self
46 }
47}
48
49impl<T> VldSchema for ZSet<T>
50where
51 T: VldSchema,
52 T::Output: Eq + std::hash::Hash,
53{
54 type Output = HashSet<T::Output>;
55
56 fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
57 let arr = value
58 .as_array()
59 .ok_or_else(|| crate::primitives::type_error(&None, "array", value))?;
60
61 let mut result = HashSet::new();
62 let mut errors = VldError::new();
63 let mut failed = false;
65
66 for (i, item) in arr.iter().enumerate() {
67 match self.element.parse_value(item) {
68 Ok(v) => {
69 result.insert(v);
70 }
71 Err(e) => {
72 failed = true;
73 errors = errors.merge(e.with_prefix(PathSegment::Index(i)));
74 }
75 }
76 }
77
78 if let Some(min) = self.min_size {
79 if result.len() < min {
80 errors.push_code(IssueCode::TooSmall {
81 origin: SizeOrigin::Set,
82 minimum: min.into(),
83 inclusive: true,
84 });
85 }
86 }
87
88 if let Some(max) = self.max_size {
89 if result.len() > max {
90 errors.push_code(IssueCode::TooBig {
91 origin: SizeOrigin::Set,
92 maximum: max.into(),
93 inclusive: true,
94 });
95 }
96 }
97
98 if errors.is_empty() && !failed {
99 Ok(result)
100 } else {
101 Err(errors)
102 }
103 }
104}
105
106#[cfg(feature = "openapi")]
111impl<T> crate::openapi::JsonSchema for ZSet<T>
112where
113 T: VldSchema + crate::openapi::JsonSchema,
114 T::Output: Eq + std::hash::Hash,
115{
116 fn json_schema(&self) -> serde_json::Value {
117 let mut schema = serde_json::json!({
118 "type": "array",
119 "items": self.element.json_schema(),
120 });
121 if let Some(min) = self.min_size {
122 schema["minItems"] = serde_json::json!(min);
124 schema["x-minUniqueItems"] = serde_json::json!(min);
125 }
126 if let Some(max) = self.max_size {
127 schema["x-maxUniqueItems"] = serde_json::json!(max);
128 }
129 schema
130 }
131}
132
133#[cfg(feature = "openapi")]
134impl<T> crate::openapi::CollectNestedSchemas for ZSet<T>
135where
136 T: VldSchema + crate::openapi::CollectNestedSchemas,
137 T::Output: Eq + std::hash::Hash,
138{
139 fn collect_nested_schemas(&self, out: &mut Vec<crate::schema::NestedSchemaEntry>) {
140 self.element.collect_nested_schemas(out);
141 }
142}