ipld_nostd/cid/
serde.rs

1//! CID Serde (de)serialization for the IPLD Data Model.
2//!
3//! CIDs cannot directly be represented in any of the native Serde Data model
4//! types. In order to work around that limitation. a newtype struct is
5//! introduced, that is used as a marker for Serde (de)serialization.
6
7use {
8	super::CidGeneric,
9	alloc::{format, vec::Vec},
10	core::{convert::TryFrom, fmt},
11	serde::{de, ser},
12	serde_bytes::ByteBuf,
13};
14
15/// An identifier that is used internally by Serde implementations that support
16/// [`Cid`]s.
17pub const CID_SERDE_PRIVATE_IDENTIFIER: &str =
18	"$__private__serde__identifier__for__cid";
19
20/// Serialize a CID into the Serde data model as enum.
21///
22/// Custom types are not supported by Serde, hence we map a CID into an enum
23/// that can be identified as a CID by implementations that support CIDs. The
24/// corresponding Rust type would be:
25///
26/// ```text
27/// struct $__private__serde__identifier__for__cid(serde_bytes::BytesBuf);
28/// ```
29impl<const SIZE: usize> ser::Serialize for CidGeneric<SIZE> {
30	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
31	where
32		S: ser::Serializer,
33	{
34		let value = ByteBuf::from(self.to_bytes());
35		serializer.serialize_newtype_struct(CID_SERDE_PRIVATE_IDENTIFIER, &value)
36	}
37}
38
39/// Visitor to transform bytes into a CID.
40pub struct BytesToCidVisitor<const SIZE: usize = 64>;
41
42impl<'de, const SIZE: usize> de::Visitor<'de> for BytesToCidVisitor<SIZE> {
43	type Value = CidGeneric<SIZE>;
44
45	fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
46		write!(fmt, "a valid CID in bytes")
47	}
48
49	fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
50	where
51		E: de::Error,
52	{
53		CidGeneric::<SIZE>::try_from(value).map_err(|err| {
54			de::Error::custom(format!("Failed to deserialize CID: {}", err))
55		})
56	}
57
58	/// Some Serde data formats interpret a byte stream as a sequence of bytes
59	/// (e.g. `serde_json`).
60	fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
61	where
62		A: de::SeqAccess<'de>,
63	{
64		let mut bytes = Vec::new();
65		while let Some(byte) = seq.next_element()? {
66			bytes.push(byte);
67		}
68		CidGeneric::<SIZE>::try_from(bytes).map_err(|err| {
69			de::Error::custom(format!("Failed to deserialize CID: {}", err))
70		})
71	}
72}
73
74/// Deserialize a CID into a newtype struct.
75///
76/// Deserialize a CID that was serialized as a newtype struct, so that can be
77/// identified as a CID. Its corresponding Rust type would be:
78///
79/// ```text
80/// struct $__private__serde__identifier__for__cid(serde_bytes::BytesBuf);
81/// ```
82impl<'de, const SIZE: usize> de::Deserialize<'de> for CidGeneric<SIZE> {
83	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
84	where
85		D: de::Deserializer<'de>,
86	{
87		/// Main visitor to deserialize a CID.
88		///
89		/// This visitor has only a single entry point to deserialize CIDs, it's
90		/// `visit_new_type_struct()`. This ensures that it isn't accidentally used
91		/// to decode CIDs to bytes.
92		struct MainEntryVisitor<const SIZE: usize>;
93
94		impl<'de, const SIZE: usize> de::Visitor<'de> for MainEntryVisitor<SIZE> {
95			type Value = CidGeneric<SIZE>;
96
97			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
98				write!(fmt, "a valid CID in bytes, wrapped in an newtype struct")
99			}
100
101			fn visit_newtype_struct<D>(
102				self,
103				deserializer: D,
104			) -> Result<Self::Value, D::Error>
105			where
106				D: de::Deserializer<'de>,
107			{
108				deserializer.deserialize_bytes(BytesToCidVisitor)
109			}
110		}
111
112		deserializer.deserialize_newtype_struct(
113			CID_SERDE_PRIVATE_IDENTIFIER,
114			MainEntryVisitor,
115		)
116	}
117}
118
119#[cfg(test)]
120mod tests {
121	use super::CidGeneric;
122
123	#[test]
124	fn test_cid_serde() {
125		let cid = CidGeneric::<70>::try_from(
126			"bafkreibme22gw2h7y2h7tg2fhqotaqjucnbc24deqo72b6mkl2egezxhvy",
127		)
128		.unwrap();
129		let bytes = serde_json::to_string(&cid).unwrap();
130		let cid2 = serde_json::from_str(&bytes).unwrap();
131		assert_eq!(cid, cid2);
132	}
133}