use serde_json::Value;
use crate::error::{FraiseQLError, Result};
pub struct OneOfValidator;
impl OneOfValidator {
pub fn validate(
input: &Value,
field_names: &[String],
context_path: Option<&str>,
) -> Result<()> {
let field_path = context_path.unwrap_or("input");
let present_count = field_names
.iter()
.filter(|name| {
if let Value::Object(obj) = input {
obj.get(*name).is_some_and(|v| !matches!(v, Value::Null))
} else {
false
}
})
.count();
if present_count != 1 {
return Err(FraiseQLError::Validation {
message: format!(
"Exactly one of [{}] must be provided, but {} {} provided",
field_names.join(", "),
present_count,
if present_count == 1 { "was" } else { "were" }
),
path: Some(field_path.to_string()),
});
}
Ok(())
}
}
pub struct AnyOfValidator;
impl AnyOfValidator {
pub fn validate(
input: &Value,
field_names: &[String],
context_path: Option<&str>,
) -> Result<()> {
let field_path = context_path.unwrap_or("input");
let has_any = field_names.iter().any(|name| {
if let Value::Object(obj) = input {
obj.get(name).is_some_and(|v| !matches!(v, Value::Null))
} else {
false
}
});
if !has_any {
return Err(FraiseQLError::Validation {
message: format!("At least one of [{}] must be provided", field_names.join(", ")),
path: Some(field_path.to_string()),
});
}
Ok(())
}
}
pub struct ConditionalRequiredValidator;
impl ConditionalRequiredValidator {
pub fn validate(
input: &Value,
if_field_present: &str,
then_required: &[String],
context_path: Option<&str>,
) -> Result<()> {
let field_path = context_path.unwrap_or("input");
if let Value::Object(obj) = input {
let condition_met =
obj.get(if_field_present).is_some_and(|v| !matches!(v, Value::Null));
if condition_met {
let missing_fields: Vec<&String> = then_required
.iter()
.filter(|name| obj.get(*name).is_none_or(|v| matches!(v, Value::Null)))
.collect();
if !missing_fields.is_empty() {
return Err(FraiseQLError::Validation {
message: format!(
"Since '{}' is provided, {} must also be provided",
if_field_present,
missing_fields
.iter()
.map(|s| format!("'{}'", s))
.collect::<Vec<_>>()
.join(", ")
),
path: Some(field_path.to_string()),
});
}
}
}
Ok(())
}
}
pub struct RequiredIfAbsentValidator;
impl RequiredIfAbsentValidator {
pub fn validate(
input: &Value,
absent_field: &str,
then_required: &[String],
context_path: Option<&str>,
) -> Result<()> {
let field_path = context_path.unwrap_or("input");
if let Value::Object(obj) = input {
let field_absent = obj.get(absent_field).is_none_or(|v| matches!(v, Value::Null));
if field_absent {
let missing_fields: Vec<&String> = then_required
.iter()
.filter(|name| obj.get(*name).is_none_or(|v| matches!(v, Value::Null)))
.collect();
if !missing_fields.is_empty() {
return Err(FraiseQLError::Validation {
message: format!(
"Since '{}' is not provided, {} must be provided",
absent_field,
missing_fields
.iter()
.map(|s| format!("'{}'", s))
.collect::<Vec<_>>()
.join(", ")
),
path: Some(field_path.to_string()),
});
}
}
}
Ok(())
}
}