1use {
3 super::{Ipld, IpldKind},
4 crate::cid::Cid,
5 alloc::{
6 borrow::ToOwned,
7 boxed::Box,
8 collections::BTreeMap,
9 string::{String, ToString},
10 vec::Vec,
11 },
12 core::{any::TypeId, fmt},
13};
14
15#[derive(Clone, Debug)]
17#[non_exhaustive]
18pub enum ConversionError {
19 WrongIpldKind {
21 expected: IpldKind,
23 found: IpldKind,
25 },
26 FromIpld {
29 from: IpldKind,
31 into: TypeId,
33 },
34}
35
36impl fmt::Display for ConversionError {
37 fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
38 match self {
39 Self::WrongIpldKind { expected, found } => {
40 write!(
41 formatter,
42 "kind error: expected {:?} but found {:?}",
43 expected, found
44 )
45 }
46 Self::FromIpld { from, into } => {
47 write!(
48 formatter,
49 "conversion error: cannot convert {:?} into {:?}",
50 from, into
51 )
52 }
53 }
54 }
55}
56
57impl TryFrom<Ipld> for () {
58 type Error = ConversionError;
59
60 fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
61 match ipld {
62 Ipld::Null => Ok(()),
63 _ => Err(ConversionError::WrongIpldKind {
64 expected: IpldKind::Null,
65 found: ipld.kind(),
66 }),
67 }
68 }
69}
70
71macro_rules! derive_try_from_ipld_option {
72 ($enum:ident, $ty:ty) => {
73 impl TryFrom<Ipld> for Option<$ty> {
74 type Error = ConversionError;
75
76 fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
77 match ipld {
78 Ipld::Null => Ok(None),
79 Ipld::$enum(value) => Ok(Some(value.try_into().map_err(|_| {
80 ConversionError::FromIpld {
81 from: IpldKind::$enum,
82 into: TypeId::of::<$ty>(),
83 }
84 })?)),
85 _ => Err(ConversionError::WrongIpldKind {
86 expected: IpldKind::$enum,
87 found: ipld.kind(),
88 }),
89 }
90 }
91 }
92 };
93}
94
95macro_rules! derive_try_from_ipld {
96 ($enum:ident, $ty:ty) => {
97 impl TryFrom<Ipld> for $ty {
98 type Error = ConversionError;
99
100 fn try_from(ipld: Ipld) -> Result<Self, Self::Error> {
101 match ipld {
102 Ipld::$enum(value) => {
103 Ok(value.try_into().map_err(|_| ConversionError::FromIpld {
104 from: IpldKind::$enum,
105 into: TypeId::of::<$ty>(),
106 })?)
107 }
108
109 _ => Err(ConversionError::WrongIpldKind {
110 expected: IpldKind::$enum,
111 found: ipld.kind(),
112 }),
113 }
114 }
115 }
116 };
117}
118
119macro_rules! derive_into_ipld_prim {
120 ($enum:ident, $ty:ty, $fn:ident) => {
121 impl From<$ty> for Ipld {
122 fn from(t: $ty) -> Self {
123 Ipld::$enum(t.$fn() as _)
124 }
125 }
126 };
127}
128
129macro_rules! derive_into_ipld {
130 ($enum:ident, $ty:ty, $($fn:ident),*) => {
131 impl From<$ty> for Ipld {
132 fn from(t: $ty) -> Self {
133 Ipld::$enum(t$(.$fn())*)
134 }
135 }
136 };
137}
138
139derive_into_ipld!(Bool, bool, clone);
140derive_into_ipld_prim!(Integer, i8, clone);
141derive_into_ipld_prim!(Integer, i16, clone);
142derive_into_ipld_prim!(Integer, i32, clone);
143derive_into_ipld_prim!(Integer, i64, clone);
144derive_into_ipld_prim!(Integer, i128, clone);
145derive_into_ipld_prim!(Integer, isize, clone);
146derive_into_ipld_prim!(Integer, u8, clone);
147derive_into_ipld_prim!(Integer, u16, clone);
148derive_into_ipld_prim!(Integer, u32, clone);
149derive_into_ipld_prim!(Integer, u64, clone);
150derive_into_ipld_prim!(Integer, usize, clone);
151derive_into_ipld_prim!(Float, f32, clone);
152derive_into_ipld_prim!(Float, f64, clone);
153derive_into_ipld!(String, String, into);
154derive_into_ipld!(String, &str, to_string);
155derive_into_ipld!(Bytes, Box<[u8]>, into_vec);
156derive_into_ipld!(Bytes, Vec<u8>, into);
157derive_into_ipld!(Bytes, &[u8], to_vec);
158derive_into_ipld!(List, Vec<Ipld>, into);
159derive_into_ipld!(Map, BTreeMap<String, Ipld>, to_owned);
160derive_into_ipld!(Link, Cid, clone);
161derive_into_ipld!(Link, &Cid, to_owned);
162
163derive_try_from_ipld!(Bool, bool);
164derive_try_from_ipld!(Integer, i8);
165derive_try_from_ipld!(Integer, i16);
166derive_try_from_ipld!(Integer, i32);
167derive_try_from_ipld!(Integer, i64);
168derive_try_from_ipld!(Integer, i128);
169derive_try_from_ipld!(Integer, isize);
170derive_try_from_ipld!(Integer, u8);
171derive_try_from_ipld!(Integer, u16);
172derive_try_from_ipld!(Integer, u32);
173derive_try_from_ipld!(Integer, u64);
174derive_try_from_ipld!(Integer, u128);
175derive_try_from_ipld!(Integer, usize);
176
177derive_try_from_ipld!(Float, f64);
181derive_try_from_ipld!(String, String);
182derive_try_from_ipld!(Bytes, Vec<u8>);
183derive_try_from_ipld!(List, Vec<Ipld>);
184derive_try_from_ipld!(Map, BTreeMap<String, Ipld>);
185derive_try_from_ipld!(Link, Cid);
186
187derive_try_from_ipld_option!(Bool, bool);
188derive_try_from_ipld_option!(Integer, i8);
189derive_try_from_ipld_option!(Integer, i16);
190derive_try_from_ipld_option!(Integer, i32);
191derive_try_from_ipld_option!(Integer, i64);
192derive_try_from_ipld_option!(Integer, i128);
193derive_try_from_ipld_option!(Integer, isize);
194derive_try_from_ipld_option!(Integer, u8);
195derive_try_from_ipld_option!(Integer, u16);
196derive_try_from_ipld_option!(Integer, u32);
197derive_try_from_ipld_option!(Integer, u64);
198derive_try_from_ipld_option!(Integer, u128);
199derive_try_from_ipld_option!(Integer, usize);
200
201derive_try_from_ipld_option!(Float, f64);
205derive_try_from_ipld_option!(String, String);
206derive_try_from_ipld_option!(Bytes, Vec<u8>);
207derive_try_from_ipld_option!(List, Vec<Ipld>);
208derive_try_from_ipld_option!(Map, BTreeMap<String, Ipld>);
209derive_try_from_ipld_option!(Link, Cid);
210
211#[cfg(test)]
212mod tests {
213 use {
214 super::super::Ipld,
215 crate::cid::Cid,
216 ::alloc::{collections::BTreeMap, string::String, vec::Vec, *},
217 };
218
219 #[test]
220 #[should_panic]
221 fn try_into_wrong_type() {
222 let _boolean: bool = Ipld::Integer(u8::MAX as i128).try_into().unwrap();
223 }
224
225 #[test]
226 #[should_panic]
227 fn try_into_wrong_range() {
228 let int: u128 = Ipld::Integer(-1i128).try_into().unwrap();
229 assert_eq!(int, u128::MIN);
230 }
231
232 #[test]
233 fn try_into_bool() {
234 let boolean: bool = Ipld::Bool(true).try_into().unwrap();
235 assert!(boolean);
236
237 let boolean: Option<bool> = Ipld::Null.try_into().unwrap();
238 assert_eq!(boolean, Option::None)
239 }
240
241 #[test]
242 fn try_into_ints() {
243 let int: u8 = Ipld::Integer(u8::MAX as i128).try_into().unwrap();
244 assert_eq!(int, u8::MAX);
245
246 let int: u16 = Ipld::Integer(u16::MAX as i128).try_into().unwrap();
247 assert_eq!(int, u16::MAX);
248
249 let int: u32 = Ipld::Integer(u32::MAX as i128).try_into().unwrap();
250 assert_eq!(int, u32::MAX);
251
252 let int: u64 = Ipld::Integer(u64::MAX as i128).try_into().unwrap();
253 assert_eq!(int, u64::MAX);
254
255 let int: usize = Ipld::Integer(usize::MAX as i128).try_into().unwrap();
256 assert_eq!(int, usize::MAX);
257
258 let int: u128 = Ipld::Integer(i128::MAX).try_into().unwrap();
259 assert_eq!(int, i128::MAX as u128);
260
261 let int: i8 = Ipld::Integer(i8::MIN as i128).try_into().unwrap();
262 assert_eq!(int, i8::MIN);
263
264 let int: i16 = Ipld::Integer(i16::MIN as i128).try_into().unwrap();
265 assert_eq!(int, i16::MIN);
266
267 let int: i32 = Ipld::Integer(i32::MIN as i128).try_into().unwrap();
268 assert_eq!(int, i32::MIN);
269
270 let int: i64 = Ipld::Integer(i64::MIN as i128).try_into().unwrap();
271 assert_eq!(int, i64::MIN);
272
273 let int: isize = Ipld::Integer(isize::MIN as i128).try_into().unwrap();
274 assert_eq!(int, isize::MIN);
275
276 let int: i128 = Ipld::Integer(i128::MIN).try_into().unwrap();
277 assert_eq!(int, i128::MIN);
278
279 let int: Option<i32> = Ipld::Null.try_into().unwrap();
280 assert_eq!(int, Option::None)
281 }
282
283 #[test]
284 fn try_into_floats() {
285 let float: f64 = Ipld::Float(f64::MAX).try_into().unwrap();
289 assert_eq!(float, f64::MAX);
290
291 let float: Option<f64> = Ipld::Null.try_into().unwrap();
292 assert_eq!(float, Option::None)
293 }
294
295 #[test]
296 fn try_into_string() {
297 let lyrics: String = "I'm blue babedi babeda".into();
298 let string: String = Ipld::String(lyrics.clone()).try_into().unwrap();
299 assert_eq!(string, lyrics);
300
301 let option: Option<String> = Ipld::Null.try_into().unwrap();
302 assert_eq!(option, Option::None)
303 }
304
305 #[test]
306 fn try_into_vec() {
307 let data = vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9];
308 let bytes: Vec<u8> = Ipld::Bytes(data.clone()).try_into().unwrap();
309 assert_eq!(bytes, data);
310
311 let option: Option<Vec<u8>> = Ipld::Null.try_into().unwrap();
312 assert_eq!(option, Option::None)
313 }
314
315 #[test]
316 fn try_into_list() {
317 let ints = vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)];
318 let list: Vec<Ipld> = Ipld::List(ints.clone()).try_into().unwrap();
319 assert_eq!(ints, list);
320
321 let option: Option<Vec<Ipld>> = Ipld::Null.try_into().unwrap();
322 assert_eq!(option, Option::None)
323 }
324
325 #[test]
326 fn try_into_map() {
327 let mut numbs = BTreeMap::new();
328 numbs.insert("zero".into(), Ipld::Integer(0));
329 numbs.insert("one".into(), Ipld::Integer(1));
330 numbs.insert("two".into(), Ipld::Integer(2));
331 let map: BTreeMap<String, Ipld> =
332 Ipld::Map(numbs.clone()).try_into().unwrap();
333 assert_eq!(numbs, map);
334
335 let option: Option<BTreeMap<String, Ipld>> = Ipld::Null.try_into().unwrap();
336 assert_eq!(option, Option::None)
337 }
338
339 #[test]
340 fn try_into_cid() {
341 let cid = Cid::default();
342 let link: Cid = Ipld::Link(cid).try_into().unwrap();
343 assert_eq!(cid, link);
344
345 let option: Option<Cid> = Ipld::Null.try_into().unwrap();
346 assert_eq!(option, Option::None)
347 }
348}