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