From 8a0b890a6250dd25a5e424f43d0e3e896825f185 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Sat, 4 Mar 2023 17:16:58 +0100 Subject: [PATCH 1/3] Add serde_as compatible versions for duplicate key handling This adds `serde_as` compatible versions for these four modules: `maps_duplicate_key_is_error`, `maps_first_key_wins`, `sets_duplicate_value_is_error`, and `sets_last_value_wins`. Closes #534 --- serde_with/src/de/duplicates.rs | 223 +++++++++++++++++++++++++++++++ serde_with/src/de/mod.rs | 1 + serde_with/src/lib.rs | 113 ++++++++++++++++ serde_with/src/rust.rs | 66 ++++++++- serde_with/src/ser/duplicates.rs | 70 ++++++++++ serde_with/src/ser/mod.rs | 1 + 6 files changed, 473 insertions(+), 1 deletion(-) create mode 100644 serde_with/src/de/duplicates.rs create mode 100644 serde_with/src/ser/duplicates.rs diff --git a/serde_with/src/de/duplicates.rs b/serde_with/src/de/duplicates.rs new file mode 100644 index 00000000..a03dfba2 --- /dev/null +++ b/serde_with/src/de/duplicates.rs @@ -0,0 +1,223 @@ +use super::impls::{foreach_map_create, foreach_set_create}; +use crate::{ + duplicate_key_impls::{ + DuplicateInsertsFirstWinsMap, DuplicateInsertsLastWinsSet, PreventDuplicateInsertsMap, + PreventDuplicateInsertsSet, + }, + prelude::*, + MapFirstKeyWins, MapPreventDuplicates, SetLastValueWins, SetPreventDuplicates, +}; +#[cfg(feature = "indexmap_1")] +use indexmap_1::{IndexMap, IndexSet}; + +struct SetPreventDuplicatesVisitor(PhantomData<(SET, T, TAs)>); + +impl<'de, SET, T, TAs> Visitor<'de> for SetPreventDuplicatesVisitor +where + SET: PreventDuplicateInsertsSet, + TAs: DeserializeAs<'de, T>, +{ + type Value = SET; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(self, mut access: A) -> Result + where + A: SeqAccess<'de>, + { + let mut values = Self::Value::new(access.size_hint()); + + while let Some(value) = access.next_element::>()? { + if !values.insert(value.into_inner()) { + return Err(DeError::custom("invalid entry: found duplicate value")); + }; + } + + Ok(values) + } +} + +struct SetLastValueWinsVisitor(PhantomData<(SET, T, TAs)>); + +impl<'de, SET, T, TAs> Visitor<'de> for SetLastValueWinsVisitor +where + SET: DuplicateInsertsLastWinsSet, + TAs: DeserializeAs<'de, T>, +{ + type Value = SET; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a sequence") + } + + #[inline] + fn visit_seq(self, mut access: A) -> Result + where + A: SeqAccess<'de>, + { + let mut values = Self::Value::new(access.size_hint()); + + while let Some(value) = access.next_element::>()? { + values.replace(value.into_inner()); + } + + Ok(values) + } +} + +#[cfg(feature = "alloc")] +macro_rules! set_impl { + ( + $ty:ident < T $(: $tbound1:ident $(+ $tbound2:ident)*)* $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)* )* >, + $with_capacity:expr, + $append:ident + ) => { + impl<'de, T, TAs $(, $typaram)*> DeserializeAs<'de, $ty> for SetPreventDuplicates + where + TAs: DeserializeAs<'de, T>, + $(T: $tbound1 $(+ $tbound2)*,)* + $($typaram: $bound1 $(+ $bound2)*),* + { + fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_seq(SetPreventDuplicatesVisitor::<$ty, T, TAs>( + PhantomData, + )) + } + } + + impl<'de, T, TAs $(, $typaram)*> DeserializeAs<'de, $ty> for SetLastValueWins + where + TAs: DeserializeAs<'de, T>, + $(T: $tbound1 $(+ $tbound2)*,)* + $($typaram: $bound1 $(+ $bound2)*),* + { + fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> + where + D: Deserializer<'de>, + { + deserializer + .deserialize_seq(SetLastValueWinsVisitor::<$ty, T, TAs>(PhantomData)) + } + } + } +} +foreach_set_create!(set_impl); + +struct MapPreventDuplicatesVisitor(PhantomData<(MAP, K, KAs, V, VAs)>); + +impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapPreventDuplicatesVisitor +where + MAP: PreventDuplicateInsertsMap, + KAs: DeserializeAs<'de, K>, + VAs: DeserializeAs<'de, V>, +{ + type Value = MAP; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a map") + } + + #[inline] + fn visit_map(self, mut access: A) -> Result + where + A: MapAccess<'de>, + { + let mut values = Self::Value::new(access.size_hint()); + + while let Some((key, value)) = + access.next_entry::, DeserializeAsWrap>()? + { + if !values.insert(key.into_inner(), value.into_inner()) { + return Err(DeError::custom("invalid entry: found duplicate key")); + }; + } + + Ok(values) + } +} + +struct MapFirstKeyWinsVisitor(PhantomData<(MAP, K, KAs, V, VAs)>); + +impl<'de, MAP, K, KAs, V, VAs> Visitor<'de> for MapFirstKeyWinsVisitor +where + MAP: DuplicateInsertsFirstWinsMap, + KAs: DeserializeAs<'de, K>, + VAs: DeserializeAs<'de, V>, +{ + type Value = MAP; + + fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { + formatter.write_str("a map") + } + + #[inline] + fn visit_map(self, mut access: A) -> Result + where + A: MapAccess<'de>, + { + let mut values = Self::Value::new(access.size_hint()); + + while let Some((key, value)) = + access.next_entry::, DeserializeAsWrap>()? + { + values.insert(key.into_inner(), value.into_inner()); + } + + Ok(values) + } +} + +#[cfg(feature = "alloc")] +macro_rules! map_impl { + ( + $ty:ident < K $(: $kbound1:ident $(+ $kbound2:ident)*)*, V $(, $typaram:ident : $bound1:ident $(+ $bound2:ident)*)* >, + $with_capacity:expr + ) => { + impl<'de, K, V, KAs, VAs $(, $typaram)*> DeserializeAs<'de, $ty> + for MapPreventDuplicates + where + KAs: DeserializeAs<'de, K>, + VAs: DeserializeAs<'de, V>, + $(K: $kbound1 $(+ $kbound2)*,)* + $($typaram: $bound1 $(+ $bound2)*),* + { + fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(MapPreventDuplicatesVisitor::< + $ty, + K, + KAs, + V, + VAs, + >(PhantomData)) + } + } + + impl<'de, K, V, KAs, VAs $(, $typaram)*> DeserializeAs<'de, $ty> + for MapFirstKeyWins + where + KAs: DeserializeAs<'de, K>, + VAs: DeserializeAs<'de, V>, + $(K: $kbound1 $(+ $kbound2)*,)* + $($typaram: $bound1 $(+ $bound2)*),* + { + fn deserialize_as(deserializer: D) -> Result<$ty, D::Error> + where + D: Deserializer<'de>, + { + deserializer.deserialize_map(MapFirstKeyWinsVisitor::<$ty, K, KAs, V, VAs>( + PhantomData, + )) + } + } + }; +} +foreach_map_create!(map_impl); diff --git a/serde_with/src/de/mod.rs b/serde_with/src/de/mod.rs index 2818eee3..c7a8afe0 100644 --- a/serde_with/src/de/mod.rs +++ b/serde_with/src/de/mod.rs @@ -7,6 +7,7 @@ //! //! [user guide]: crate::guide +mod duplicates; mod impls; use crate::prelude::*; diff --git a/serde_with/src/lib.rs b/serde_with/src/lib.rs index baa49b2c..e0735fbe 100644 --- a/serde_with/src/lib.rs +++ b/serde_with/src/lib.rs @@ -2192,3 +2192,116 @@ pub struct Map(PhantomData<(K, V)>); /// # } /// ``` pub struct Seq(PhantomData); + +/// Ensure no duplicate keys exist in a map. +/// +/// By default serde has a last-value-wins implementation, if duplicate keys for a map exist. +/// Sometimes it is desirable to know when such an event happens, as the first value is overwritten +/// and it can indicate an error in the serialized data. +/// +/// This helper returns an error if two identical keys exist in a map. +/// +/// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. +/// +/// [`HashMap`]: std::collections::HashMap +/// [`BTreeMap`]: std::collections::HashMap +/// +/// # Example +/// +/// ```rust +/// # #[cfg(feature = "macros")] { +/// # use serde::Deserialize; +/// # use std::collections::HashMap; +/// # use serde_with::MapPreventDuplicates; +/// # +/// #[serde_with::serde_as] +/// # #[derive(Debug, Eq, PartialEq)] +/// #[derive(Deserialize)] +/// struct Doc { +/// #[serde_as(as = "MapPreventDuplicates<_, _>")] +/// map: HashMap, +/// } +/// +/// // Maps are serialized normally, +/// let s = r#"{"map": {"1": 1, "2": 2, "3": 3}}"#; +/// let mut v = Doc { +/// map: HashMap::new(), +/// }; +/// v.map.insert(1, 1); +/// v.map.insert(2, 2); +/// v.map.insert(3, 3); +/// assert_eq!(v, serde_json::from_str(s).unwrap()); +/// +/// // but create an error if duplicate keys, like the `1`, exist. +/// let s = r#"{"map": {"1": 1, "2": 2, "1": 3}}"#; +/// let res: Result = serde_json::from_str(s); +/// assert!(res.is_err()); +/// # } +/// ``` +pub struct MapPreventDuplicates(PhantomData<(K, V)>); + +/// Ensure that the last value is taken, if duplicate values exist +/// +/// By default serde has a first-value-wins implementation, if duplicate keys for a set exist. +/// Sometimes the opposite strategy is desired. This helper implements a first-value-wins strategy. +/// +/// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. +/// +/// [`HashSet`]: std::collections::HashSet +/// [`BTreeSet`]: std::collections::HashSet +pub struct MapFirstKeyWins(PhantomData<(K, V)>); + +/// Ensure no duplicate values exist in a set. +/// +/// By default serde has a last-value-wins implementation, if duplicate values for a set exist. +/// Sometimes it is desirable to know when such an event happens, as the first value is overwritten +/// and it can indicate an error in the serialized data. +/// +/// This helper returns an error if two identical values exist in a set. +/// +/// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. +/// +/// [`HashSet`]: std::collections::HashSet +/// [`BTreeSet`]: std::collections::HashSet +/// +/// # Example +/// +/// ```rust +/// # #[cfg(feature = "macros")] { +/// # use std::collections::HashSet; +/// # use serde::Deserialize; +/// # use serde_with::SetPreventDuplicates; +/// # +/// #[serde_with::serde_as] +/// # #[derive(Debug, Eq, PartialEq)] +/// #[derive(Deserialize)] +/// struct Doc { +/// #[serde_as(as = "SetPreventDuplicates<_>")] +/// set: HashSet, +/// } +/// +/// // Sets are serialized normally, +/// let s = r#"{"set": [1, 2, 3, 4]}"#; +/// let v = Doc { +/// set: HashSet::from_iter(vec![1, 2, 3, 4]), +/// }; +/// assert_eq!(v, serde_json::from_str(s).unwrap()); +/// +/// // but create an error if duplicate values, like the `1`, exist. +/// let s = r#"{"set": [1, 2, 3, 4, 1]}"#; +/// let res: Result = serde_json::from_str(s); +/// assert!(res.is_err()); +/// # } +/// ``` +pub struct SetPreventDuplicates(PhantomData); + +/// Ensure that the last value is taken, if duplicate values exist +/// +/// By default serde has a first-value-wins implementation, if duplicate keys for a set exist. +/// Sometimes the opposite strategy is desired. This helper implements a first-value-wins strategy. +/// +/// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. +/// +/// [`HashSet`]: std::collections::HashSet +/// [`BTreeSet`]: std::collections::HashSet +pub struct SetLastValueWins(PhantomData); diff --git a/serde_with/src/rust.rs b/serde_with/src/rust.rs index 10741939..acfca714 100644 --- a/serde_with/src/rust.rs +++ b/serde_with/src/rust.rs @@ -178,13 +178,29 @@ pub mod unwrap_or_skip { /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// +/// # Converting to serde_as +/// +/// The same functionality can be more clearly expressed using the `serde_as` macro and [`SetPreventDuplicates`]. +/// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. +/// +/// ```rust +/// # #[cfg(FALSE)] { +/// #[serde_as] +/// #[derive(Deserialize, Serialize)] +/// struct A { +/// #[serde_as(as = "SetPreventDuplicates<_, _>")] +/// s: HashSet, +/// } +/// # } +/// ``` +/// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet /// /// # Example /// /// ```rust -/// # use std::{collections::HashSet, iter::FromIterator}; +/// # use std::collections::HashSet; /// # use serde::Deserialize; /// # /// # #[derive(Debug, Eq, PartialEq)] @@ -278,6 +294,22 @@ pub mod sets_duplicate_value_is_error { /// /// The implementation supports both the [`HashMap`] and the [`BTreeMap`] from the standard library. /// +/// # Converting to serde_as +/// +/// The same functionality can be more clearly expressed using the `serde_as` macro and [`MapPreventDuplicates`]. +/// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. +/// +/// ```rust +/// # #[cfg(FALSE)] { +/// #[serde_as] +/// #[derive(Deserialize, Serialize)] +/// struct A { +/// #[serde_as(as = "MapPreventDuplicates<_, _>")] +/// s: HashMap, +/// } +/// # } +/// ``` +/// /// [`HashMap`]: std::collections::HashMap /// [`BTreeMap`]: std::collections::HashMap /// @@ -382,6 +414,22 @@ pub mod maps_duplicate_key_is_error { /// /// The implementation supports both the [`HashSet`] and the [`BTreeSet`] from the standard library. /// +/// # Converting to serde_as +/// +/// The same functionality can be more clearly expressed using the `serde_as` macro and [`SetLastValueWins`]. +/// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. +/// +/// ```rust +/// # #[cfg(FALSE)] { +/// #[serde_as] +/// #[derive(Deserialize, Serialize)] +/// struct A { +/// #[serde_as(as = "SetLastValueWins<_, _>")] +/// s: HashSet, +/// } +/// # } +/// ``` +/// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet #[cfg(feature = "alloc")] @@ -454,6 +502,22 @@ pub mod sets_last_value_wins { /// [`HashMap`]: std::collections::HashMap /// [`BTreeMap`]: std::collections::HashMap /// +/// # Converting to serde_as +/// +/// The same functionality can be more clearly expressed using the `serde_as` macro and [`MapFirstKeyWins`]. +/// The `_` is a placeholder which works for any type which implements [`Serialize`]/[`Deserialize`]. +/// +/// ```rust +/// # #[cfg(FALSE)] { +/// #[serde_as] +/// #[derive(Deserialize, Serialize)] +/// struct A { +/// #[serde_as(as = "MapFirstKeyWins<_, _>")] +/// s: HashMap, +/// } +/// # } +/// ``` +/// /// # Example /// /// ```rust diff --git a/serde_with/src/ser/duplicates.rs b/serde_with/src/ser/duplicates.rs new file mode 100644 index 00000000..d70f48df --- /dev/null +++ b/serde_with/src/ser/duplicates.rs @@ -0,0 +1,70 @@ +use super::impls::{foreach_map, foreach_set}; +use crate::{ + prelude::*, MapFirstKeyWins, MapPreventDuplicates, SetLastValueWins, SetPreventDuplicates, +}; +#[cfg(feature = "indexmap_1")] +use indexmap_1::{IndexMap, IndexSet}; + +macro_rules! set_duplicate_handling { + ($tyorig:ident < T $(, $typaram:ident : $bound:ident)* >) => { + impl SerializeAs<$tyorig> for SetPreventDuplicates + where + TAs: SerializeAs, + $($typaram: ?Sized + $bound,)* + { + fn serialize_as(value: &$tyorig, serializer: S) -> Result + where + S: Serializer, + { + <$tyorig>::serialize_as(value, serializer) + } + } + + impl SerializeAs<$tyorig> for SetLastValueWins + where + TAs: SerializeAs, + $($typaram: ?Sized + $bound,)* + { + fn serialize_as(value: &$tyorig, serializer: S) -> Result + where + S: Serializer, + { + <$tyorig>::serialize_as(value, serializer) + } + } + } +} +foreach_set!(set_duplicate_handling); + +macro_rules! map_duplicate_handling { + ($tyorig:ident < K, V $(, $typaram:ident : $bound:ident)* >) => { + impl SerializeAs<$tyorig> for MapPreventDuplicates + where + KAs: SerializeAs, + VAs: SerializeAs, + $($typaram: ?Sized + $bound,)* + { + fn serialize_as(value: &$tyorig, serializer: S) -> Result + where + S: Serializer, + { + <$tyorig>::serialize_as(value, serializer) + } + } + + impl SerializeAs<$tyorig> for MapFirstKeyWins + where + KAs: SerializeAs, + VAs: SerializeAs, + $($typaram: ?Sized + $bound,)* + { + fn serialize_as(value: &$tyorig, serializer: S) -> Result + where + S: Serializer, + { + <$tyorig>::serialize_as(value, serializer) + } + } + } +} +foreach_map!(map_duplicate_handling); diff --git a/serde_with/src/ser/mod.rs b/serde_with/src/ser/mod.rs index e0447be1..6dae862c 100644 --- a/serde_with/src/ser/mod.rs +++ b/serde_with/src/ser/mod.rs @@ -7,6 +7,7 @@ //! //! [user guide]: crate::guide +mod duplicates; mod impls; use crate::prelude::*; From d220adb33b72766c78fbabc9bee640a9b0208746 Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Sat, 4 Mar 2023 17:50:40 +0100 Subject: [PATCH 2/3] Add documentation --- serde_with/CHANGELOG.md | 5 +++ .../src/guide/serde_as_transformations.md | 44 ++++++++++++++++--- 2 files changed, 44 insertions(+), 5 deletions(-) diff --git a/serde_with/CHANGELOG.md b/serde_with/CHANGELOG.md index 1a6e0119..86931ac9 100644 --- a/serde_with/CHANGELOG.md +++ b/serde_with/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] +### Added + +* Add `serde_as` compatible versions for the existing duplicate key and value handling. (#534) + The new types `MapPreventDuplicates`, `MapFirstKeyWins`, `SetPreventDuplicates`, and `SetLastValueWins` can replace the existing modules `maps_duplicate_key_is_error`, `maps_first_key_wins`, `sets_duplicate_value_is_error`, and `sets_last_value_wins`. + ### Fixed * `EnumMap` passes the `human_readable` status of the `Serializer` to more places. diff --git a/serde_with/src/guide/serde_as_transformations.md b/serde_with/src/guide/serde_as_transformations.md index a2a023fd..980c4c57 100644 --- a/serde_with/src/guide/serde_as_transformations.md +++ b/serde_with/src/guide/serde_as_transformations.md @@ -20,11 +20,15 @@ This page lists the transformations implemented in this crate and supported by ` 16. [`NaiveDateTime` like UTC timestamp](#naivedatetime-like-utc-timestamp) 17. [`None` as empty `String`](#none-as-empty-string) 18. [One or many elements into `Vec`](#one-or-many-elements-into-vec) -19. [Pick first successful deserialization](#pick-first-successful-deserialization) -20. [Timestamps as seconds since UNIX epoch](#timestamps-as-seconds-since-unix-epoch) -21. [Value into JSON String](#value-into-json-string) -22. [`Vec` of tuples to `Maps`](#vec-of-tuples-to-maps) -23. [Well-known time formats for `OffsetDateTime`](#well-known-time-formats-for-offsetdatetime) +19. [Overwrite existing set values](#overwrite-existing-set-values) +20. [Pick first successful deserialization](#pick-first-successful-deserialization) +21. [Prefer the first map key when duplicates exist](#prefer-the-first-map-key-when-duplicates-exist) +22. [Prevent duplicate map keys](#prevent-duplicate-map-keys) +23. [Prevent duplicate set values](#prevent-duplicate-set-values) +24. [Timestamps as seconds since UNIX epoch](#timestamps-as-seconds-since-unix-epoch) +25. [Value into JSON String](#value-into-json-string) +26. [`Vec` of tuples to `Maps`](#vec-of-tuples-to-maps) +27. [Well-known time formats for `OffsetDateTime`](#well-known-time-formats-for-offsetdatetime) ## Base64 encode bytes @@ -371,6 +375,13 @@ value: Vec, "value": ["Hello", "World!"], // or lists of many ``` +## Overwrite existing set values + +[`SetLastValueWins`] + +serdes default behavior for sets is to take the first value, when multiple "equal" values are inserted into a set. +This changes the logic, to prefer the last value. + ## Pick first successful deserialization [`PickFirst`] @@ -388,6 +399,25 @@ value: u32, "value": "666", ``` +## Prefer the first map key when duplicates exist + +[`MapFirstKeyWins`] + +serdes default behavior is to take the last key and value combination, if multiple "equal" keys exist. +This changes the logic to instead prefer the first found key-value combination. + +## Prevent duplicate map keys + +[`MapPreventDuplicates`] + +Error during deserialization, when duplicate map keys are detected. + +## Prevent duplicate set values + +[`SetPreventDuplicates`] + +Error during deserialization, when duplicate set values are detected. + ## Timestamps as seconds since UNIX epoch [`TimestampSeconds`] @@ -515,9 +545,13 @@ These conversions are available with the `time_0_3` feature flag. [`FromInto`]: crate::FromInto [`Hex`]: crate::hex::Hex [`JsonString`]: crate::json::JsonString +[`MapFirstKeyWins`]: crate::MapFirstKeyWins +[`MapPreventDuplicates`]: crate::MapPreventDuplicates [`NoneAsEmptyString`]: crate::NoneAsEmptyString [`OneOrMany`]: crate::OneOrMany [`PickFirst`]: crate::PickFirst +[`SetLastValueWins`]: crate::SetLastValueWins +[`SetPreventDuplicates`]: crate::SetPreventDuplicates [`time::Duration`]: time_0_3::Duration [`time::format_description::well_known::Iso8601`]: time_0_3::format_description::well_known::Iso8601 [`time::format_description::well_known::Rfc2822`]: time_0_3::format_description::well_known::Rfc2822 From ad49c0c04058c1a8896c01f43939a98820a1368c Mon Sep 17 00:00:00 2001 From: Jonas Bushart Date: Sat, 4 Mar 2023 17:54:23 +0100 Subject: [PATCH 3/3] Gate the duplicate handling types behind alloc --- serde_with/src/de/mod.rs | 1 + serde_with/src/lib.rs | 4 ++++ serde_with/src/ser/mod.rs | 1 + 3 files changed, 6 insertions(+) diff --git a/serde_with/src/de/mod.rs b/serde_with/src/de/mod.rs index c7a8afe0..3b549e53 100644 --- a/serde_with/src/de/mod.rs +++ b/serde_with/src/de/mod.rs @@ -7,6 +7,7 @@ //! //! [user guide]: crate::guide +#[cfg(feature = "alloc")] mod duplicates; mod impls; diff --git a/serde_with/src/lib.rs b/serde_with/src/lib.rs index e0735fbe..78e00ba2 100644 --- a/serde_with/src/lib.rs +++ b/serde_with/src/lib.rs @@ -2238,6 +2238,7 @@ pub struct Seq(PhantomData); /// assert!(res.is_err()); /// # } /// ``` +#[cfg(feature = "alloc")] pub struct MapPreventDuplicates(PhantomData<(K, V)>); /// Ensure that the last value is taken, if duplicate values exist @@ -2249,6 +2250,7 @@ pub struct MapPreventDuplicates(PhantomData<(K, V)>); /// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet +#[cfg(feature = "alloc")] pub struct MapFirstKeyWins(PhantomData<(K, V)>); /// Ensure no duplicate values exist in a set. @@ -2293,6 +2295,7 @@ pub struct MapFirstKeyWins(PhantomData<(K, V)>); /// assert!(res.is_err()); /// # } /// ``` +#[cfg(feature = "alloc")] pub struct SetPreventDuplicates(PhantomData); /// Ensure that the last value is taken, if duplicate values exist @@ -2304,4 +2307,5 @@ pub struct SetPreventDuplicates(PhantomData); /// /// [`HashSet`]: std::collections::HashSet /// [`BTreeSet`]: std::collections::HashSet +#[cfg(feature = "alloc")] pub struct SetLastValueWins(PhantomData); diff --git a/serde_with/src/ser/mod.rs b/serde_with/src/ser/mod.rs index 6dae862c..82cb9f63 100644 --- a/serde_with/src/ser/mod.rs +++ b/serde_with/src/ser/mod.rs @@ -7,6 +7,7 @@ //! //! [user guide]: crate::guide +#[cfg(feature = "alloc")] mod duplicates; mod impls;