serde_with/
serde_conv.rs

1/// Create new conversion adapters from functions
2///
3/// The macro lets you create a new converter, which is usable for serde's with-attribute and `#[serde_as]`.
4/// Its main use case is to write simple converters for types, which are not serializable.
5/// Another use-case is to change the serialization behavior if the implemented `Serialize`/`Deserialize` trait is insufficient.
6///
7/// The macro takes four arguments:
8///
9/// 1. The name of the converter type.
10///    The type can be prefixed with a visibility modifies like `pub` or `pub(crate)`.
11///    By default, the type is not marked as public (`pub(self)`).
12/// 2. The type `T` we want to extend with custom behavior.
13/// 3. A function or macro taking a `&T` and returning a serializable type.
14/// 4. A function or macro taking a deserializable type and returning a `Result<T, E>`.
15///    The error type `E` must implement [`Display`].
16///
17/// [`Display`]: std::fmt::Display
18///
19/// # Example
20///
21/// In this example, we write custom serialization behavior for a `Rgb` type.
22/// We want to serialize it as a `[u8; 3]`.
23///
24/// ```rust
25/// # #[cfg(feature = "macros")] {
26/// # use serde::{Serialize, Deserialize};
27/// # use serde_with::serde_as;
28///
29/// #[derive(Clone, Copy, Debug, PartialEq)]
30/// struct Rgb {
31///     red: u8,
32///     green: u8,
33///     blue: u8,
34/// }
35///
36/// serde_with::serde_conv!(
37///     #[doc = "Serialize and deserialize `Rgb` as `[u8; 3]`."]
38///     RgbAsArray,
39///     Rgb,
40///     |rgb: &Rgb| [rgb.red, rgb.green, rgb.blue],
41///     |value: [u8; 3]| -> Result<_, std::convert::Infallible> {
42///         Ok(Rgb {
43///             red: value[0],
44///             green: value[1],
45///             blue: value[2],
46///         })
47///     }
48/// );
49///
50/// //////////////////////////////////////////////////
51///
52/// // We define some colors to be used later
53///
54/// let green = Rgb {red: 0, green: 255, blue: 0};
55/// let orange = Rgb {red: 255, green: 128, blue: 0};
56/// let pink = Rgb {red: 255, green: 0, blue: 255};
57///
58/// //////////////////////////////////////////////////
59///
60/// // We can now use the `RgbAsArray` adapter with `serde_as`.
61///
62/// #[serde_as]
63/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
64/// struct Colors {
65///     #[serde_as(as = "RgbAsArray")]
66///     one_rgb: Rgb,
67///     #[serde_as(as = "Vec<RgbAsArray>")]
68///     rgbs_in_vec: Vec<Rgb>,
69/// }
70///
71/// let data = Colors {
72///     one_rgb: orange,
73///     rgbs_in_vec: vec![green, pink],
74/// };
75/// let json = serde_json::json!({
76///     "one_rgb": [255, 128, 0],
77///     "rgbs_in_vec": [
78///         [0, 255, 0],
79///         [255, 0, 255]
80///     ]
81/// });
82///
83/// assert_eq!(json, serde_json::to_value(&data).unwrap());
84/// assert_eq!(data, serde_json::from_value(json).unwrap());
85///
86/// //////////////////////////////////////////////////
87///
88/// // The types generated by `serde_conv` is also compatible with serde's with attribute
89///
90/// #[derive(Debug, PartialEq, Serialize, Deserialize)]
91/// struct ColorsWith {
92///     #[serde(with = "RgbAsArray")]
93///     rgb_with: Rgb,
94/// }
95///
96/// let data = ColorsWith {
97///     rgb_with: pink,
98/// };
99/// let json = serde_json::json!({
100///     "rgb_with": [255, 0, 255]
101/// });
102///
103/// assert_eq!(json, serde_json::to_value(&data).unwrap());
104/// assert_eq!(data, serde_json::from_value(json).unwrap());
105/// # }
106/// ```
107#[macro_export]
108macro_rules! serde_conv {
109    ($(#[$attr:meta])* $m:ident, $t:ty, $ser:expr, $de:expr) => {$crate::serde_conv!($(#[$attr])* pub(self) $m, $t, $ser, $de);};
110    ($(#[$attr:meta])* $vis:vis $m:ident, $t:ty, $ser:expr, $de:expr) => {
111        #[allow(non_camel_case_types)]
112        $(#[$attr])*
113        $vis struct $m;
114
115        // Prevent clippy lints triggering because of the template here
116        // https://github.com/jonasbb/serde_with/pull/320
117        // https://github.com/jonasbb/serde_with/pull/729
118        #[allow(clippy::all)]
119        const _:() = {
120            impl $m {
121                $vis fn serialize<S>(x: &$t, serializer: S) -> $crate::__private__::Result<S::Ok, S::Error>
122                where
123                    S: $crate::__private__::Serializer,
124                {
125                    let y = $ser(x);
126                    $crate::__private__::Serialize::serialize(&y, serializer)
127                }
128
129                $vis fn deserialize<'de, D>(deserializer: D) -> $crate::__private__::Result<$t, D::Error>
130                where
131                    D: $crate::__private__::Deserializer<'de>,
132                {
133                    let y = $crate::__private__::Deserialize::deserialize(deserializer)?;
134                    $de(y).map_err($crate::__private__::DeError::custom)
135                }
136            }
137
138            impl $crate::SerializeAs<$t> for $m {
139                fn serialize_as<S>(x: &$t, serializer: S) -> $crate::__private__::Result<S::Ok, S::Error>
140                where
141                    S: $crate::__private__::Serializer,
142                {
143                    Self::serialize(x, serializer)
144                }
145            }
146
147            impl<'de> $crate::DeserializeAs<'de, $t> for $m {
148                fn deserialize_as<D>(deserializer: D) -> $crate::__private__::Result<$t, D::Error>
149                where
150                    D: $crate::__private__::Deserializer<'de>,
151                {
152                    Self::deserialize(deserializer)
153                }
154            }
155        };
156    };
157}