1mod de;
7mod extract_links;
8mod ser;
9
10use {
11 alloc::string::{String, ToString},
12 core::fmt,
13};
14pub use {
15 de::from_ipld,
16 extract_links::ExtractLinks,
17 ser::{to_ipld, Serializer},
18};
19
20#[derive(Clone, Debug)]
22pub struct SerdeError(String);
23
24impl fmt::Display for SerdeError {
25 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26 write!(f, "serde error: {}", self.0)
27 }
28}
29
30impl serde::de::Error for SerdeError {
31 fn custom<T: fmt::Display>(message: T) -> Self {
32 Self(message.to_string())
33 }
34}
35
36impl serde::ser::Error for SerdeError {
37 fn custom<T: fmt::Display>(message: T) -> Self {
38 Self(message.to_string())
39 }
40}
41
42impl serde::ser::StdError for SerdeError {}
43
44#[cfg(test)]
45mod tests {
46 use {
47 super::super::{
48 serde::{from_ipld, to_ipld},
49 Ipld,
50 },
51 crate::cid::{serde::CID_SERDE_PRIVATE_IDENTIFIER, Cid},
52 ::alloc::{collections::BTreeMap, string::String, vec::Vec, *},
53 core::fmt,
54 serde::{de::DeserializeOwned, Serialize},
55 serde_derive::Deserialize,
56 serde_test::{assert_tokens, Token},
57 };
58
59 fn assert_roundtrip<T>(data: &T, ipld: &Ipld)
63 where
64 T: Serialize + DeserializeOwned + PartialEq + fmt::Debug,
65 {
66 let encoded: Ipld = to_ipld(data).unwrap();
67 assert_eq!(&encoded, ipld);
68 let decoded: T = from_ipld(ipld.clone()).unwrap();
69 assert_eq!(&decoded, data);
70 }
71
72 #[derive(Debug, Deserialize, PartialEq, Serialize)]
73 struct Person {
74 name: String,
75 age: u8,
76 hobbies: Vec<String>,
77 is_cool: bool,
78 link: Cid,
79 }
80
81 impl Default for Person {
82 fn default() -> Self {
83 Self {
84 name: "Hello World!".into(),
85 age: 52,
86 hobbies: vec!["geography".into(), "programming".into()],
87 is_cool: true,
88 link: Cid::try_from(
89 "bafyreibvjvcv745gig4mvqs4hctx4zfkono4rjejm2ta6gtyzkqxfjeily",
90 )
91 .unwrap(),
92 }
93 }
94 }
95
96 #[test]
97 fn test_tokens() {
98 let person = Person::default();
99
100 assert_tokens(&person, &[
101 Token::Struct {
102 name: "Person",
103 len: 5,
104 },
105 Token::Str("name"),
106 Token::Str("Hello World!"),
107 Token::Str("age"),
108 Token::U8(52),
109 Token::Str("hobbies"),
110 Token::Seq { len: Some(2) },
111 Token::Str("geography"),
112 Token::Str("programming"),
113 Token::SeqEnd,
114 Token::Str("is_cool"),
115 Token::Bool(true),
116 Token::Str("link"),
117 Token::NewtypeStruct {
118 name: CID_SERDE_PRIVATE_IDENTIFIER,
119 },
120 Token::Bytes(&[
121 0x01, 0x71, 0x12, 0x20, 0x35, 0x4d, 0x45, 0x5f, 0xf3, 0xa6, 0x41, 0xb8,
122 0xca, 0xc2, 0x5c, 0x38, 0xa7, 0x7e, 0x64, 0xaa, 0x73, 0x5d, 0xc8, 0xa4,
123 0x89, 0x66, 0xa6, 0xf, 0x1a, 0x78, 0xca, 0xa1, 0x72, 0xa4, 0x88, 0x5e,
124 ]),
125 Token::StructEnd,
126 ]);
127 }
128
129 #[test]
131 fn test_ipld() {
132 let person = Person::default();
133
134 let expected_ipld = Ipld::Map({
135 BTreeMap::from([
136 ("name".into(), Ipld::String("Hello World!".into())),
137 ("age".into(), Ipld::Integer(52)),
138 (
139 "hobbies".into(),
140 Ipld::List(vec![
141 Ipld::String("geography".into()),
142 Ipld::String("programming".into()),
143 ]),
144 ),
145 ("is_cool".into(), Ipld::Bool(true)),
146 ("link".into(), Ipld::Link(person.link)),
147 ])
148 });
149
150 assert_roundtrip(&person, &expected_ipld);
151 }
152
153 #[test]
156 fn test_bytes_not_cid() {
157 let cid = Cid::try_from(
158 "bafyreibvjvcv745gig4mvqs4hctx4zfkono4rjejm2ta6gtyzkqxfjeily",
159 )
160 .unwrap();
161
162 let bytes_not_cid = Ipld::Bytes(cid.to_bytes());
163 let not_a_cid: Result<Cid, _> = from_ipld(bytes_not_cid);
164 assert!(not_a_cid.is_err());
165
166 let link = Ipld::Link(cid);
168 let a_cid: Cid = from_ipld(link).unwrap();
169 assert_eq!(a_cid, cid);
170 }
171}