use serde_json::Value;
use std::collections::HashSet;
use crate::error::{IssueCode, PathSegment, SizeOrigin, VldError};
use crate::schema::VldSchema;
pub struct ZSet<T: VldSchema> {
element: T,
min_size: Option<usize>,
max_size: Option<usize>,
}
impl<T: VldSchema> ZSet<T> {
pub fn new(element: T) -> Self {
Self {
element,
min_size: None,
max_size: None,
}
}
pub fn min_size(mut self, n: usize) -> Self {
self.min_size = Some(n);
self
}
pub fn max_size(mut self, n: usize) -> Self {
self.max_size = Some(n);
self
}
}
impl<T> VldSchema for ZSet<T>
where
T: VldSchema,
T::Output: Eq + std::hash::Hash,
{
type Output = HashSet<T::Output>;
fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
let arr = value
.as_array()
.ok_or_else(|| crate::primitives::type_error(&None, "array", value))?;
let mut result = HashSet::new();
let mut errors = VldError::new();
let mut failed = false;
for (i, item) in arr.iter().enumerate() {
match self.element.parse_value(item) {
Ok(v) => {
result.insert(v);
}
Err(e) => {
failed = true;
errors = errors.merge(e.with_prefix(PathSegment::Index(i)));
}
}
}
if let Some(min) = self.min_size {
if result.len() < min {
errors.push_code(IssueCode::TooSmall {
origin: SizeOrigin::Set,
minimum: min.into(),
inclusive: true,
});
}
}
if let Some(max) = self.max_size {
if result.len() > max {
errors.push_code(IssueCode::TooBig {
origin: SizeOrigin::Set,
maximum: max.into(),
inclusive: true,
});
}
}
if errors.is_empty() && !failed {
Ok(result)
} else {
Err(errors)
}
}
}
#[cfg(feature = "openapi")]
impl<T> crate::openapi::JsonSchema for ZSet<T>
where
T: VldSchema + crate::openapi::JsonSchema,
T::Output: Eq + std::hash::Hash,
{
fn json_schema(&self) -> serde_json::Value {
let mut schema = serde_json::json!({
"type": "array",
"items": self.element.json_schema(),
});
if let Some(min) = self.min_size {
schema["minItems"] = serde_json::json!(min);
schema["x-minUniqueItems"] = serde_json::json!(min);
}
if let Some(max) = self.max_size {
schema["x-maxUniqueItems"] = serde_json::json!(max);
}
schema
}
}
#[cfg(feature = "openapi")]
impl<T> crate::openapi::CollectNestedSchemas for ZSet<T>
where
T: VldSchema + crate::openapi::CollectNestedSchemas,
T::Output: Eq + std::hash::Hash,
{
fn collect_nested_schemas(&self, out: &mut Vec<crate::schema::NestedSchemaEntry>) {
self.element.collect_nested_schemas(out);
}
}