Skip to main content

vld/combinators/
union.rs

1use serde_json::Value;
2
3use crate::error::{IssueCode, VldError};
4use crate::schema::VldSchema;
5
6/// A value that can be one of two types.
7#[derive(Debug, Clone, PartialEq)]
8#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
9#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
10pub enum Either<A, B> {
11    Left(A),
12    Right(B),
13}
14
15impl<A, B> Either<A, B> {
16    pub fn is_left(&self) -> bool {
17        matches!(self, Either::Left(_))
18    }
19    pub fn is_right(&self) -> bool {
20        matches!(self, Either::Right(_))
21    }
22    pub fn left(self) -> Option<A> {
23        match self {
24            Either::Left(a) => Some(a),
25            _ => None,
26        }
27    }
28    pub fn right(self) -> Option<B> {
29        match self {
30            Either::Right(b) => Some(b),
31            _ => None,
32        }
33    }
34}
35
36/// Union of two schemas. Tries the first, then the second.
37///
38/// Created via [`vld::union()`](crate::union()).
39pub struct ZUnion2<A: VldSchema, B: VldSchema> {
40    first: A,
41    second: B,
42}
43
44impl<A: VldSchema, B: VldSchema> ZUnion2<A, B> {
45    pub fn new(first: A, second: B) -> Self {
46        Self { first, second }
47    }
48
49    /// Access the first schema.
50    pub fn schema_a(&self) -> &A {
51        &self.first
52    }
53    /// Access the second schema.
54    pub fn schema_b(&self) -> &B {
55        &self.second
56    }
57}
58
59impl<A: VldSchema, B: VldSchema> VldSchema for ZUnion2<A, B> {
60    type Output = Either<A::Output, B::Output>;
61
62    fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
63        if let Ok(v) = self.first.parse_value(value) {
64            return Ok(Either::Left(v));
65        }
66        if let Ok(v) = self.second.parse_value(value) {
67            return Ok(Either::Right(v));
68        }
69        Err(VldError::single(
70            IssueCode::Custom {
71                code: "invalid_union".to_string(),
72            },
73            "Input did not match any variant of the union",
74        ))
75    }
76}
77
78/// A value that can be one of three types.
79#[derive(Debug, Clone, PartialEq)]
80#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
81#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
82pub enum Either3<A, B, C> {
83    First(A),
84    Second(B),
85    Third(C),
86}
87
88/// Union of three schemas.
89///
90/// Created via [`vld::union3()`](crate::union3).
91pub struct ZUnion3<A: VldSchema, B: VldSchema, C: VldSchema> {
92    first: A,
93    second: B,
94    third: C,
95}
96
97impl<A: VldSchema, B: VldSchema, C: VldSchema> ZUnion3<A, B, C> {
98    pub fn new(first: A, second: B, third: C) -> Self {
99        Self {
100            first,
101            second,
102            third,
103        }
104    }
105
106    /// Access the first schema.
107    pub fn schema_a(&self) -> &A {
108        &self.first
109    }
110    /// Access the second schema.
111    pub fn schema_b(&self) -> &B {
112        &self.second
113    }
114    /// Access the third schema.
115    pub fn schema_c(&self) -> &C {
116        &self.third
117    }
118}
119
120impl<A: VldSchema, B: VldSchema, C: VldSchema> VldSchema for ZUnion3<A, B, C> {
121    type Output = Either3<A::Output, B::Output, C::Output>;
122
123    fn parse_value(&self, value: &Value) -> Result<Self::Output, VldError> {
124        if let Ok(v) = self.first.parse_value(value) {
125            return Ok(Either3::First(v));
126        }
127        if let Ok(v) = self.second.parse_value(value) {
128            return Ok(Either3::Second(v));
129        }
130        if let Ok(v) = self.third.parse_value(value) {
131            return Ok(Either3::Third(v));
132        }
133        Err(VldError::single(
134            IssueCode::Custom {
135                code: "invalid_union".to_string(),
136            },
137            "Input did not match any variant of the union",
138        ))
139    }
140}