diff --git a/Cargo.toml b/Cargo.toml index 8af6bb5..fc5c53b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,10 +8,12 @@ repository = "https://github.com/bevyengine/atomicow" license = "MIT OR Apache-2.0" [dependencies] +serde = { version = "1", default-features = false, features = ["alloc"], optional = true } [features] default = ["std"] std = [] +serde = ["dep:serde"] [target.'cfg(not(target_has_atomic = "ptr"))'.dependencies] portable-atomic = { version = "1", default-features = false } diff --git a/RELEASES.md b/RELEASES.md index c6fd8db..73b47b8 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,9 @@ # `atomicow` Release Notes +## Unreleased + +Added the `serde` feature, allowing the serialization and deserialization of `CowArc` types when the contained type implements the appropriate trait. + ## Version 1.2 The `T` in `Arc` can now be `!Sized` when converting into an `CowArc<'static, T>` diff --git a/src/lib.rs b/src/lib.rs index 60fb1fd..32bea74 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -182,6 +182,128 @@ impl Display for CowArc<'_, T> { } } +#[cfg(feature = "serde")] +impl serde::Serialize for CowArc<'_, T> { + #[inline] + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + serde::Serialize::serialize(self.deref(), serializer) + } +} + +#[cfg(feature = "serde")] +impl<'de, T> serde::Deserialize<'de> for CowArc<'static, T> +where + T: serde::Deserialize<'de>, +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + ::deserialize(deserializer).map(CowArc::new_owned) + } +} + +#[cfg(feature = "serde")] +impl<'de, T> serde::Deserialize<'de> for CowArc<'static, [T]> +where + T: serde::Deserialize<'de> + 'static, +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct SliceVisitor(core::marker::PhantomData); + + impl<'de, T> serde::de::Visitor<'de> for SliceVisitor + where + T: serde::Deserialize<'de> + 'static, + { + type Value = CowArc<'static, [T]>; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a sequence") + } + + fn visit_seq(self, mut sequence: A) -> Result + where + A: serde::de::SeqAccess<'de>, + { + let mut values = alloc::vec::Vec::new(); + + while let Some(value) = sequence.next_element()? { + values.push(value); + } + + Ok(CowArc::Owned(Arc::from(values))) + } + } + + deserializer.deserialize_seq(SliceVisitor(core::marker::PhantomData)) + } +} + +#[cfg(feature = "serde")] +impl<'de> serde::Deserialize<'de> for CowArc<'static, str> { + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct StrVisitor; + + impl<'de> serde::de::Visitor<'de> for StrVisitor { + type Value = CowArc<'static, str>; + + fn expecting(&self, formatter: &mut core::fmt::Formatter) -> core::fmt::Result { + formatter.write_str("a string") + } + + fn visit_borrowed_str(self, value: &'de str) -> Result + where + E: serde::de::Error, + { + Ok(CowArc::Owned(Arc::from(value))) + } + + fn visit_str(self, value: &str) -> Result + where + E: serde::de::Error, + { + Ok(CowArc::Owned(Arc::from(value))) + } + + fn visit_bytes(self, value: &[u8]) -> Result + where + E: serde::de::Error, + { + let value = core::str::from_utf8(value).map_err(E::custom)?; + self.visit_str(value) + } + + fn visit_borrowed_bytes(self, value: &'de [u8]) -> Result + where + E: serde::de::Error, + { + self.visit_bytes(value) + } + + fn visit_string(self, value: String) -> Result + where + E: serde::de::Error, + { + Ok(CowArc::Owned(Arc::from(value))) + } + } + + deserializer.deserialize_str(StrVisitor) + } +} + impl PartialOrd for CowArc<'_, T> { #[inline] fn partial_cmp(&self, other: &Self) -> Option {