Skip to content

Commit 764de53

Browse files
authored
fix: Move from serde private Content struct to serde_value. (#187)
* fix: Move from serde private Content struct to serde_value. Signed-off-by: David Steele <david.steele@alianza.com> * Markups Signed-off-by: David Steele <david.steele@alianza.com> --------- Signed-off-by: David Steele <david.steele@alianza.com>
1 parent 1a3231e commit 764de53

File tree

3 files changed

+289
-13
lines changed

3 files changed

+289
-13
lines changed

CHANGELOG.md

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
99

1010
### Fixed
1111

12+
## [7.0.0-rc3] - 2025-09-16
13+
### Fixed
14+
- Replace usage of `serde` `__private` module with `serde-value` crate to avoid breakage after `serde` 1.0.220.
15+
1216
## [7.0.0-rc2] - 2025-06-10
1317
### Changed
1418
- Replace `mime_multipart` with fork for hyper 1.x
@@ -229,7 +233,8 @@ No changes. We now think we've got enough to declare this crate stable.
229233
## [0.5.0] - 2017-09-18
230234
- Start of changelog.
231235

232-
[Unreleased]: https://github.com/Metaswitch/swagger-rs/compare/7.0.0-rc2...HEAD
236+
[Unreleased]: https://github.com/Metaswitch/swagger-rs/compare/7.0.0-rc3...HEAD
237+
[7.0.0-rc3]: https://github.com/Metaswitch/swagger-rs/compare/7.0.0-rc2...7.0.0-rc3
233238
[7.0.0-rc2]: https://github.com/Metaswitch/swagger-rs/compare/7.0.0-rc1...7.0.0-rc2
234239
[7.0.0-rc1]: https://github.com/Metaswitch/swagger-rs/compare/6.5.0...7.0.0-rc1
235240
[6.5.0]: https://github.com/Metaswitch/swagger-rs/compare/6.4.1...6.5.0

Cargo.toml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "swagger"
3-
version = "7.0.0-rc2"
3+
version = "7.0.0-rc3"
44
authors = ["Metaswitch Networks Ltd"]
55
license = "Apache-2.0"
66
description = "A set of common utilities for Rust code generated by OpenAPI Generator"
@@ -14,7 +14,7 @@ edition = "2021"
1414
default = ["serdejson"]
1515
multipart_form = ["mime"]
1616
multipart_related = ["mime_multipart", "mime"]
17-
serdejson = ["serde", "serde_json"]
17+
serdejson = ["serde_json"]
1818
serdevalid = ["serdejson", "serde_valid", "regex", "paste"]
1919
server = ["hyper/server"]
2020
http1 = ["hyper/http1"]
@@ -60,9 +60,10 @@ mime_multipart = { version = "0.10", package = "mime-multipart-hyper1", optional
6060
# serde
6161
paste = { version = "1", optional = true }
6262
regex = { version = "1", optional = true }
63-
serde = { version = "1.0.119", optional = true, features = ["derive"] }
63+
serde = { version = "1.0.225", features = ["derive"] }
6464
serde_json = { version = "1.0", optional = true }
6565
serde_valid = { version = "1.0", optional = true }
66+
serde-value = { version = "0.7" }
6667

6768
# UDS (Unix Domain Sockets)
6869
tokio = { version = "1.0", default-features = false, optional = true }

src/one_any_of.rs

Lines changed: 279 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,15 @@
11
//! Implementations of OpenAPI `oneOf` and `anyOf` types, assuming rules are just types
22
#[cfg(feature = "conversion")]
33
use frunk_enum_derive::LabelledGenericEnum;
4-
use serde::{
5-
de::Error,
6-
Deserialize, Deserializer, Serialize, Serializer,
7-
__private::de::{Content, ContentRefDeserializer},
8-
};
4+
use serde::{de::Error, Deserialize, Deserializer, Serialize, Serializer};
95
#[cfg(feature = "serdevalid")]
106
use serde_valid::Validate;
117
use std::fmt;
128
use std::str::FromStr;
139
use std::string::ToString;
1410

11+
use serde_value::Value as SerdeValue;
12+
1513
// Define a macro to define the common parts between `OneOf` and `AnyOf` enums for a specific
1614
// number of inner types.
1715
macro_rules! common_one_any_of {
@@ -67,10 +65,11 @@ macro_rules! one_of {
6765
$($i: PartialEq + for<'a> Deserialize<'a>,)*
6866
{
6967
fn deserialize<De: Deserializer<'b>>(deserializer: De) -> Result<Self, De::Error> {
70-
let content = Content::deserialize(deserializer)?;
68+
// Capture once into a generic value (serde_value supports all JSON-like data)
69+
let captured: SerdeValue = SerdeValue::deserialize(deserializer)?;
7170
let mut result = Err(De::Error::custom("data did not match any within oneOf"));
7271
$(
73-
if let Ok(inner) = $i::deserialize(ContentRefDeserializer::<De::Error>::new(&content)) {
72+
if let Ok(inner) = <$i as Deserialize>::deserialize(captured.clone()) {
7473
if result.is_err() {
7574
result = Ok(Self::$i(inner));
7675
} else {
@@ -133,9 +132,9 @@ macro_rules! any_of {
133132
$($i: PartialEq + for<'a> Deserialize<'a>,)*
134133
{
135134
fn deserialize<De: Deserializer<'b>>(deserializer: De) -> Result<Self, De::Error> {
136-
let content = Content::deserialize(deserializer)?;
135+
let captured: SerdeValue = SerdeValue::deserialize(deserializer)?;
137136
$(
138-
if let Ok(inner) = $i::deserialize(ContentRefDeserializer::<De::Error>::new(&content)) {
137+
if let Ok(inner) = <$i as Deserialize>::deserialize(captured.clone()) {
139138
return Ok(Self::$i(inner));
140139
}
141140
)*
@@ -176,3 +175,274 @@ any_of!(AnyOf13, A, B, C, D, E, F, G, H, I, J, K, L, M);
176175
any_of!(AnyOf14, A, B, C, D, E, F, G, H, I, J, K, L, M, N);
177176
any_of!(AnyOf15, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O);
178177
any_of!(AnyOf16, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P);
178+
179+
#[cfg(test)]
180+
mod tests {
181+
use super::*;
182+
183+
#[test]
184+
fn anyof_prefers_first_matching_deserialize_number() {
185+
let json = "123";
186+
let v: AnyOf2<u32, String> = serde_json::from_str(json).unwrap();
187+
match v {
188+
AnyOf2::A(n) => assert_eq!(n, 123),
189+
AnyOf2::B(_) => panic!("expected A variant"),
190+
}
191+
}
192+
193+
#[test]
194+
fn anyof_prefers_first_matching_fromstr_number() {
195+
let v = AnyOf2::<u8, String>::from_str("123").unwrap();
196+
match v {
197+
AnyOf2::A(n) => assert_eq!(n, 123),
198+
AnyOf2::B(_) => panic!("expected A variant"),
199+
}
200+
}
201+
202+
#[test]
203+
fn anyof_deserialize_string_to_second_variant() {
204+
let json = "\"hello\"";
205+
let v: AnyOf2<u32, String> = serde_json::from_str(json).unwrap();
206+
match v {
207+
AnyOf2::B(s) => assert_eq!(s, "hello"),
208+
AnyOf2::A(_) => panic!("expected B variant"),
209+
}
210+
}
211+
212+
#[test]
213+
fn oneof_deserialize_single_match() {
214+
let json = "\"hi\"";
215+
let v: OneOf2<u32, String> = serde_json::from_str(json).unwrap();
216+
match v {
217+
OneOf2::B(s) => assert_eq!(s, "hi"),
218+
OneOf2::A(_) => panic!("expected B variant"),
219+
}
220+
}
221+
222+
#[test]
223+
fn oneof_deserialize_error_on_multiple_matches() {
224+
// both u32 and u64 will deserialize from "123" -> ambiguity -> error
225+
let json = "123";
226+
let res: Result<OneOf2<u32, u64>, _> = serde_json::from_str(json);
227+
assert!(res.is_err(), "expected error when multiple variants match");
228+
}
229+
230+
#[test]
231+
fn oneof_fromstr_error_on_multiple_matches() {
232+
// String::from_str always succeeds and u8::from_str also succeeds here -> multiple matches
233+
let res = OneOf2::<u8, String>::from_str("123");
234+
assert!(res.is_err(), "expected error when multiple FromStr matches");
235+
}
236+
237+
#[test]
238+
fn display_and_serialize_roundtrip() {
239+
let a: OneOf2<u32, String> = OneOf2::A(7u32);
240+
assert_eq!(a.to_string(), "7");
241+
let ser = serde_json::to_string(&a).unwrap();
242+
assert_eq!(ser, "7");
243+
244+
let b: OneOf2<u32, String> = OneOf2::B(String::from("abc"));
245+
assert_eq!(b.to_string(), "abc");
246+
let ser_b = serde_json::to_string(&b).unwrap();
247+
assert_eq!(ser_b, "\"abc\"");
248+
}
249+
250+
#[test]
251+
fn map_ambiguity_oneof() {
252+
#[derive(Debug, PartialEq, Deserialize)]
253+
struct S1 {
254+
x: u32,
255+
}
256+
#[derive(Debug, PartialEq, Deserialize)]
257+
struct S2 {
258+
x: u64,
259+
}
260+
let json = "{\"x\":123}";
261+
let res: Result<OneOf2<S1, S2>, _> = serde_json::from_str(json);
262+
assert!(
263+
res.is_err(),
264+
"expected ambiguity error for map matching two structs"
265+
);
266+
}
267+
268+
#[test]
269+
fn map_first_match_anyof() {
270+
#[derive(Debug, PartialEq, Deserialize)]
271+
struct S1 {
272+
x: u32,
273+
}
274+
#[derive(Debug, PartialEq, Deserialize)]
275+
struct S2 {
276+
x: u64,
277+
}
278+
let json = "{\"x\":123}";
279+
let v: AnyOf2<S1, S2> = serde_json::from_str(json).unwrap();
280+
match v {
281+
AnyOf2::A(s) => assert_eq!(s.x, 123),
282+
AnyOf2::B(_) => panic!("expected first struct"),
283+
}
284+
}
285+
286+
#[test]
287+
fn null_ambiguity_oneof() {
288+
let json = "null";
289+
// Option<Unit> deserializes to None from null; Unit also deserializes from null? (No, unit struct expects object usually) -> To create ambiguity, use Option<String> and Option<u32> both None
290+
let res: Result<OneOf2<Option<u32>, Option<String>>, _> = serde_json::from_str(json);
291+
assert!(
292+
res.is_err(),
293+
"expected ambiguity with null across two Option types"
294+
);
295+
}
296+
297+
#[test]
298+
fn null_preference_anyof() {
299+
let json = "null";
300+
let v: AnyOf2<Option<u32>, Option<String>> = serde_json::from_str(json).unwrap();
301+
match v {
302+
AnyOf2::A(opt) => assert!(opt.is_none()),
303+
AnyOf2::B(_) => panic!("expected first Option variant"),
304+
}
305+
}
306+
307+
#[test]
308+
fn sequence_ambiguity_oneof() {
309+
let json = "[1,2]";
310+
let res: Result<OneOf2<Vec<u8>, Vec<u16>>, _> = serde_json::from_str(json);
311+
assert!(
312+
res.is_err(),
313+
"expected ambiguity for two vector numeric element types fitting both"
314+
);
315+
}
316+
317+
#[test]
318+
fn sequence_first_match_anyof() {
319+
let json = "[1,2]";
320+
let v: AnyOf2<Vec<u8>, Vec<u16>> = serde_json::from_str(json).unwrap();
321+
match v {
322+
AnyOf2::A(v1) => assert_eq!(v1, vec![1, 2]),
323+
AnyOf2::B(_) => panic!("expected first vector variant"),
324+
}
325+
}
326+
327+
#[test]
328+
fn large_number_anyof_prefers_second() {
329+
let json = "5000000000"; // > u32::MAX
330+
let v: AnyOf2<u32, u64> = serde_json::from_str(json).unwrap();
331+
match v {
332+
AnyOf2::B(n) => assert_eq!(n, 5_000_000_000u64),
333+
AnyOf2::A(_) => panic!("expected second variant since first fails"),
334+
}
335+
}
336+
337+
#[test]
338+
fn oneof_deserialize_no_match_error() {
339+
// bool doesn't match u32 or String (needs quotes for string)
340+
let json = "true";
341+
let res: Result<OneOf2<u32, String>, _> = serde_json::from_str(json);
342+
assert!(res.is_err());
343+
let msg = format!("{}", res.unwrap_err());
344+
assert!(
345+
msg.contains("did not match any within oneOf"),
346+
"unexpected error message: {msg}"
347+
);
348+
}
349+
350+
#[test]
351+
fn anyof_deserialize_no_match_error() {
352+
let json = "false"; // neither u32 nor String
353+
let res: Result<AnyOf2<u32, String>, _> = serde_json::from_str(json);
354+
assert!(res.is_err());
355+
let msg = format!("{}", res.unwrap_err());
356+
assert!(
357+
msg.contains("did not match any within anyOf"),
358+
"unexpected error message: {msg}"
359+
);
360+
}
361+
362+
#[test]
363+
fn oneof_fromstr_single_match() {
364+
let v = OneOf2::<bool, u8>::from_str("true").expect("bool should parse");
365+
match v {
366+
OneOf2::A(b) => assert!(b),
367+
_ => panic!("expected bool variant"),
368+
}
369+
}
370+
371+
#[test]
372+
fn oneof_fromstr_no_match() {
373+
let res = OneOf2::<u32, u16>::from_str("abc");
374+
assert!(res.is_err());
375+
}
376+
377+
#[test]
378+
fn anyof_fromstr_later_match() {
379+
let v = AnyOf2::<u32, bool>::from_str("true").expect("bool should parse");
380+
match v {
381+
AnyOf2::B(b) => assert!(b),
382+
_ => panic!("expected second bool variant"),
383+
}
384+
}
385+
386+
#[test]
387+
fn anyof_fromstr_no_match() {
388+
let res = AnyOf2::<u32, u16>::from_str("abc");
389+
assert!(res.is_err());
390+
}
391+
392+
#[test]
393+
fn oneof_higher_arity_ambiguity() {
394+
// "5" (number) matches u8, u16, u32 -> ambiguity error
395+
let json = "5";
396+
let res: Result<OneOf3<u8, u16, u32>, _> = serde_json::from_str(json);
397+
assert!(res.is_err(), "expected ambiguity for three numeric matches");
398+
}
399+
400+
#[test]
401+
fn anyof_higher_arity_middle_match() {
402+
// true fails u32, matches bool (middle), should select variant B
403+
let json = "true";
404+
let v: AnyOf3<u32, bool, String> = serde_json::from_str(json).unwrap();
405+
match v {
406+
AnyOf3::B(b) => assert!(b),
407+
_ => panic!("expected middle bool variant"),
408+
}
409+
}
410+
411+
#[test]
412+
fn oneof_display_serialize_variant_c() {
413+
let c: OneOf3<String, u32, bool> = OneOf3::C(true);
414+
assert_eq!(c.to_string(), "true");
415+
let ser = serde_json::to_string(&c).unwrap();
416+
assert_eq!(ser, "true");
417+
}
418+
419+
#[test]
420+
fn anyof_nested_structure_second_variant() {
421+
#[derive(Debug, PartialEq, Deserialize)]
422+
struct A {
423+
x: u32,
424+
}
425+
#[derive(Debug, PartialEq, Deserialize)]
426+
struct B {
427+
y: u32,
428+
}
429+
let json = "{\"y\":5}";
430+
let v: AnyOf3<A, B, String> = serde_json::from_str(json).unwrap();
431+
match v {
432+
AnyOf3::B(b) => assert_eq!(b.y, 5),
433+
_ => panic!("expected struct B variant"),
434+
}
435+
}
436+
437+
#[test]
438+
fn oneof_ambiguity_error_message() {
439+
let json = "123"; // matches multiple integer widths
440+
let res: Result<OneOf2<u32, u64>, _> = serde_json::from_str(json);
441+
let err = res.unwrap_err();
442+
let msg = format!("{}", err);
443+
assert!(
444+
msg.contains("data matched multiple within oneOf"),
445+
"missing ambiguity message: {msg}"
446+
);
447+
}
448+
}

0 commit comments

Comments
 (0)