salvo_oapi/openapi/schema/
one_of.rs1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4use crate::{Array, Discriminator, PropMap, RefOr, Schema, SchemaType};
5
6#[non_exhaustive]
13#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
14pub struct OneOf {
15    #[serde(rename = "oneOf")]
17    pub items: Vec<RefOr<Schema>>,
18
19    #[serde(
24        rename = "type",
25        default = "SchemaType::any",
26        skip_serializing_if = "SchemaType::is_any_value"
27    )]
28    pub schema_type: SchemaType,
29
30    #[serde(skip_serializing_if = "Option::is_none")]
32    pub title: Option<String>,
33
34    #[serde(skip_serializing_if = "Option::is_none")]
36    pub description: Option<String>,
37
38    #[serde(rename = "default", skip_serializing_if = "Option::is_none")]
40    pub default_value: Option<Value>,
41
42    #[serde(skip_serializing_if = "Vec::is_empty", default)]
44    pub examples: Vec<Value>,
45
46    #[serde(skip_serializing_if = "Option::is_none")]
49    pub discriminator: Option<Discriminator>,
50
51    #[serde(skip_serializing_if = "PropMap::is_empty", flatten)]
53    pub extensions: PropMap<String, serde_json::Value>,
54}
55
56impl Default for OneOf {
57    fn default() -> Self {
58        Self {
59            items: Default::default(),
60            schema_type: SchemaType::AnyValue,
61            title: Default::default(),
62            description: Default::default(),
63            default_value: Default::default(),
64            examples: Default::default(),
65            discriminator: Default::default(),
66            extensions: Default::default(),
67        }
68    }
69}
70
71impl OneOf {
72    #[must_use]
74    pub fn new() -> Self {
75        Default::default()
76    }
77
78    #[must_use]
91    pub fn with_capacity(capacity: usize) -> Self {
92        Self {
93            items: Vec::with_capacity(capacity),
94            ..Default::default()
95        }
96    }
97    #[must_use]
101    pub fn item<I: Into<RefOr<Schema>>>(mut self, component: I) -> Self {
102        self.items.push(component.into());
103
104        self
105    }
106
107    #[must_use]
110    pub fn schema_type<T: Into<SchemaType>>(mut self, schema_type: T) -> Self {
111        self.schema_type = schema_type.into();
112        self
113    }
114
115    #[must_use]
117    pub fn title(mut self, title: impl Into<String>) -> Self {
118        self.title = Some(title.into());
119        self
120    }
121
122    #[must_use]
124    pub fn description(mut self, description: impl Into<String>) -> Self {
125        self.description = Some(description.into());
126        self
127    }
128
129    #[must_use]
131    pub fn default_value(mut self, default: Value) -> Self {
132        self.default_value = Some(default);
133        self
134    }
135
136    #[must_use]
138    pub fn add_example<V: Into<Value>>(mut self, example: V) -> Self {
139        self.examples.push(example.into());
140        self
141    }
142
143    #[must_use]
145    pub fn discriminator(mut self, discriminator: Discriminator) -> Self {
146        self.discriminator = Some(discriminator);
147        self
148    }
149
150    #[must_use]
152    pub fn add_extension<K: Into<String>>(mut self, key: K, value: serde_json::Value) -> Self {
153        self.extensions.insert(key.into(), value);
154        self
155    }
156
157    #[must_use]
159    pub fn to_array(self) -> Array {
160        Array::new().items(self)
161    }
162}
163
164impl From<OneOf> for Schema {
165    fn from(one_of: OneOf) -> Self {
166        Self::OneOf(one_of)
167    }
168}
169
170impl From<OneOf> for RefOr<Schema> {
171    fn from(one_of: OneOf) -> Self {
172        Self::Type(Schema::OneOf(one_of))
173    }
174}
175
176#[cfg(test)]
177mod tests {
178    use assert_json_diff::assert_json_eq;
179    use serde_json::json;
180
181    use super::*;
182
183    #[test]
184    fn test_build_one_of() {
185        let one_of = OneOf::with_capacity(5)
186            .title("title")
187            .description("description")
188            .default_value(Value::String("default".to_owned()))
189            .add_example(Value::String("example1".to_owned()))
190            .add_example(Value::String("example2".to_owned()))
191            .discriminator(Discriminator::new("discriminator".to_owned()));
192
193        assert_eq!(one_of.items.len(), 0);
194        assert_eq!(one_of.items.capacity(), 5);
195        assert_json_eq!(
196            one_of,
197            json!({
198                "oneOf": [],
199                "title": "title",
200                "description": "description",
201                "default": "default",
202                "examples": ["example1", "example2"],
203                "discriminator": {
204                    "propertyName": "discriminator"
205                }
206            })
207        )
208    }
209
210    #[test]
211    fn test_schema_from_one_of() {
212        let one_of = OneOf::new();
213        let schema = Schema::from(one_of);
214        assert_json_eq!(
215            schema,
216            json!({
217                "oneOf": []
218            })
219        )
220    }
221
222    #[test]
223    fn test_refor_schema_from_one_of() {
224        let one_of = OneOf::new();
225        let ref_or: RefOr<Schema> = RefOr::from(one_of);
226        assert_json_eq!(
227            ref_or,
228            json!({
229                "oneOf": []
230            })
231        )
232    }
233
234    #[test]
235    fn test_oneof_with_extensions() {
236        let expected = json!("value");
237        let json_value = OneOf::new().add_extension("x-some-extension", expected.clone());
238
239        let value = serde_json::to_value(&json_value).unwrap();
240        assert_eq!(value.get("x-some-extension"), Some(&expected));
241    }
242}