serdapt/lib.rs
1// Copyright (c) 2024 Stephane Raux. Distributed under the 0BSD license.
2
3//! # Overview
4//! - [📦 crates.io](https://crates.io/crates/serdapt)
5//! - [📖 Documentation](https://docs.rs/serdapt)
6//! - [âš– 0BSD license](https://spdx.org/licenses/0BSD.html)
7//!
8//! Tools to build composable adapters for `#[serde(with = ...)]`.
9//!
10//! `serde` allows customizing how fields are serialized when deriving `Serialize` and `Deserialize`
11//! thanks to the `#[serde(with = "path")]` attribute. With such an attribute, `path::serialize`
12//! and `path::deserialize` are the functions used for serialization. By using a type for `path`,
13//! composable serialization adapters can be defined, e.g. to customize how items in a container
14//! are serialized.
15//!
16//! These adapters can also simplify implementing `Serialize` and `Deserialize`.
17//!
18//! # Apply adapter
19//! An adapter is applied by specifying the adapter path in `#[serde(with = "...")]`. The path
20//! needs to be suitable as a prefix for functions, i.e. `path::serialize` and `path::deserialize`.
21//! This means the turbofish is needed for generic adapters, e.g. `Outer::<Inner>`.
22//!
23//! ## Example
24//! ```
25//! use serde::{Deserialize, Serialize};
26//!
27//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
28//! struct Foo {
29//! #[serde(with = "serdapt::Seq::<serdapt::Str>")]
30//! xs: Vec<i32>,
31//! }
32//!
33//! let foo = Foo { xs: vec![3, 4] };
34//! let v = serde_json::to_value(&foo).unwrap();
35//! assert_eq!(v, serde_json::json!({ "xs": ["3", "4"] }));
36//! assert_eq!(serde_json::from_value::<Foo>(v).unwrap(), foo);
37//! ```
38//!
39//! # Define serialization adapter
40//! 1. Define a type to represent the new adapter.
41//! 1. Implement [`SerializeWith`] and [`DeserializeWith`] for this type. This allows adapter
42//! composability.
43//! 1. Define `serialize` and `deserialize` inherent functions for this type, delegating to
44//! [`SerializeWith`] and [`DeserializeWith`] respectively. These are the functions the
45//! serde-generated code calls.
46//!
47//! ## Simple adapter example
48//! ```
49//! use serdapt::{DeserializeWith, SerializeWith};
50//! use serde::{Deserialize, Deserializer, Serialize, Serializer};
51//! use serde_json::json;
52//!
53//! #[derive(Deserialize, Serialize)]
54//! struct Point {
55//! x: i32,
56//! y: i32,
57//! }
58//!
59//! struct Coords;
60//!
61//! impl Coords {
62//! fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
63//! where
64//! T: ?Sized,
65//! S: Serializer,
66//! Self: SerializeWith<T>,
67//! {
68//! Self::serialize_with(value, serializer)
69//! }
70//!
71//! fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
72//! where
73//! D: Deserializer<'de>,
74//! Self: DeserializeWith<'de, T>,
75//! {
76//! Self::deserialize_with(deserializer)
77//! }
78//! }
79//!
80//! impl SerializeWith<(i32, i32)> for Coords {
81//! fn serialize_with<S>(&(x, y): &(i32, i32), serializer: S) -> Result<S::Ok, S::Error>
82//! where
83//! S: Serializer,
84//! {
85//! Serialize::serialize(&Point { x, y }, serializer)
86//! }
87//! }
88//!
89//! impl<'de> DeserializeWith<'de, (i32, i32)> for Coords {
90//! fn deserialize_with<D>(deserializer: D) -> Result<(i32, i32), D::Error>
91//! where
92//! D: Deserializer<'de>,
93//! {
94//! let Point { x, y } = Deserialize::deserialize(deserializer)?;
95//! Ok((x, y))
96//! }
97//! }
98//!
99//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
100//! struct Shape(#[serde(with = "serdapt::Seq::<Coords>")] Vec<(i32, i32)>);
101//!
102//! let original = Shape(vec![(1, 2), (3, 4)]);
103//! let serialized = serde_json::to_value(&original).unwrap();
104//! assert_eq!(serialized, json!([{ "x": 1, "y": 2 }, { "x": 3, "y": 4 }]));
105//! let deserialized = serde_json::from_value::<Shape>(serialized).unwrap();
106//! assert_eq!(deserialized, original);
107//! ```
108//!
109//! ## Generic adapter example
110//! ```
111//! use core::marker::PhantomData;
112//! use serdapt::{DeserializeWith, SerializeWith, WithEncoding};
113//! use serde::{Deserialize, Deserializer, Serialize, Serializer};
114//! use serde_json::json;
115//!
116//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
117//! struct Point<T> {
118//! x: T,
119//! y: T,
120//! }
121//!
122//! struct Coords<F>(PhantomData<F>);
123//!
124//! impl<F> Coords<F> {
125//! fn serialize<T, S>(value: &T, serializer: S) -> Result<S::Ok, S::Error>
126//! where
127//! T: ?Sized,
128//! S: Serializer,
129//! Self: SerializeWith<T>,
130//! {
131//! Self::serialize_with(value, serializer)
132//! }
133//!
134//! fn deserialize<'de, T, D>(deserializer: D) -> Result<T, D::Error>
135//! where
136//! D: Deserializer<'de>,
137//! Self: DeserializeWith<'de, T>,
138//! {
139//! Self::deserialize_with(deserializer)
140//! }
141//! }
142//!
143//! impl<F, T> SerializeWith<Point<T>> for Coords<F>
144//! where
145//! F: SerializeWith<T>,
146//! {
147//! fn serialize_with<S>(Point { x, y }: &Point<T>, serializer: S) -> Result<S::Ok, S::Error>
148//! where
149//! S: Serializer,
150//! {
151//! let p: Point<WithEncoding<&F, &T>> = Point {
152//! x: x.into(),
153//! y: y.into()
154//! };
155//! Serialize::serialize(&p, serializer)
156//! }
157//! }
158//!
159//! impl<'de, F, T> DeserializeWith<'de, Point<T>> for Coords<F>
160//! where
161//! F: DeserializeWith<'de, T>,
162//! {
163//! fn deserialize_with<D>(deserializer: D) -> Result<Point<T>, D::Error>
164//! where
165//! D: Deserializer<'de>,
166//! {
167//! let p: Point<WithEncoding<F, T>> = Deserialize::deserialize(deserializer)?;
168//! Ok(Point {
169//! x: p.x.into_inner(),
170//! y: p.y.into_inner(),
171//! })
172//! }
173//! }
174//!
175//! #[derive(Debug, Deserialize, PartialEq, Serialize)]
176//! struct Shape(
177//! #[serde(with = "serdapt::Seq::<Coords<serdapt::Str>>")] Vec<Point<i32>>,
178//! );
179//!
180//! let original = Shape(vec![Point { x: 1, y: 2 }, Point { x: 3, y: 4 }]);
181//! let serialized = serde_json::to_value(&original).unwrap();
182//! assert_eq!(serialized, json!([{ "x": "1", "y": "2" }, { "x": "3", "y": "4" }]));
183//! let deserialized = serde_json::from_value::<Shape>(serialized).unwrap();
184//! assert_eq!(deserialized, original);
185//! ```
186//!
187//! # Related project
188//! [`serde_with`](https://crates.io/crates/serde_with) allows the same composability with the help
189//! of an additional proc-macro, though it is also possible to use `#[serde(with = ...)]` directly.
190//!
191//! Some key differences are:
192//! - `serdapt` is simpler and does not need any additional proc-macro, giving up on any ergonomics
193//! such a macro provides.
194//! - It avoids a macro ordering issue that can lead to generated serialization code not using the
195//! requested adapter despite a sucessful compilation.
196//! - It works seamlessly with conditional compilation.
197//! - It is limited to supporting types in the standard library, with support for third-party types
198//! delegated to other crates, which solves dependency issues.
199//!
200//! # Contribute
201//! All contributions shall be licensed under the [0BSD license](https://spdx.org/licenses/0BSD.html).
202
203#![deny(missing_docs)]
204#![cfg_attr(not(feature = "std"), no_std)]
205
206#[cfg(feature = "alloc")]
207extern crate alloc;
208
209mod add_ref;
210mod array;
211mod bytes;
212mod cell;
213mod codec;
214mod convert;
215#[cfg(feature = "alloc")]
216mod cow;
217mod fold;
218mod from;
219mod human;
220mod identity;
221mod into;
222mod map;
223mod map_as_seq;
224#[cfg(feature = "std")]
225mod mutex;
226mod option;
227mod ptr;
228mod range;
229mod result;
230mod reverse;
231#[cfg(feature = "std")]
232mod rwlock;
233mod seq_as_map;
234mod sequence;
235mod str;
236mod try_from;
237mod try_into;
238mod wrapping;
239
240pub use add_ref::AddRef;
241pub use array::Array;
242#[cfg(feature = "alloc")]
243pub use bytes::ByteVec;
244pub use bytes::Bytes;
245pub use cell::Cell;
246pub use codec::Codec;
247pub use convert::{Convert, RefConvert, RefTryConvert, TryConvert};
248#[cfg(feature = "alloc")]
249pub use cow::Cow;
250pub use fold::Fold;
251pub use from::From;
252pub use human::HumanOr;
253pub use identity::Id;
254pub use into::Into;
255pub use map::Map;
256pub use map_as_seq::MapAsSeq;
257#[cfg(feature = "std")]
258pub use mutex::Mutex;
259pub use option::Option;
260pub use ptr::Ptr;
261pub use range::Range;
262pub use result::Result;
263pub use reverse::Reverse;
264#[cfg(feature = "std")]
265pub use rwlock::RwLock;
266pub use seq_as_map::SeqAsMap;
267pub use sequence::Seq;
268pub use str::Str;
269pub use try_from::TryFrom;
270pub use try_into::TryInto;
271pub use wrapping::Wrapping;
272
273use core::marker::PhantomData;
274use serde::{Deserialize, Deserializer, Serialize, Serializer};
275
276/// Trait for types that can be used as serialization adapters with `#[serde(with = ...)]`
277///
278/// This is the foundation to build composable serialization adapters.
279pub trait SerializeWith<T: ?Sized> {
280 /// Serializes `value` using `serializer`
281 fn serialize_with<S: Serializer>(
282 value: &T,
283 serializer: S,
284 ) -> core::result::Result<S::Ok, S::Error>;
285}
286
287/// Trait for types that can be used as deserialization adapters with `#[serde(with = ...)]`
288///
289/// This is the foundation to build composable deserialization adapters.
290pub trait DeserializeWith<'de, T> {
291 /// Deserializes a value using `deserializer`
292 fn deserialize_with<D>(deserializer: D) -> core::result::Result<T, D::Error>
293 where
294 D: Deserializer<'de>;
295}
296
297impl<F, T> SerializeWith<&T> for &F
298where
299 F: SerializeWith<T>,
300 T: ?Sized,
301{
302 fn serialize_with<S: Serializer>(
303 value: &&T,
304 serializer: S,
305 ) -> core::result::Result<S::Ok, S::Error> {
306 F::serialize_with(*value, serializer)
307 }
308}
309
310impl<F, T> SerializeWith<&mut T> for &F
311where
312 F: SerializeWith<T>,
313 T: ?Sized,
314{
315 fn serialize_with<S: Serializer>(
316 value: &&mut T,
317 serializer: S,
318 ) -> core::result::Result<S::Ok, S::Error> {
319 F::serialize_with(*value, serializer)
320 }
321}
322
323/// Type bundling a value and how to (de)serialize it
324///
325/// It allows a value to be (de)serialized with `serde` in a custom manner.
326pub struct WithEncoding<F, T: ?Sized> {
327 encoding: PhantomData<F>,
328 value: T,
329}
330
331impl<F, T> WithEncoding<F, T> {
332 /// Returns inner value
333 pub fn into_inner(self) -> T {
334 self.value
335 }
336}
337
338impl<F, T> core::convert::From<T> for WithEncoding<F, T> {
339 fn from(value: T) -> Self {
340 Self {
341 encoding: PhantomData,
342 value,
343 }
344 }
345}
346
347impl<F, T> Serialize for WithEncoding<F, T>
348where
349 F: SerializeWith<T>,
350 T: ?Sized,
351{
352 fn serialize<S>(&self, serializer: S) -> core::result::Result<S::Ok, S::Error>
353 where
354 S: Serializer,
355 {
356 F::serialize_with(&self.value, serializer)
357 }
358}
359
360impl<'de, F, T> Deserialize<'de> for WithEncoding<F, T>
361where
362 F: DeserializeWith<'de, T>,
363{
364 fn deserialize<D>(deserializer: D) -> core::result::Result<Self, D::Error>
365 where
366 D: Deserializer<'de>,
367 {
368 F::deserialize_with(deserializer).map(|x| x.into())
369 }
370}
371
372macro_rules! impl_tuple {
373 ($($types:ident $adapters:ident $xs:ident,)*) => {
374 impl<$($types, $adapters),*> SerializeWith<($($types,)*)> for ($($adapters,)*)
375 where
376 $($adapters: SerializeWith<$types>,)*
377 {
378 fn serialize_with<S: Serializer>(
379 value: &($($types,)*),
380 serializer: S,
381 ) -> core::result::Result<S::Ok, S::Error> {
382 let ($($xs,)*) = value;
383 Serialize::serialize(
384 &($(WithEncoding::<&$adapters, _>::from($xs),)*),
385 serializer,
386 )
387 }
388 }
389
390 impl<'de, $($types, $adapters),*> DeserializeWith<'de, ($($types,)*)> for ($($adapters,)*)
391 where
392 $($adapters: DeserializeWith<'de, $types>,)*
393 {
394 fn deserialize_with<D>(deserializer: D) -> core::result::Result<($($types,)*), D::Error>
395 where
396 D: Deserializer<'de>,
397 {
398 let ($($xs,)*) = <($(WithEncoding<$adapters, $types>,)*) as Deserialize>::deserialize(deserializer)?;
399 Ok(($($xs.value,)*))
400 }
401 }
402 };
403}
404
405macro_rules! impl_tuples {
406 (
407 $($tys:ident $adapters:ident $xs:ident,)*
408 @ $ty_head:ident $adapter_head:ident $x_head:ident, $($ty_tail:ident $adapter_tail:ident $x_tail:ident,)*
409 ) => {
410 impl_tuple!($($tys $adapters $xs,)*);
411 impl_tuples!($($tys $adapters $xs,)* $ty_head $adapter_head $x_head, @ $($ty_tail $adapter_tail $x_tail,)*);
412
413 };
414 ($($tys:ident $adapters:ident $xs:ident,)* @) => {
415 impl_tuple!($($tys $adapters $xs,)*);
416
417 };
418 ($($tys:ident $adapters:ident $xs:ident,)*) => {
419 impl_tuples!(@ $($tys $adapters $xs,)*);
420 };
421}
422
423impl_tuples!(
424 T0 A0 x0,
425 T1 A1 x1,
426 T2 A2 x2,
427 T3 A3 x3,
428 T4 A4 x4,
429 T5 A5 x5,
430 T6 A6 x6,
431 T7 A7 x7,
432 T8 A8 x8,
433 T9 A9 x9,
434 T10 A10 x10,
435 T11 A11 x11,
436 T12 A12 x12,
437 T13 A13 x13,
438 T14 A14 x14,
439 T15 A15 x15,
440);
441
442#[cfg(test)]
443mod test_utils {
444 use core::fmt::Debug;
445 use serde::{de::DeserializeOwned, Serialize};
446 use serde_json::Value;
447
448 #[track_caller]
449 pub(crate) fn check_serialization<T>(x: T, expected: Value)
450 where
451 T: PartialEq + Debug + Serialize + DeserializeOwned,
452 {
453 let actual = serde_json::to_value(&x).unwrap();
454 assert_eq!(actual, expected);
455 let deserialized = serde_json::from_value::<T>(actual).unwrap();
456 assert_eq!(x, deserialized);
457 }
458}