Skip to main content

ipld/
canon.rs

1//! Canonicalization conversion implementations.
2//!
3//!
4
5use crate::{borrowed::Ipld, CodecExt, Error};
6use cid::Cid;
7use failure::format_err;
8use std::{convert::TryFrom, marker::PhantomData};
9
10/// Shorthand for deriving `From<& _>` for a reference to a type.
11#[doc(hidden)]
12#[macro_export(local_inner_macros)]
13macro_rules! borrowed_ipld_from_ref {
14    ($type:ty : $member:ident) => {
15        impl<'a, C> From<&'a $type> for Ipld<'a, C>
16        where
17            C: CodecExt,
18        {
19            #[inline]
20            fn from(t: &'a $type) -> Ipld<'a, C> {
21                Ipld::$member(*t)
22            }
23        }
24    };
25}
26
27// null
28
29impl<'a, C> TryFrom<Ipld<'a, C>> for ()
30where
31    C: CodecExt,
32{
33    type Error = Error;
34
35    #[inline]
36    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
37        match ipld {
38            Ipld::Null(_) => Ok(()),
39            _ => Err(Error::Ipld(format_err!("Not Null"))),
40        }
41    }
42}
43
44impl<'a, C> From<&'a ()> for Ipld<'a, C>
45where
46    C: CodecExt,
47{
48    #[inline]
49    fn from(_: &'a ()) -> Ipld<'a, C> {
50        Ipld::Null(PhantomData)
51    }
52}
53
54// bool
55
56borrowed_ipld_from_ref!(bool: Bool);
57impl<'a, C> TryFrom<Ipld<'a, C>> for bool
58where
59    C: CodecExt,
60{
61    type Error = Error;
62
63    #[inline]
64    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
65        match ipld {
66            Ipld::Bool(b) => Ok(b),
67            _ => Err(Error::Ipld(format_err!("Not Bool"))),
68        }
69    }
70}
71
72// int
73
74#[doc(hidden)]
75#[macro_export(local_inner_macros)]
76macro_rules! try_from_num {
77    ($type:ty : $member:ident) => {
78        impl<'a, C> TryFrom<Ipld<'a, C>> for $type
79        where
80            C: CodecExt,
81        {
82            type Error = Error;
83
84            #[inline]
85            fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
86                match ipld {
87                    Ipld::$member(i) => Ok(i),
88                    _ => Err(Error::Ipld(::failure::format_err!(
89                        "Not {}",
90                        ::std::stringify!($type)
91                    ))),
92                }
93            }
94        }
95
96        borrowed_ipld_from_ref!($type: $member);
97    };
98}
99
100try_from_num!(i8: Int8);
101try_from_num!(i16: Int16);
102try_from_num!(i32: Int32);
103try_from_num!(i64: Int64);
104try_from_num!(i128: Int128);
105try_from_num!(u8: Uint8);
106try_from_num!(u16: Uint16);
107try_from_num!(u32: Uint32);
108try_from_num!(u64: Uint64);
109try_from_num!(u128: Uint128);
110try_from_num!(f32: Float32);
111try_from_num!(f64: Float64);
112
113// string
114
115impl<'a, C> TryFrom<Ipld<'a, C>> for &'a str
116where
117    C: CodecExt,
118{
119    type Error = Error;
120
121    #[inline]
122    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
123        match ipld {
124            Ipld::String(s) => Ok(s),
125            _ => Err(Error::Ipld(format_err!("Not String"))),
126        }
127    }
128}
129
130impl<'a, C> TryFrom<Ipld<'a, C>> for String
131where
132    C: CodecExt,
133{
134    type Error = Error;
135
136    #[inline]
137    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
138        match ipld {
139            Ipld::String(s) => Ok(s.into()),
140            _ => Err(Error::Ipld(format_err!("Not String"))),
141        }
142    }
143}
144
145impl<'a, C> From<&'a String> for Ipld<'a, C>
146where
147    C: CodecExt,
148{
149    #[inline]
150    fn from(s: &'a String) -> Ipld<'a, C> {
151        Ipld::String(&*s)
152    }
153}
154
155// bytes
156
157impl<'a, C> TryFrom<Ipld<'a, C>> for &'a [u8]
158where
159    C: CodecExt,
160{
161    type Error = Error;
162
163    #[inline]
164    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
165        match ipld {
166            Ipld::Bytes(b) => Ok(b),
167            _ => Err(Error::Ipld(format_err!("Not Bytes"))),
168        }
169    }
170}
171
172impl<'a, C> From<&'a Box<[u8]>> for Ipld<'a, C>
173where
174    C: CodecExt,
175{
176    #[inline]
177    fn from(bytes: &'a Box<[u8]>) -> Ipld<'a, C> {
178        Ipld::Bytes(&*bytes)
179    }
180}
181
182#[cfg(feature = "bytes_")]
183impl<'a, C> TryFrom<Ipld<'a, C>> for bytes::Bytes
184where
185    C: CodecExt,
186{
187    type Error = Error;
188
189    #[inline]
190    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
191        match ipld {
192            Ipld::Bytes(b) => Ok(bytes::Bytes::copy_from_slice(b)),
193            _ => Err(Error::Ipld(format_err!("Not Bytes"))),
194        }
195    }
196}
197
198#[cfg(feature = "bytes_")]
199impl<'a, C> From<&'a bytes::Bytes> for Ipld<'a, C>
200where
201    C: CodecExt,
202{
203    #[inline]
204    fn from(bytes: &'a bytes::Bytes) -> Ipld<'a, C> {
205        Ipld::Bytes(bytes.as_ref())
206    }
207}
208
209// cid
210
211impl<'a, C> TryFrom<Ipld<'a, C>> for Cid
212where
213    C: CodecExt,
214{
215    type Error = Error;
216
217    #[inline]
218    fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
219        match ipld {
220            Ipld::Link(cid) => Ok(cid),
221            _ => Err(Error::Ipld(format_err!("Not Link"))),
222        }
223    }
224}
225
226impl<'a, C> From<&'a Cid> for Ipld<'a, C>
227where
228    C: CodecExt,
229{
230    #[inline]
231    fn from(link: &'a Cid) -> Ipld<'a, C> {
232        Ipld::Link(link.to_owned())
233    }
234}
235
236/// Derives `TryFrom<Ipld>` for an arbitrary struct.
237/// Used within other macros that produce an IPLD `Representation` for custom types.
238#[macro_export]
239macro_rules! derive_ipld_for_struct {
240    ($name:ident { $($member:ident : $value_type:ty,)* }) => {
241        ::paste::item! {
242            #[doc(hidden)]
243            enum [<$name Field>] {
244                $(
245                    [<Field $member>],
246                )*
247            }
248        }
249        ::paste::item! {
250            #[doc(hidden)]
251            #[derive(Default)]
252            struct [<$name Builder>] { $($member : Option<$value_type>,)* }
253
254            impl [<$name Builder>] {
255                $(
256                    #[inline]
257                    fn [<set_ $member>](&mut self, value: $value_type) -> &mut Self {
258                        self.$member = Some(value);
259                        self
260                    }
261                )*
262
263                #[inline]
264                fn field(key: &str) -> Result<[<$name Field>], Error> {
265                    match key {
266                        $(::std::stringify!($member) => Ok([<$name Field>]::[<Field $member>]),)*
267                        _ => Err(Error::Codec(::failure::format_err!("missing key: {}", key).into())),
268                    }
269                }
270
271                #[inline]
272                fn build(self) -> Result<$name, Error> {
273                    Ok($name {
274                        $($member: self.$member
275                            .ok_or(Error::Codec(::failure::format_err!("Missing key: {}", ::std::stringify!($member))))?,)*
276                    })
277                }
278            }
279        }
280
281        ::paste::item! {
282            impl<'a, C> TryFrom<Ipld<'a, C>> for $name
283            where
284                C: CodecExt,
285            {
286                type Error = Error;
287
288                #[inline]
289                fn try_from(ipld: Ipld<'a, C>) -> Result<Self, Self::Error> {
290                    match ipld {
291                        Ipld::Map(map_iter) => match map_iter {
292                            IpldMapIter::Vec(iter) => {
293                                let mut iter = iter.into_inner();
294                                let mut builder = [<$name Builder>]::default();
295
296                                while let Some((key, value)) = iter.next() {
297                                    match [<$name Builder>]::field(key)? {
298                                        $([<$name Field>]::[<Field $member>] => {
299                                            builder.[<set_ $member>]($value_type::try_from(value)?)
300                                        },)*
301                                    };
302                                };
303
304                                builder.build()
305                            }
306                        },
307                        _ => Err(Error::Ipld(IpldError::NotMap)),
308                    }
309                }
310            }
311        }
312
313        ::paste::item! {
314            impl<'a, C> TryInto<Ipld<'a, C>> for &'a $name
315            where
316                C: CodecExt,
317            {
318                type Error = Error;
319
320                #[inline]
321                fn try_into(self) -> Result<Ipld<'a, C>, Self::Error> {
322
323
324    //                let mut map: BTreeMap<&'static str, Ipld<'a, C>> = BTreeMap::new();
325    //                $(map.insert(::std::stringify!($member), (&(self.$member)).try_into()?);)*
326    //                Ok(Ipld::Map(map))
327                }
328            }
329        }
330    };
331    (@count $t1:tt, $($t:tt),+) => { 1 + derive_ipld_for_struct!(@count $($t),+) };
332    (@count $t:tt) => { 1 };
333}
334
335#[macro_export]
336macro_rules! derive_ipld_for_enum {
337    ($name:ident { $(| $member:ident,)* }) => {};
338}
339
340#[macro_export]
341macro_rules! derive_ipld_for_union {
342    ($name:ident { $($member:ident : $value_type:ty,)* }) => {};
343}