Skip to content

Commit aa7b018

Browse files
committed
Extend OneOrMany implementation to more collection types
This makes use of the foreach_seq! macro to generate the implementations for all sequence and set types. Closes #925 Closes #926
1 parent d9d8974 commit aa7b018

File tree

4 files changed

+197
-54
lines changed

4 files changed

+197
-54
lines changed

serde_with/src/de/impls.rs

Lines changed: 44 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1705,35 +1705,52 @@ impl<'de, const N: usize> DeserializeAs<'de, Box<[u8; N]>> for Bytes {
17051705
}
17061706

17071707
#[cfg(feature = "alloc")]
1708-
impl<'de, T, TAs, FORMAT> DeserializeAs<'de, Vec<T>> for OneOrMany<TAs, FORMAT>
1709-
where
1710-
TAs: DeserializeAs<'de, T>,
1711-
FORMAT: Format,
1712-
{
1713-
fn deserialize_as<D>(deserializer: D) -> Result<Vec<T>, D::Error>
1714-
where
1715-
D: Deserializer<'de>,
1716-
{
1717-
let is_hr = deserializer.is_human_readable();
1718-
let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?;
1708+
macro_rules! one_or_many_impl {
1709+
(
1710+
$ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)? $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >,
1711+
$with_capacity:expr,
1712+
$append:ident
1713+
) => {
1714+
impl<'de, T, TAs, FORMAT $(, $typaram)*> DeserializeAs<'de, $ty<T $(, $typaram)*>> for OneOrMany<TAs, FORMAT>
1715+
where
1716+
TAs: DeserializeAs<'de, T>,
1717+
FORMAT: Format,
1718+
$(T: $tbound1 $(+ $tbound2)*,)?
1719+
$($typaram: $bound1 $(+ $bound2)*),*
1720+
{
1721+
fn deserialize_as<D>(deserializer: D) -> Result<$ty<T $(, $typaram)*>, D::Error>
1722+
where
1723+
D: Deserializer<'de>,
1724+
{
1725+
let is_hr = deserializer.is_human_readable();
1726+
let content: content::de::Content<'de> = Deserialize::deserialize(deserializer)?;
17191727

1720-
let one_err: D::Error = match <DeserializeAsWrap<T, TAs>>::deserialize(
1721-
content::de::ContentRefDeserializer::new(&content, is_hr),
1722-
) {
1723-
Ok(one) => return Ok(alloc::vec![one.into_inner()]),
1724-
Err(err) => err,
1725-
};
1726-
let many_err: D::Error = match <DeserializeAsWrap<Vec<T>, Vec<TAs>>>::deserialize(
1727-
content::de::ContentDeserializer::new(content, is_hr),
1728-
) {
1729-
Ok(many) => return Ok(many.into_inner()),
1730-
Err(err) => err,
1731-
};
1732-
Err(DeError::custom(format_args!(
1733-
"OneOrMany could not deserialize any variant:\n One: {one_err}\n Many: {many_err}"
1734-
)))
1735-
}
1728+
let one_err: D::Error = match <DeserializeAsWrap<T, TAs>>::deserialize(
1729+
content::de::ContentRefDeserializer::new(&content, is_hr),
1730+
) {
1731+
Ok(one) => {
1732+
#[allow(clippy::redundant_closure_call)]
1733+
let mut values = ($with_capacity)(1);
1734+
values.$append(one.into_inner());
1735+
return Ok(values.into());
1736+
}
1737+
Err(err) => err,
1738+
};
1739+
let many_err: D::Error = match <DeserializeAsWrap<$ty<T $(, $typaram)*>, $ty<TAs $(, $typaram)*>>>::deserialize(
1740+
content::de::ContentDeserializer::new(content, is_hr),
1741+
) {
1742+
Ok(many) => return Ok(many.into_inner()),
1743+
Err(err) => err,
1744+
};
1745+
Err(DeError::custom(format_args!(
1746+
"OneOrMany could not deserialize any variant:\n One: {one_err}\n Many: {many_err}"
1747+
)))
1748+
}
1749+
}
1750+
};
17361751
}
1752+
#[cfg(feature = "alloc")]
1753+
foreach_seq!(one_or_many_impl);
17371754

17381755
#[cfg(all(feature = "alloc", feature = "smallvec_1"))]
17391756
impl<'de, T, TAs, FORMAT, A> DeserializeAs<'de, smallvec_1::SmallVec<A>> for OneOrMany<TAs, FORMAT>

serde_with/src/lib.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1707,8 +1707,9 @@ pub struct Bytes;
17071707
/// assert_eq!(serde_json::to_value(data).unwrap(), j);
17081708
/// # }
17091709
/// ```
1710-
#[cfg(feature = "alloc")]
1711-
pub struct OneOrMany<T, FORMAT: formats::Format = formats::PreferOne>(PhantomData<(T, FORMAT)>);
1710+
pub struct OneOrMany<T: ?Sized, FORMAT: formats::Format = formats::PreferOne>(
1711+
PhantomData<(FORMAT, T)>,
1712+
);
17121713

17131714
/// Try multiple deserialization options until one succeeds.
17141715
///

serde_with/src/ser/impls.rs

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -936,34 +936,43 @@ impl<'a, const N: usize> SerializeAs<Cow<'a, [u8; N]>> for Bytes {
936936
}
937937

938938
#[cfg(feature = "alloc")]
939-
impl<T, U> SerializeAs<Vec<T>> for OneOrMany<U, formats::PreferOne>
940-
where
941-
U: SerializeAs<T>,
942-
{
943-
fn serialize_as<S>(source: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
944-
where
945-
S: Serializer,
946-
{
947-
match source.len() {
948-
1 => SerializeAsWrap::<T, U>::new(source.iter().next().expect("Cannot be empty"))
949-
.serialize(serializer),
950-
_ => SerializeAsWrap::<Vec<T>, Vec<U>>::new(source).serialize(serializer),
939+
macro_rules! one_or_many_impl {
940+
($ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound:ident )* >) => {
941+
impl<T, U $(, $typaram)*> SerializeAs<$ty<T $(, $typaram)*>> for OneOrMany<U, formats::PreferOne>
942+
where
943+
U: SerializeAs<T>,
944+
$(T: ?Sized + $tbound1 $(+ $tbound2)*,)*
945+
$($typaram: ?Sized + $bound,)*
946+
{
947+
fn serialize_as<S>(source: &$ty<T $(, $typaram)*>, serializer: S) -> Result<S::Ok, S::Error>
948+
where
949+
S: Serializer,
950+
{
951+
match source.len() {
952+
1 => SerializeAsWrap::<T, U>::new(source.iter().next().expect("Cannot be empty"))
953+
.serialize(serializer),
954+
_ => serializer.collect_seq(source.iter().map(|item| SerializeAsWrap::<T, U>::new(item))),
955+
}
956+
}
951957
}
952-
}
953-
}
954958

955-
#[cfg(feature = "alloc")]
956-
impl<T, U> SerializeAs<Vec<T>> for OneOrMany<U, formats::PreferMany>
957-
where
958-
U: SerializeAs<T>,
959-
{
960-
fn serialize_as<S>(source: &Vec<T>, serializer: S) -> Result<S::Ok, S::Error>
961-
where
962-
S: Serializer,
963-
{
964-
SerializeAsWrap::<Vec<T>, Vec<U>>::new(source).serialize(serializer)
959+
impl<T, U $(, $typaram)*> SerializeAs<$ty<T $(, $typaram)*>> for OneOrMany<U, formats::PreferMany>
960+
where
961+
U: SerializeAs<T>,
962+
$(T: ?Sized + $tbound1 $(+ $tbound2)*,)*
963+
$($typaram: ?Sized + $bound,)*
964+
{
965+
fn serialize_as<S>(source: &$ty<T $(, $typaram)*>, serializer: S) -> Result<S::Ok, S::Error>
966+
where
967+
S: Serializer,
968+
{
969+
serializer.collect_seq(source.iter().map(|item| SerializeAsWrap::<T, U>::new(item)))
970+
}
971+
}
965972
}
966973
}
974+
#[cfg(feature = "alloc")]
975+
foreach_seq!(one_or_many_impl);
967976

968977
#[cfg(all(feature = "alloc", feature = "smallvec_1"))]
969978
impl<T, TAs, A> SerializeAs<smallvec_1::SmallVec<A>> for OneOrMany<TAs, formats::PreferOne>

serde_with/tests/serde_as/lib.rs

Lines changed: 117 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ use serde_with::{
3535
NoneAsEmptyString, OneOrMany, Same, Seq, StringWithSeparator,
3636
};
3737
use std::{
38-
collections::HashMap,
38+
collections::{HashMap, HashSet},
3939
sync::{Mutex, RwLock},
4040
};
4141

@@ -1323,6 +1323,122 @@ fn test_one_or_many_prefer_many() {
13231323
);
13241324
}
13251325

1326+
#[test]
1327+
fn test_one_or_many_hashset_prefer_one() {
1328+
#[serde_as]
1329+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1330+
struct S1(#[serde_as(as = "OneOrMany<_>")] HashSet<u32>);
1331+
1332+
is_equal(S1(HashSet::new()), expect![[r#"[]"#]]);
1333+
is_equal(S1(HashSet::from([1])), expect![[r#"1"#]]);
1334+
check_deserialization(S1(HashSet::from([1])), r#"1"#);
1335+
check_deserialization(S1(HashSet::from([1])), r#"[1]"#);
1336+
check_deserialization(S1(HashSet::from([1, 2, 3])), r#"[1, 2, 3]"#);
1337+
check_deserialization(S1(HashSet::from([1, 2, 3])), r#"[3, 2, 1]"#);
1338+
check_error_deserialization::<S1>(
1339+
r#""xx""#,
1340+
expect![[r#"
1341+
OneOrMany could not deserialize any variant:
1342+
One: invalid type: string "xx", expected u32
1343+
Many: invalid type: string "xx", expected a sequence"#]],
1344+
);
1345+
1346+
#[serde_as]
1347+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1348+
struct S2(#[serde_as(as = "OneOrMany<DisplayFromStr>")] HashSet<u32>);
1349+
1350+
is_equal(S2(HashSet::from([1])), expect![[r#""1""#]]);
1351+
check_deserialization(S2(HashSet::from([1])), r#""1""#);
1352+
check_deserialization(S2(HashSet::from([1])), r#"["1"]"#);
1353+
check_deserialization(S2(HashSet::from([1, 2, 3])), r#"["1", "2", "3"]"#);
1354+
check_error_deserialization::<S2>(
1355+
r#"{}"#,
1356+
expect![[r#"
1357+
OneOrMany could not deserialize any variant:
1358+
One: invalid type: map, expected a string
1359+
Many: invalid type: map, expected a sequence"#]],
1360+
);
1361+
}
1362+
1363+
#[test]
1364+
fn test_one_or_many_hashset_prefer_many() {
1365+
use serde_with::formats::PreferMany;
1366+
1367+
#[serde_as]
1368+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1369+
struct S1(#[serde_as(as = "OneOrMany<_, PreferMany>")] HashSet<u32>);
1370+
1371+
is_equal(S1(HashSet::new()), expect![[r#"[]"#]]);
1372+
is_equal(
1373+
S1(HashSet::from([1])),
1374+
expect![[r#"
1375+
[
1376+
1
1377+
]"#]],
1378+
);
1379+
check_deserialization(S1(HashSet::from([1])), r#"1"#);
1380+
check_deserialization(S1(HashSet::from([1])), r#"[1]"#);
1381+
check_deserialization(S1(HashSet::from([1, 2, 3])), r#"[1, 2, 3]"#);
1382+
}
1383+
1384+
#[test]
1385+
fn test_one_or_many_btreeset_prefer_one() {
1386+
#[serde_as]
1387+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1388+
struct S1(#[serde_as(as = "OneOrMany<_>")] BTreeSet<u32>);
1389+
1390+
is_equal(S1(BTreeSet::new()), expect![[r#"[]"#]]);
1391+
is_equal(S1(BTreeSet::from([1])), expect![[r#"1"#]]);
1392+
check_deserialization(S1(BTreeSet::from([1])), r#"1"#);
1393+
check_deserialization(S1(BTreeSet::from([1])), r#"[1]"#);
1394+
check_deserialization(S1(BTreeSet::from([1, 2, 3])), r#"[1, 2, 3]"#);
1395+
check_deserialization(S1(BTreeSet::from([1, 2, 3])), r#"[3, 2, 1]"#);
1396+
check_error_deserialization::<S1>(
1397+
r#""xx""#,
1398+
expect![[r#"
1399+
OneOrMany could not deserialize any variant:
1400+
One: invalid type: string "xx", expected u32
1401+
Many: invalid type: string "xx", expected a sequence"#]],
1402+
);
1403+
1404+
#[serde_as]
1405+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1406+
struct S2(#[serde_as(as = "OneOrMany<DisplayFromStr>")] BTreeSet<u32>);
1407+
1408+
is_equal(S2(BTreeSet::from([1])), expect![[r#""1""#]]);
1409+
check_deserialization(S2(BTreeSet::from([1])), r#""1""#);
1410+
check_deserialization(S2(BTreeSet::from([1])), r#"["1"]"#);
1411+
check_deserialization(S2(BTreeSet::from([1, 2, 3])), r#"["1", "2", "3"]"#);
1412+
check_error_deserialization::<S2>(
1413+
r#"{}"#,
1414+
expect![[r#"
1415+
OneOrMany could not deserialize any variant:
1416+
One: invalid type: map, expected a string
1417+
Many: invalid type: map, expected a sequence"#]],
1418+
);
1419+
}
1420+
1421+
#[test]
1422+
fn test_one_or_many_btreeset_prefer_many() {
1423+
use serde_with::formats::PreferMany;
1424+
1425+
#[serde_as]
1426+
#[derive(Debug, Serialize, Deserialize, PartialEq)]
1427+
struct S1(#[serde_as(as = "OneOrMany<_, PreferMany>")] BTreeSet<u32>);
1428+
1429+
is_equal(S1(BTreeSet::new()), expect![[r#"[]"#]]);
1430+
is_equal(
1431+
S1(BTreeSet::from([1])),
1432+
expect![[r#"
1433+
[
1434+
1
1435+
]"#]],
1436+
);
1437+
check_deserialization(S1(BTreeSet::from([1])), r#"1"#);
1438+
check_deserialization(S1(BTreeSet::from([1])), r#"[1]"#);
1439+
check_deserialization(S1(BTreeSet::from([1, 2, 3])), r#"[1, 2, 3]"#);
1440+
}
1441+
13261442
/// Test that Cow borrows from the input
13271443
#[test]
13281444
fn test_borrow_cow_str() {

0 commit comments

Comments
 (0)