ipld_nostd/ipld/
convert.rs

1//! Conversion to and from ipld.
2use {
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/// Error used for converting from and into [`crate::ipld::Ipld`].
16#[derive(Clone, Debug)]
17#[non_exhaustive]
18pub enum ConversionError {
19	/// Error when the IPLD kind wasn't the one we expected.
20	WrongIpldKind {
21		/// The expected type.
22		expected: IpldKind,
23		/// The actual type.
24		found: IpldKind,
25	},
26	/// Error when the given Ipld kind cannot be converted into a certain value
27	/// type.
28	FromIpld {
29		/// The IPLD kind trying to convert from.
30		from: IpldKind,
31		/// The type trying to convert into.
32		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
177// derive_from_ipld!(Float, f32); // User explicit conversion is prefered. Would
178// implicitly lossily convert from f64.
179
180derive_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
201// derive_from_ipld_option!(Float, f32); // User explicit conversion is
202// prefered. Would implicitly lossily convert from f64.
203
204derive_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: f32 = Ipld::Float(f32::MAX as f64).try_into().unwrap();
286		// assert_eq!(float, f32::MAX);
287
288		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}