ipld_nostd/cid/
serde.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
//! CID Serde (de)serialization for the IPLD Data Model.
//!
//! CIDs cannot directly be represented in any of the native Serde Data model
//! types. In order to work around that limitation. a newtype struct is
//! introduced, that is used as a marker for Serde (de)serialization.

use {
	super::CidGeneric,
	alloc::{format, vec::Vec},
	core::{convert::TryFrom, fmt},
	serde::{de, ser},
	serde_bytes::ByteBuf,
};

/// An identifier that is used internally by Serde implementations that support
/// [`Cid`]s.
pub const CID_SERDE_PRIVATE_IDENTIFIER: &str =
	"$__private__serde__identifier__for__cid";

/// Serialize a CID into the Serde data model as enum.
///
/// Custom types are not supported by Serde, hence we map a CID into an enum
/// that can be identified as a CID by implementations that support CIDs. The
/// corresponding Rust type would be:
///
/// ```text
/// struct $__private__serde__identifier__for__cid(serde_bytes::BytesBuf);
/// ```
impl<const SIZE: usize> ser::Serialize for CidGeneric<SIZE> {
	fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
	where
		S: ser::Serializer,
	{
		let value = ByteBuf::from(self.to_bytes());
		serializer.serialize_newtype_struct(CID_SERDE_PRIVATE_IDENTIFIER, &value)
	}
}

/// Visitor to transform bytes into a CID.
pub struct BytesToCidVisitor<const SIZE: usize = 64>;

impl<'de, const SIZE: usize> de::Visitor<'de> for BytesToCidVisitor<SIZE> {
	type Value = CidGeneric<SIZE>;

	fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
		write!(fmt, "a valid CID in bytes")
	}

	fn visit_bytes<E>(self, value: &[u8]) -> Result<Self::Value, E>
	where
		E: de::Error,
	{
		CidGeneric::<SIZE>::try_from(value).map_err(|err| {
			de::Error::custom(format!("Failed to deserialize CID: {}", err))
		})
	}

	/// Some Serde data formats interpret a byte stream as a sequence of bytes
	/// (e.g. `serde_json`).
	fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
	where
		A: de::SeqAccess<'de>,
	{
		let mut bytes = Vec::new();
		while let Some(byte) = seq.next_element()? {
			bytes.push(byte);
		}
		CidGeneric::<SIZE>::try_from(bytes).map_err(|err| {
			de::Error::custom(format!("Failed to deserialize CID: {}", err))
		})
	}
}

/// Deserialize a CID into a newtype struct.
///
/// Deserialize a CID that was serialized as a newtype struct, so that can be
/// identified as a CID. Its corresponding Rust type would be:
///
/// ```text
/// struct $__private__serde__identifier__for__cid(serde_bytes::BytesBuf);
/// ```
impl<'de, const SIZE: usize> de::Deserialize<'de> for CidGeneric<SIZE> {
	fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
	where
		D: de::Deserializer<'de>,
	{
		/// Main visitor to deserialize a CID.
		///
		/// This visitor has only a single entry point to deserialize CIDs, it's
		/// `visit_new_type_struct()`. This ensures that it isn't accidentally used
		/// to decode CIDs to bytes.
		struct MainEntryVisitor<const SIZE: usize>;

		impl<'de, const SIZE: usize> de::Visitor<'de> for MainEntryVisitor<SIZE> {
			type Value = CidGeneric<SIZE>;

			fn expecting(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
				write!(fmt, "a valid CID in bytes, wrapped in an newtype struct")
			}

			fn visit_newtype_struct<D>(
				self,
				deserializer: D,
			) -> Result<Self::Value, D::Error>
			where
				D: de::Deserializer<'de>,
			{
				deserializer.deserialize_bytes(BytesToCidVisitor)
			}
		}

		deserializer.deserialize_newtype_struct(
			CID_SERDE_PRIVATE_IDENTIFIER,
			MainEntryVisitor,
		)
	}
}

#[cfg(test)]
mod tests {
	use super::CidGeneric;

	#[test]
	fn test_cid_serde() {
		let cid = CidGeneric::<70>::try_from(
			"bafkreibme22gw2h7y2h7tg2fhqotaqjucnbc24deqo72b6mkl2egezxhvy",
		)
		.unwrap();
		let bytes = serde_json::to_string(&cid).unwrap();
		let cid2 = serde_json::from_str(&bytes).unwrap();
		assert_eq!(cid, cid2);
	}
}