Skip to content

Commit babfaeb

Browse files
committed
Add FromIntoRef and TryFromIntoRef
1 parent e096f0b commit babfaeb

File tree

5 files changed

+417
-0
lines changed

5 files changed

+417
-0
lines changed

serde_with/src/de/impls.rs

+29
Original file line numberDiff line numberDiff line change
@@ -1719,6 +1719,35 @@ where
17191719
}
17201720
}
17211721

1722+
impl<'de, T, U> DeserializeAs<'de, T> for FromIntoRef<U>
1723+
where
1724+
U: Into<T>,
1725+
U: Deserialize<'de>,
1726+
{
1727+
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
1728+
where
1729+
D: Deserializer<'de>,
1730+
{
1731+
Ok(U::deserialize(deserializer)?.into())
1732+
}
1733+
}
1734+
1735+
impl<'de, T, U> DeserializeAs<'de, T> for TryFromIntoRef<U>
1736+
where
1737+
U: TryInto<T>,
1738+
<U as TryInto<T>>::Error: Display,
1739+
U: Deserialize<'de>,
1740+
{
1741+
fn deserialize_as<D>(deserializer: D) -> Result<T, D::Error>
1742+
where
1743+
D: Deserializer<'de>,
1744+
{
1745+
U::deserialize(deserializer)?
1746+
.try_into()
1747+
.map_err(DeError::custom)
1748+
}
1749+
}
1750+
17221751
#[cfg(feature = "alloc")]
17231752
impl<'de> DeserializeAs<'de, Cow<'de, str>> for BorrowCow {
17241753
fn deserialize_as<D>(deserializer: D) -> Result<Cow<'de, str>, D::Error>

serde_with/src/lib.rs

+170
Original file line numberDiff line numberDiff line change
@@ -1825,6 +1825,87 @@ pub struct PickFirst<T>(PhantomData<T>);
18251825
/// ```
18261826
pub struct FromInto<T>(PhantomData<T>);
18271827

1828+
/// Serialize a reference value by converting to/from a proxy type with serde support.
1829+
///
1830+
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
1831+
/// Deserializing works analogue, by deserializing a `T` and then converting into `O`.
1832+
///
1833+
/// ```rust
1834+
/// # #[cfg(FALSE)] {
1835+
/// struct S {
1836+
/// #[serde_as(as = "FromIntoRef<T>")]
1837+
/// value: O,
1838+
/// }
1839+
/// # }
1840+
/// ```
1841+
///
1842+
/// For serialization `O` needs to be `for<'a> &'a O: Into<T>`.
1843+
/// For deserialization the opposite `T: Into<O>` is required.
1844+
///
1845+
/// **Note**: [`TryFromIntoRef`] is the more generalized version of this adapter which uses the [`TryInto`](std::convert::TryInto) trait instead.
1846+
///
1847+
/// # Example
1848+
///
1849+
/// ```rust
1850+
/// # #[cfg(feature = "macros")] {
1851+
/// # use serde::{Deserialize, Serialize};
1852+
/// # use serde_json::json;
1853+
/// # use serde_with::{serde_as, FromIntoRef};
1854+
/// #
1855+
/// #[derive(Debug, PartialEq)]
1856+
/// struct Rgb {
1857+
/// red: u8,
1858+
/// green: u8,
1859+
/// blue: u8,
1860+
/// }
1861+
///
1862+
/// # /*
1863+
/// impl From<(u8, u8, u8)> for Rgb { ... }
1864+
/// impl From<Rgb> for (u8, u8, u8) { ... }
1865+
/// # */
1866+
/// #
1867+
/// # impl From<(u8, u8, u8)> for Rgb {
1868+
/// # fn from(v: (u8, u8, u8)) -> Self {
1869+
/// # Rgb {
1870+
/// # red: v.0,
1871+
/// # green: v.1,
1872+
/// # blue: v.2,
1873+
/// # }
1874+
/// # }
1875+
/// # }
1876+
/// #
1877+
/// # impl<'a> From<&'a Rgb> for (u8, u8, u8) {
1878+
/// # fn from(v: &'a Rgb) -> Self {
1879+
/// # (v.red, v.green, v.blue)
1880+
/// # }
1881+
/// # }
1882+
///
1883+
/// #[serde_as]
1884+
/// # #[derive(Debug, PartialEq)]
1885+
/// #[derive(Deserialize, Serialize)]
1886+
/// struct Color {
1887+
/// #[serde_as(as = "FromIntoRef<(u8, u8, u8)>")]
1888+
/// rgb: Rgb,
1889+
/// }
1890+
/// let color = Color {
1891+
/// rgb: Rgb {
1892+
/// red: 128,
1893+
/// green: 64,
1894+
/// blue: 32,
1895+
/// },
1896+
/// };
1897+
///
1898+
/// // Define our expected JSON form
1899+
/// let j = json!({
1900+
/// "rgb": [128, 64, 32],
1901+
/// });
1902+
/// // Ensure serialization and deserialization produce the expected results
1903+
/// assert_eq!(j, serde_json::to_value(&color).unwrap());
1904+
/// assert_eq!(color, serde_json::from_value(j).unwrap());
1905+
/// # }
1906+
/// ```
1907+
pub struct FromIntoRef<T>(PhantomData<T>);
1908+
18281909
/// Serialize value by converting to/from a proxy type with serde support.
18291910
///
18301911
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
@@ -1915,6 +1996,95 @@ pub struct FromInto<T>(PhantomData<T>);
19151996
/// ```
19161997
pub struct TryFromInto<T>(PhantomData<T>);
19171998

1999+
/// Serialize a reference value by converting to/from a proxy type with serde support.
2000+
///
2001+
/// This adapter serializes a type `O` by converting it into a second type `T` and serializing `T`.
2002+
/// Deserializing works analogue, by deserializing a `T` and then converting into `O`.
2003+
///
2004+
/// ```rust
2005+
/// # #[cfg(FALSE)] {
2006+
/// struct S {
2007+
/// #[serde_as(as = "TryFromIntoRef<T>")]
2008+
/// value: O,
2009+
/// }
2010+
/// # }
2011+
/// ```
2012+
///
2013+
/// For serialization `O` needs to be `for<'a> &'a O: TryInto<T>`.
2014+
/// For deserialization the opposite `T: TryInto<O>` is required.
2015+
/// In both cases the `TryInto::Error` type must implement [`Display`](std::fmt::Display).
2016+
///
2017+
/// **Note**: [`FromIntoRef`] is the more specialized version of this adapter which uses the infallible [`Into`] trait instead.
2018+
/// [`TryFromIntoRef`] is strictly more general and can also be used where [`FromIntoRef`] is applicable.
2019+
/// The example shows a use case, when only the deserialization behavior is fallible, but not serializing.
2020+
///
2021+
/// # Example
2022+
///
2023+
/// ```rust
2024+
/// # #[cfg(feature = "macros")] {
2025+
/// # use serde::{Deserialize, Serialize};
2026+
/// # use serde_json::json;
2027+
/// # use serde_with::{serde_as, TryFromIntoRef};
2028+
/// # use std::convert::TryFrom;
2029+
/// #
2030+
/// #[derive(Debug, PartialEq)]
2031+
/// enum Boollike {
2032+
/// True,
2033+
/// False,
2034+
/// }
2035+
///
2036+
/// # /*
2037+
/// impl From<Boollike> for u8 { ... }
2038+
/// # */
2039+
/// #
2040+
/// impl TryFrom<u8> for Boollike {
2041+
/// type Error = String;
2042+
/// fn try_from(v: u8) -> Result<Self, Self::Error> {
2043+
/// match v {
2044+
/// 0 => Ok(Boollike::False),
2045+
/// 1 => Ok(Boollike::True),
2046+
/// _ => Err(format!("Boolikes can only be constructed from 0 or 1 but found {}", v))
2047+
/// }
2048+
/// }
2049+
/// }
2050+
/// #
2051+
/// # impl<'a> From<&'a Boollike> for u8 {
2052+
/// # fn from(v: &'a Boollike) -> Self {
2053+
/// # match v {
2054+
/// # Boollike::True => 1,
2055+
/// # Boollike::False => 0,
2056+
/// # }
2057+
/// # }
2058+
/// # }
2059+
///
2060+
/// #[serde_as]
2061+
/// # #[derive(Debug, PartialEq)]
2062+
/// #[derive(Deserialize, Serialize)]
2063+
/// struct Data {
2064+
/// #[serde_as(as = "TryFromIntoRef<u8>")]
2065+
/// b: Boollike,
2066+
/// }
2067+
/// let data = Data {
2068+
/// b: Boollike::True,
2069+
/// };
2070+
///
2071+
/// // Define our expected JSON form
2072+
/// let j = json!({
2073+
/// "b": 1,
2074+
/// });
2075+
/// // Ensure serialization and deserialization produce the expected results
2076+
/// assert_eq!(j, serde_json::to_value(&data).unwrap());
2077+
/// assert_eq!(data, serde_json::from_value(j).unwrap());
2078+
///
2079+
/// // Numbers besides 0 or 1 should be an error
2080+
/// let j = json!({
2081+
/// "b": 2,
2082+
/// });
2083+
/// assert_eq!("Boolikes can only be constructed from 0 or 1 but found 2", serde_json::from_value::<Data>(j).unwrap_err().to_string());
2084+
/// # }
2085+
/// ```
2086+
pub struct TryFromIntoRef<T>(PhantomData<T>);
2087+
19182088
/// Borrow `Cow` data during deserialization when possible.
19192089
///
19202090
/// The types `Cow<'a, [u8]>`, `Cow<'a, [u8; N]>`, and `Cow<'a, str>` can borrow from the input data during deserialization.

serde_with/src/ser/impls.rs

+30
Original file line numberDiff line numberDiff line change
@@ -877,6 +877,36 @@ where
877877
}
878878
}
879879

880+
impl<T, U> SerializeAs<T> for FromIntoRef<U>
881+
where
882+
for<'a> &'a T: Into<U>,
883+
U: Serialize,
884+
{
885+
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
886+
where
887+
S: Serializer,
888+
{
889+
source.into().serialize(serializer)
890+
}
891+
}
892+
893+
impl<T, U> SerializeAs<T> for TryFromIntoRef<U>
894+
where
895+
for<'a> &'a T: TryInto<U>,
896+
for<'a> <&'a T as TryInto<U>>::Error: Display,
897+
U: Serialize,
898+
{
899+
fn serialize_as<S>(source: &T, serializer: S) -> Result<S::Ok, S::Error>
900+
where
901+
S: Serializer,
902+
{
903+
source
904+
.try_into()
905+
.map_err(S::Error::custom)?
906+
.serialize(serializer)
907+
}
908+
}
909+
880910
#[cfg(feature = "alloc")]
881911
impl<'a> SerializeAs<Cow<'a, str>> for BorrowCow {
882912
fn serialize_as<S>(source: &Cow<'a, str>, serializer: S) -> Result<S::Ok, S::Error>

0 commit comments

Comments
 (0)