1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6use crate::{
7 Map,
8 model::{extensions::Extensions, reference::RefOr},
9};
10
11#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
29#[serde(try_from = "RawExample", into = "RawExample")]
30pub struct Example {
31 pub summary: Option<String>,
33
34 pub description: Option<String>,
38
39 value: Option<ExampleValue>,
40
41 pub extensions: Extensions,
43}
44
45#[non_exhaustive]
58#[derive(Clone, Debug, PartialEq, Eq)]
59pub enum ExampleValue {
60 Embedded(Value),
70
71 #[non_exhaustive]
79 External {
80 uri: String,
82
83 #[cfg(feature = "openapi32")]
85 data: Option<Value>,
86 },
87
88 #[cfg(feature = "openapi32")]
92 Data {
93 data: Value,
95
96 serialized: Option<String>,
98 },
99
100 #[cfg(feature = "openapi32")]
105 Serialized(String),
106}
107
108impl Example {
109 pub fn new(value: impl Into<Value>) -> Self {
111 Self::carrying(ExampleValue::Embedded(value.into()))
112 }
113
114 pub fn external(uri: impl Into<String>) -> Self {
116 Self::carrying(ExampleValue::External {
117 uri: uri.into(),
118 #[cfg(feature = "openapi32")]
119 data: None,
120 })
121 }
122
123 #[cfg(feature = "openapi32")]
128 pub fn data(data: impl Into<Value>) -> Self {
129 Self::carrying(ExampleValue::Data {
130 data: data.into(),
131 serialized: None,
132 })
133 }
134
135 #[cfg(feature = "openapi32")]
137 pub fn serialized(serialized: impl Into<String>) -> Self {
138 Self::carrying(ExampleValue::Serialized(serialized.into()))
139 }
140
141 #[cfg(feature = "openapi32")]
146 pub fn data_serialized(data: impl Into<Value>, serialized: impl Into<String>) -> Self {
147 Self::carrying(ExampleValue::Data {
148 data: data.into(),
149 serialized: Some(serialized.into()),
150 })
151 }
152
153 #[cfg(feature = "openapi32")]
155 pub fn data_external(data: impl Into<Value>, uri: impl Into<String>) -> Self {
156 Self::carrying(ExampleValue::External {
157 uri: uri.into(),
158 data: Some(data.into()),
159 })
160 }
161
162 #[must_use]
166 pub fn value(&self) -> Option<&ExampleValue> {
167 self.value.as_ref()
168 }
169
170 fn carrying(value: ExampleValue) -> Self {
171 Self {
172 value: Some(value),
173 ..Self::default()
174 }
175 }
176
177 #[must_use]
179 pub fn with_summary(mut self, summary: impl Into<String>) -> Self {
180 self.summary = Some(summary.into());
181 self
182 }
183
184 #[must_use]
186 pub fn with_description(mut self, description: impl Into<String>) -> Self {
187 self.description = Some(description.into());
188 self
189 }
190}
191
192#[derive(Serialize, Deserialize)]
194struct RawExample {
195 #[serde(default, skip_serializing_if = "Option::is_none")]
196 summary: Option<String>,
197
198 #[serde(default, skip_serializing_if = "Option::is_none")]
199 description: Option<String>,
200
201 #[serde(
202 default,
203 deserialize_with = "crate::model::nullable::some",
204 skip_serializing_if = "Option::is_none"
205 )]
206 value: Option<Value>,
207
208 #[cfg(feature = "openapi32")]
209 #[serde(
210 rename = "dataValue",
211 default,
212 deserialize_with = "crate::model::nullable::some",
213 skip_serializing_if = "Option::is_none"
214 )]
215 data_value: Option<Value>,
216
217 #[cfg(feature = "openapi32")]
218 #[serde(
219 rename = "serializedValue",
220 default,
221 skip_serializing_if = "Option::is_none"
222 )]
223 serialized_value: Option<String>,
224
225 #[serde(
226 rename = "externalValue",
227 default,
228 skip_serializing_if = "Option::is_none"
229 )]
230 external_value: Option<String>,
231
232 #[serde(flatten)]
233 extensions: Extensions,
234}
235
236#[derive(Debug)]
238enum ExampleConflict {
239 ValueNotAlone,
241
242 #[cfg(feature = "openapi32")]
244 TwoSerializations,
245}
246
247impl std::fmt::Display for ExampleConflict {
248 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
249 match self {
250 Self::ValueNotAlone => f.write_str(
251 "`value` is mutually exclusive with `dataValue`, `serializedValue` and \
252 `externalValue` on an Example Object",
253 ),
254 #[cfg(feature = "openapi32")]
255 Self::TwoSerializations => f.write_str(
256 "`serializedValue` and `externalValue` are mutually exclusive on an Example \
257 Object",
258 ),
259 }
260 }
261}
262
263impl TryFrom<RawExample> for Example {
264 type Error = ExampleConflict;
265
266 fn try_from(raw: RawExample) -> Result<Self, Self::Error> {
267 #[cfg(feature = "openapi32")]
270 let value = match (
271 raw.value,
272 raw.data_value,
273 raw.serialized_value,
274 raw.external_value,
275 ) {
276 (Some(_), Some(_), _, _) | (Some(_), _, Some(_), _) | (Some(_), _, _, Some(_)) => {
277 return Err(ExampleConflict::ValueNotAlone);
278 }
279 (_, _, Some(_), Some(_)) => return Err(ExampleConflict::TwoSerializations),
280
281 (Some(value), None, None, None) => Some(ExampleValue::Embedded(value)),
282 (None, data, None, Some(uri)) => Some(ExampleValue::External { uri, data }),
283 (None, Some(data), serialized, None) => Some(ExampleValue::Data { data, serialized }),
284 (None, None, Some(serialized), None) => Some(ExampleValue::Serialized(serialized)),
285 (None, None, None, None) => None,
286 };
287
288 #[cfg(not(feature = "openapi32"))]
289 let value = match (raw.value, raw.external_value) {
290 (Some(_), Some(_)) => return Err(ExampleConflict::ValueNotAlone),
291 (Some(value), None) => Some(ExampleValue::Embedded(value)),
292 (None, Some(uri)) => Some(ExampleValue::External { uri }),
293 (None, None) => None,
294 };
295
296 Ok(Self {
297 summary: raw.summary,
298 description: raw.description,
299 value,
300 extensions: raw.extensions,
301 })
302 }
303}
304
305impl From<Example> for RawExample {
306 fn from(example: Example) -> Self {
307 #[cfg(feature = "openapi32")]
308 let (value, data_value, serialized_value, external_value) = match example.value {
309 Some(ExampleValue::Embedded(value)) => (Some(value), None, None, None),
310 Some(ExampleValue::External { uri, data }) => (None, data, None, Some(uri)),
311 Some(ExampleValue::Data { data, serialized }) => (None, Some(data), serialized, None),
312 Some(ExampleValue::Serialized(serialized)) => (None, None, Some(serialized), None),
313 None => (None, None, None, None),
314 };
315
316 #[cfg(not(feature = "openapi32"))]
317 let (value, external_value) = match example.value {
318 Some(ExampleValue::Embedded(value)) => (Some(value), None),
319 Some(ExampleValue::External { uri }) => (None, Some(uri)),
320 None => (None, None),
321 };
322
323 Self {
324 summary: example.summary,
325 description: example.description,
326 value,
327 #[cfg(feature = "openapi32")]
328 data_value,
329 #[cfg(feature = "openapi32")]
330 serialized_value,
331 external_value,
332 extensions: example.extensions,
333 }
334 }
335}
336
337#[derive(Clone, Debug, PartialEq)]
349pub enum Examples {
350 Inline(Value),
352
353 Named(Map<RefOr<Example>>),
358}
359
360#[derive(Debug)]
362pub(crate) struct ExamplesConflict;
363
364impl std::fmt::Display for ExamplesConflict {
365 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
366 f.write_str("`example` and `examples` are mutually exclusive")
367 }
368}
369
370pub(crate) fn examples_from(
372 example: Option<Value>,
373 examples: Option<Map<RefOr<Example>>>,
374) -> Result<Option<Examples>, ExamplesConflict> {
375 match (example, examples) {
376 (Some(_), Some(_)) => Err(ExamplesConflict),
377 (Some(value), None) => Ok(Some(Examples::Inline(value))),
378 (None, Some(named)) => Ok(Some(Examples::Named(named))),
379 (None, None) => Ok(None),
380 }
381}
382
383pub(crate) fn examples_into(
385 examples: Option<Examples>,
386) -> (Option<Value>, Option<Map<RefOr<Example>>>) {
387 match examples {
388 Some(Examples::Inline(value)) => (Some(value), None),
389 Some(Examples::Named(named)) => (None, Some(named)),
390 None => (None, None),
391 }
392}
393
394pub(crate) fn examples_with_named(
399 examples: Option<Examples>,
400 name: String,
401 example: RefOr<Example>,
402) -> Examples {
403 let mut named = match examples {
404 Some(Examples::Named(named)) => named,
405 Some(Examples::Inline(_)) | None => Map::new(),
406 };
407 named.insert(name, example);
408 Examples::Named(named)
409}
410
411#[cfg(test)]
412mod tests;