Skip to content

Commit 23e08b4

Browse files
committed
fix: Move from serde private Content struct to serde_value.
Signed-off-by: David Steele <david.steele@alianza.com>
1 parent 1a3231e commit 23e08b4

3 files changed

Lines changed: 232 additions & 8 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
88
### Changed
99

1010
### Fixed
11+
- Replace usage of `serde` `__private` module with `serde-value` crate to avoid breakage after `serde` 1.0.220.
1112

1213
## [7.0.0-rc2] - 2025-06-10
1314
### Changed

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", "serde_json", "serde-value"]
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", optional = true, features = ["derive"] }
6464
serde_json = { version = "1.0", optional = true }
6565
serde_valid = { version = "1.0", optional = true }
66+
serde-value = { version = "0.7", optional = true }
6667

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

src/one_any_of.rs

Lines changed: 227 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,16 @@ use frunk_enum_derive::LabelledGenericEnum;
44
use serde::{
55
de::Error,
66
Deserialize, Deserializer, Serialize, Serializer,
7-
__private::de::{Content, ContentRefDeserializer},
87
};
98
#[cfg(feature = "serdevalid")]
109
use serde_valid::Validate;
1110
use std::fmt;
1211
use std::str::FromStr;
1312
use std::string::ToString;
1413

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

0 commit comments

Comments
 (0)