use serde_json::Value;
use crate::error::{value_type_name, IssueCode, VldError};
use crate::object::DynSchema;
use crate::schema::VldSchema;
struct Variant {
discriminator_value: Value,
schema: Box<dyn DynSchema>,
}
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 + '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 variant_str<S: DynSchema + '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::single(
IssueCode::InvalidType {
expected: "object".to_string(),
received: value_type_name(value),
},
format!("Expected object, received {}", value_type_name(value)),
)
})?;
let disc_value = obj.get(&self.discriminator).ok_or_else(|| {
VldError::single(
IssueCode::MissingField,
format!("Missing discriminator field \"{}\"", self.discriminator),
)
})?;
for variant in &self.variants {
if *disc_value == variant.discriminator_value {
return variant.schema.dyn_parse(value);
}
}
let known: Vec<String> = self
.variants
.iter()
.map(|v| format!("{}", v.discriminator_value))
.collect();
Err(VldError::single(
IssueCode::Custom {
code: "invalid_discriminator".to_string(),
},
format!(
"Invalid discriminator value {}. Expected one of: {}",
disc_value,
known.join(", ")
),
))
}
}