Skip to content

Commit

Permalink
Add FromIntoRef and TryFromIntoRef
Browse files Browse the repository at this point in the history
  • Loading branch information
oblique committed Jul 17, 2023
1 parent e096f0b commit babfaeb
Show file tree
Hide file tree
Showing 5 changed files with 417 additions and 0 deletions.
29 changes: 29 additions & 0 deletions serde_with/src/de/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1719,6 +1719,35 @@ where
}
}

impl<'de, T, U> DeserializeAs<'de, T> for FromIntoRef<U>
where
U: Into<T>,
U: Deserialize<'de>,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
Ok(U::deserialize(deserializer)?.into())
}
}

impl<'de, T, U> DeserializeAs<'de, T> for TryFromIntoRef<U>
where
U: TryInto<T>,
<U as TryInto<T>>::Error: Display,
U: Deserialize<'de>,
{
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
where
D: Deserializer<'de>,
{
U::deserialize(deserializer)?
.try_into()
.map_err(DeError::custom)
}
}

#[cfg(feature = "alloc")]
impl<'de> DeserializeAs<'de, Cow<'de, str>> for BorrowCow {
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, str>, D::Error>
Expand Down
170 changes: 170 additions & 0 deletions serde_with/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1825,6 +1825,87 @@ pub struct PickFirst<T>(PhantomData<T>);
/// ```
pub struct FromInto<T>(PhantomData<T>);

/// Serialize a reference value by converting to/from a proxy type with serde support.
///
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
/// Deserializing works analogue, by deserializing a `T` and then converting into `O`.
///
/// ```rust
/// # #[cfg(FALSE)] {
/// struct S {
/// #[serde_as(as = "FromIntoRef<T>")]
/// value: O,
/// }
/// # }
/// ```
///
/// For serialization `O` needs to be `for<'a> &'a O: Into<T>`.
/// For deserialization the opposite `T: Into<O>` is required.
///
/// **Note**: [`TryFromIntoRef`] is the more generalized version of this adapter which uses the [`TryInto`](std::convert::TryInto) trait instead.
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "macros")] {
/// # use serde::{Deserialize, Serialize};
/// # use serde_json::json;
/// # use serde_with::{serde_as, FromIntoRef};
/// #
/// #[derive(Debug, PartialEq)]
/// struct Rgb {
/// red: u8,
/// green: u8,
/// blue: u8,
/// }
///
/// # /*
/// impl From<(u8, u8, u8)> for Rgb { ... }
/// impl From<Rgb> for (u8, u8, u8) { ... }
/// # */
/// #
/// # impl From<(u8, u8, u8)> for Rgb {
/// # fn from(v: (u8, u8, u8)) -> Self {
/// # Rgb {
/// # red: v.0,
/// # green: v.1,
/// # blue: v.2,
/// # }
/// # }
/// # }
/// #
/// # impl<'a> From<&'a Rgb> for (u8, u8, u8) {
/// # fn from(v: &'a Rgb) -> Self {
/// # (v.red, v.green, v.blue)
/// # }
/// # }
///
/// #[serde_as]
/// # #[derive(Debug, PartialEq)]
/// #[derive(Deserialize, Serialize)]
/// struct Color {
/// #[serde_as(as = "FromIntoRef<(u8, u8, u8)>")]
/// rgb: Rgb,
/// }
/// let color = Color {
/// rgb: Rgb {
/// red: 128,
/// green: 64,
/// blue: 32,
/// },
/// };
///
/// // Define our expected JSON form
/// let j = json!({
/// "rgb": [128, 64, 32],
/// });
/// // Ensure serialization and deserialization produce the expected results
/// assert_eq!(j, serde_json::to_value(&color).unwrap());
/// assert_eq!(color, serde_json::from_value(j).unwrap());
/// # }
/// ```
pub struct FromIntoRef<T>(PhantomData<T>);

/// Serialize value by converting to/from a proxy type with serde support.
///
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
Expand Down Expand Up @@ -1915,6 +1996,95 @@ pub struct FromInto<T>(PhantomData<T>);
/// ```
pub struct TryFromInto<T>(PhantomData<T>);

/// Serialize a reference value by converting to/from a proxy type with serde support.
///
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
/// Deserializing works analogue, by deserializing a `T` and then converting into `O`.
///
/// ```rust
/// # #[cfg(FALSE)] {
/// struct S {
/// #[serde_as(as = "TryFromIntoRef<T>")]
/// value: O,
/// }
/// # }
/// ```
///
/// For serialization `O` needs to be `for<'a> &'a O: TryInto<T>`.
/// For deserialization the opposite `T: TryInto<O>` is required.
/// In both cases the `TryInto::Error` type must implement [`Display`](std::fmt::Display).
///
/// **Note**: [`FromIntoRef`] is the more specialized version of this adapter which uses the infallible [`Into`] trait instead.
/// [`TryFromIntoRef`] is strictly more general and can also be used where [`FromIntoRef`] is applicable.
/// The example shows a use case, when only the deserialization behavior is fallible, but not serializing.
///
/// # Example
///
/// ```rust
/// # #[cfg(feature = "macros")] {
/// # use serde::{Deserialize, Serialize};
/// # use serde_json::json;
/// # use serde_with::{serde_as, TryFromIntoRef};
/// # use std::convert::TryFrom;
/// #
/// #[derive(Debug, PartialEq)]
/// enum Boollike {
/// True,
/// False,
/// }
///
/// # /*
/// impl From<Boollike> for u8 { ... }
/// # */
/// #
/// impl TryFrom<u8> for Boollike {
/// type Error = String;
/// fn try_from(v: u8) -> Result<Self, Self::Error> {
/// match v {
/// 0 => Ok(Boollike::False),
/// 1 => Ok(Boollike::True),
/// _ => Err(format!("Boolikes can only be constructed from 0 or 1 but found {}", v))
/// }
/// }
/// }
/// #
/// # impl<'a> From<&'a Boollike> for u8 {
/// # fn from(v: &'a Boollike) -> Self {
/// # match v {
/// # Boollike::True => 1,
/// # Boollike::False => 0,
/// # }
/// # }
/// # }
///
/// #[serde_as]
/// # #[derive(Debug, PartialEq)]
/// #[derive(Deserialize, Serialize)]
/// struct Data {
/// #[serde_as(as = "TryFromIntoRef<u8>")]
/// b: Boollike,
/// }
/// let data = Data {
/// b: Boollike::True,
/// };
///
/// // Define our expected JSON form
/// let j = json!({
/// "b": 1,
/// });
/// // Ensure serialization and deserialization produce the expected results
/// assert_eq!(j, serde_json::to_value(&data).unwrap());
/// assert_eq!(data, serde_json::from_value(j).unwrap());
///
/// // Numbers besides 0 or 1 should be an error
/// let j = json!({
/// "b": 2,
/// });
/// assert_eq!("Boolikes can only be constructed from 0 or 1 but found 2", serde_json::from_value::<Data>(j).unwrap_err().to_string());
/// # }
/// ```
pub struct TryFromIntoRef<T>(PhantomData<T>);

/// Borrow `Cow` data during deserialization when possible.
///
/// The types `Cow<'a, [u8]>`, `Cow<'a, [u8; N]>`, and `Cow<'a, str>` can borrow from the input data during deserialization.
Expand Down
30 changes: 30 additions & 0 deletions serde_with/src/ser/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,36 @@ where
}
}

impl<T, U> SerializeAs<T> for FromIntoRef<U>
where
for<'a> &'a T: Into<U>,
U: Serialize,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
source.into().serialize(serializer)
}
}

impl<T, U> SerializeAs<T> for TryFromIntoRef<U>
where
for<'a> &'a T: TryInto<U>,
for<'a> <&'a T as TryInto<U>>::Error: Display,
U: Serialize,
{
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
source
.try_into()
.map_err(S::Error::custom)?
.serialize(serializer)
}
}

#[cfg(feature = "alloc")]
impl<'a> SerializeAs<Cow<'a, str>> for BorrowCow {
fn serialize_as<S>(source: &Cow<'a, str>, serializer: S) -> Result<S::Ok, S::Error>
Expand Down
Loading

0 comments on commit babfaeb

Please sign in to comment.