use serde_json::Value;
use crate::error::{IssueCode, VldError};
use crate::schema::VldSchema;
pub(crate) fn union_error(value: &Value, variants: Vec<VldError>) -> VldError {
let mut err = VldError::from_code_with_value(IssueCode::InvalidUnion, value);
let mut n = 0;
for e in variants {
let nested = e
.issues
.first()
.is_some_and(|i| i.code == IssueCode::InvalidUnion && i.path.is_empty());
if !nested {
n += 1;
for mut issue in e.issues {
issue.message = format!("[variant {}] {}", n, issue.message);
err.issues.push(issue);
}
continue;
}
let mut inner_count = 0;
for mut issue in e.issues.into_iter().skip(1) {
if let Some((k, rest)) = split_variant_prefix(&issue.message) {
inner_count = inner_count.max(k);
issue.message = format!("[variant {}] {}", n + k, rest);
}
err.issues.push(issue);
}
n += inner_count.max(1);
}
err
}
pub(crate) fn split_variant_prefix(message: &str) -> Option<(usize, &str)> {
let rest = message.strip_prefix("[variant ")?;
let (k, rest) = rest.split_once("] ")?;
Some((k.parse().ok()?, rest))
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Either<A, B> {
Left(A),
Right(B),
}
impl<A, B> Either<A, B> {
pub fn is_left(&self) -> bool {
matches!(self, Either::Left(_))
}
pub fn is_right(&self) -> bool {
matches!(self, Either::Right(_))
}
pub fn left(self) -> Option<A> {
match self {
Either::Left(a) => Some(a),
_ => None,
}
}
pub fn right(self) -> Option<B> {
match self {
Either::Right(b) => Some(b),
_ => None,
}
}
}
impl<T> Either<T, T> {
pub fn into_inner(self) -> T {
match self {
Either::Left(v) | Either::Right(v) => v,
}
}
}
pub struct ZUnion2<A: VldSchema, B: VldSchema> {
first: A,
second: B,
}
impl<A: VldSchema, B: VldSchema> ZUnion2<A, B> {
pub fn new(first: A, second: B) -> Self {
Self { first, second }
}
pub fn schema_a(&self) -> &A {
&self.first
}
pub fn schema_b(&self) -> &B {
&self.second
}
}
impl<A: VldSchema, B: VldSchema> VldSchema for ZUnion2<A, B> {
type Output = Either<A::Output, B::Output>;
fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
let e1 = match self.first.parse_value(value) {
Ok(v) => return Ok(Either::Left(v)),
Err(e) => e,
};
let e2 = match self.second.parse_value(value) {
Ok(v) => return Ok(Either::Right(v)),
Err(e) => e,
};
Err(union_error(value, vec![e1, e2]))
}
fn output_json(&self, o: &Self::Output) -> Option<Value> {
match o {
Either::Left(v) => self.first.output_json(v),
Either::Right(v) => self.second.output_json(v),
}
}
}
#[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
#[serde(untagged)]
pub enum Either3<A, B, C> {
First(A),
Second(B),
Third(C),
}
pub struct ZUnion3<A: VldSchema, B: VldSchema, C: VldSchema> {
first: A,
second: B,
third: C,
}
impl<A: VldSchema, B: VldSchema, C: VldSchema> ZUnion3<A, B, C> {
pub fn new(first: A, second: B, third: C) -> Self {
Self {
first,
second,
third,
}
}
pub fn schema_a(&self) -> &A {
&self.first
}
pub fn schema_b(&self) -> &B {
&self.second
}
pub fn schema_c(&self) -> &C {
&self.third
}
}
impl<A: VldSchema, B: VldSchema, C: VldSchema> VldSchema for ZUnion3<A, B, C> {
type Output = Either3<A::Output, B::Output, C::Output>;
fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
let e1 = match self.first.parse_value(value) {
Ok(v) => return Ok(Either3::First(v)),
Err(e) => e,
};
let e2 = match self.second.parse_value(value) {
Ok(v) => return Ok(Either3::Second(v)),
Err(e) => e,
};
let e3 = match self.third.parse_value(value) {
Ok(v) => return Ok(Either3::Third(v)),
Err(e) => e,
};
Err(union_error(value, vec![e1, e2, e3]))
}
fn output_json(&self, o: &Self::Output) -> Option<Value> {
match o {
Either3::First(v) => self.first.output_json(v),
Either3::Second(v) => self.second.output_json(v),
Either3::Third(v) => self.third.output_json(v),
}
}
}