use serde_json::Value;
use crate::error::{IssueCode, PathSegment, VldError, value_type_name};
use crate::schema::DynSchema;
use crate::schema::VldSchema;
struct Variant {
discriminator_value: Value,
schema: Box<dyn DynSchema + Send + Sync>,
}
pub struct ZDiscriminatedUnion {
discriminator: String,
variants: Vec<Variant>,
}
impl ZDiscriminatedUnion {
pub fn new(discriminator: impl Into<String>) -> Self {
Self {
discriminator: discriminator.into(),
variants: vec![],
}
}
pub fn variant<S: DynSchema + Send + Sync + 'static>(
mut self,
value: impl Into<Value>,
schema: S,
) -> Self {
self.variants.push(Variant {
discriminator_value: value.into(),
schema: Box::new(schema),
});
self
}
pub fn discriminator(&self) -> &str {
&self.discriminator
}
pub fn variants(&self) -> impl Iterator<Item = (&Value, &dyn DynSchema)> {
self.variants
.iter()
.map(|v| (&v.discriminator_value, v.schema.as_ref() as &dyn DynSchema))
}
pub fn variant_str<S: DynSchema + Send + Sync + 'static>(self, value: &str, schema: S) -> Self {
self.variant(Value::String(value.to_string()), schema)
}
}
impl VldSchema for ZDiscriminatedUnion {
type Output = Value;
fn parse_value(&self, value: &Value) -> Result<Value, VldError> {
let obj = value.as_object().ok_or_else(|| {
VldError::from_code_with_value(
IssueCode::InvalidType {
expected: "object".to_string(),
received: value_type_name(value),
},
value,
)
})?;
let at_disc = |e: VldError| e.with_prefix(PathSegment::Field(self.discriminator.clone()));
let disc_value = obj
.get(&self.discriminator)
.ok_or_else(|| at_disc(VldError::from_code(IssueCode::MissingField)))?;
for variant in &self.variants {
if crate::primitives::json_eq(disc_value, &variant.discriminator_value) {
return variant.schema.dyn_parse(value);
}
}
let options = self
.variants
.iter()
.map(|v| v.discriminator_value.clone())
.collect();
Err(at_disc(VldError::from_code_with_value(
IssueCode::InvalidDiscriminator { options },
disc_value,
)))
}
}