diff --git a/src/lib.rs b/src/lib.rs index 9768370..62a503a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -30,6 +30,9 @@ //! //! assert_eq!(size_of::>(), 4 * WORD); //! assert_eq!(size_of::>(), 3 * WORD); +//! +//! // Lean variant is two words on 64-bit architecture +//! #[cfg(target_pointer_width = "64")] //! assert_eq!(size_of::>(), 2 * WORD); //! ``` #![cfg_attr(feature = "const_fn", feature(const_fn_trait_bound))] diff --git a/src/serde.rs b/src/serde.rs index bbd0a3c..a85fbc6 100644 --- a/src/serde.rs +++ b/src/serde.rs @@ -1,4 +1,7 @@ -use serde::de::{Deserialize, Deserializer}; +use alloc::{borrow::ToOwned, string::String}; +use core::{fmt, marker::PhantomData}; + +use serde::de::{self, Deserialize, Deserializer, Visitor}; use serde::ser::{Serialize, Serializer}; use crate::generic::Cow; @@ -17,18 +20,69 @@ where } } -impl<'de, 'a, T: ?Sized, U> Deserialize<'de> for Cow<'a, T, U> +struct CowVisitor<'de, 'a, T: Beef + ?Sized, U: Capacity>( + PhantomData (&'de T, Cow<'a, T, U>)>, +); + +impl<'de, 'a, U> Visitor<'de> for CowVisitor<'de, 'a, str, U> where - T: Beef, + 'de: 'a, + U: Capacity, +{ + type Value = Cow<'a, str, U>; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a string") + } + + fn visit_borrowed_str(self, value: &'de str) -> Result + where + E: de::Error, + { + Ok(Cow::borrowed(value)) + } + + fn visit_str(self, value: &str) -> Result + where + E: de::Error, + { + Ok(Cow::owned(value.to_owned())) + } + + fn visit_string(self, value: String) -> Result + where + E: de::Error, + { + Ok(Cow::owned(value)) + } +} + +impl<'de, 'a, U> Deserialize<'de> for Cow<'a, str, U> +where + 'de: 'a, U: Capacity, - T::Owned: Deserialize<'de>, { #[inline] fn deserialize(deserializer: D) -> Result where D: Deserializer<'de>, { - T::Owned::deserialize(deserializer).map(Cow::owned) + deserializer.deserialize_str(CowVisitor::<'de, 'a, str, U>(PhantomData)) + } +} + +impl<'de, 'a, T, U> Deserialize<'de> for Cow<'a, [T], U> +where + [T]: Beef, + U: Capacity, + <[T] as ToOwned>::Owned: Deserialize<'de>, +{ + #[inline] + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + <[T] as ToOwned>::Owned::deserialize(deserializer).map(Cow::owned) } } @@ -60,4 +114,36 @@ mod tests { assert_eq!(json, out); } + + #[test] + fn wide_cow_direct() { + use crate::Cow; + + let json = r#""foo""#; + let cow: Cow = serde_json::from_str(json).unwrap(); + + assert_eq!(cow, "foo"); + + assert!(cow.is_borrowed()); + + let json = r#""\tfoo""#; + let cow: Cow = serde_json::from_str(json).unwrap(); + + assert_eq!(cow, "\tfoo"); + + assert!(cow.is_owned()); + } + + #[test] + fn wide_cow_direct_bytes() { + use crate::Cow; + + let json = r#"[102, 111, 111]"#; + let cow: Cow<[u8]> = serde_json::from_str(json).unwrap(); + + assert_eq!(cow, &b"foo"[..]); + + // We need to stay generic over `[T]`, so no specialization for byte slices + assert!(cow.is_owned()); + } }