ipld_core/
ipld.rs

1//! Ipld representation.
2use alloc::{
3    borrow::ToOwned,
4    boxed::Box,
5    collections::BTreeMap,
6    string::{String, ToString},
7    vec,
8    vec::Vec,
9};
10use core::fmt;
11
12use cid::Cid;
13
14/// Error when accessing IPLD List or Map elements.
15#[derive(Clone, Debug)]
16#[non_exhaustive]
17pub enum IndexError {
18    /// Error when key cannot be parsed into an integer.
19    ParseInteger(String),
20    /// Error when the input wasn't an IPLD List or Map.
21    WrongKind(IpldKind),
22}
23
24impl fmt::Display for IndexError {
25    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
26        match self {
27            Self::ParseInteger(key) => write!(f, "cannot parse key into integer: {}", key),
28            Self::WrongKind(kind) => write!(f, "expected IPLD List or Map but found: {:?}", kind),
29        }
30    }
31}
32
33#[cfg(feature = "std")]
34impl std::error::Error for IndexError {}
35
36/// Ipld
37#[derive(Clone)]
38pub enum Ipld {
39    /// Represents the absence of a value or the value undefined.
40    Null,
41    /// Represents a boolean value.
42    Bool(bool),
43    /// Represents an integer.
44    Integer(i128),
45    /// Represents a floating point value.
46    Float(f64),
47    /// Represents an UTF-8 string.
48    String(String),
49    /// Represents a sequence of bytes.
50    Bytes(Vec<u8>),
51    /// Represents a list.
52    List(Vec<Ipld>),
53    /// Represents a map of strings.
54    Map(BTreeMap<String, Ipld>),
55    /// Represents a map of integers.
56    Link(Cid),
57}
58
59impl fmt::Debug for Ipld {
60    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
61        if f.alternate() {
62            match self {
63                Self::Null => write!(f, "Null"),
64                Self::Bool(b) => write!(f, "Bool({:?})", b),
65                Self::Integer(i) => write!(f, "Integer({:?})", i),
66                Self::Float(i) => write!(f, "Float({:?})", i),
67                Self::String(s) => write!(f, "String({:?})", s),
68                Self::Bytes(b) => write!(f, "Bytes({:?})", b),
69                Self::List(l) => write!(f, "List({:#?})", l),
70                Self::Map(m) => write!(f, "Map({:#?})", m),
71                Self::Link(cid) => write!(f, "Link({})", cid),
72            }
73        } else {
74            match self {
75                Self::Null => write!(f, "null"),
76                Self::Bool(b) => write!(f, "{:?}", b),
77                Self::Integer(i) => write!(f, "{:?}", i),
78                Self::Float(i) => write!(f, "{:?}", i),
79                Self::String(s) => write!(f, "{:?}", s),
80                Self::Bytes(b) => write!(f, "{:?}", b),
81                Self::List(l) => write!(f, "{:?}", l),
82                Self::Map(m) => write!(f, "{:?}", m),
83                Self::Link(cid) => write!(f, "{}", cid),
84            }
85        }
86    }
87}
88
89/// NaN floats are forbidden in the IPLD Data Model, but we do not enforce it. So in case such a
90/// value is introduced accidentally, make sure that it still compares as equal. This allows us
91/// to implement `Eq` for `Ipld`.
92impl PartialEq for Ipld {
93    fn eq(&self, other: &Self) -> bool {
94        match (self, other) {
95            (Self::Null, Self::Null) => true,
96            (Self::Bool(self_value), Self::Bool(other_value)) => self_value == other_value,
97            (Self::Integer(self_value), Self::Integer(other_value)) => self_value == other_value,
98            (Self::Float(self_value), Self::Float(other_value)) => {
99                // Treat two NaNs as being equal.
100                self_value == other_value || self_value.is_nan() && other_value.is_nan()
101            }
102            (Self::String(self_value), Self::String(other_value)) => self_value == other_value,
103            (Self::Bytes(self_value), Self::Bytes(other_value)) => self_value == other_value,
104            (Self::List(self_value), Self::List(other_value)) => self_value == other_value,
105            (Self::Map(self_value), Self::Map(other_value)) => self_value == other_value,
106            (Self::Link(self_value), Self::Link(other_value)) => self_value == other_value,
107            _ => false,
108        }
109    }
110}
111
112impl Eq for Ipld {}
113
114/// IPLD Kind information without the actual value.
115///
116/// Sometimes it's useful to know the kind of an Ipld object without the actual value, e.g. for
117/// error reporting. Those kinds can be a unity-only enum.
118#[derive(Clone, Debug)]
119pub enum IpldKind {
120    /// Null type.
121    Null,
122    /// Boolean type.
123    Bool,
124    /// Integer type.
125    Integer,
126    /// Float type.
127    Float,
128    /// String type.
129    String,
130    /// Bytes type.
131    Bytes,
132    /// List type.
133    List,
134    /// Map type.
135    Map,
136    /// Link type.
137    Link,
138}
139
140/// An index into IPLD.
141///
142/// It's used for accessing IPLD List and Map elements.
143pub enum IpldIndex<'a> {
144    /// An index into an ipld list.
145    List(usize),
146    /// An owned index into an ipld map.
147    Map(String),
148    /// An index into an ipld map.
149    MapRef(&'a str),
150}
151
152impl From<usize> for IpldIndex<'_> {
153    fn from(index: usize) -> Self {
154        Self::List(index)
155    }
156}
157
158impl From<String> for IpldIndex<'_> {
159    fn from(key: String) -> Self {
160        Self::Map(key)
161    }
162}
163
164impl<'a> From<&'a str> for IpldIndex<'a> {
165    fn from(key: &'a str) -> Self {
166        Self::MapRef(key)
167    }
168}
169
170impl<'a> TryFrom<IpldIndex<'a>> for usize {
171    type Error = IndexError;
172
173    fn try_from(index: IpldIndex<'a>) -> Result<Self, Self::Error> {
174        let parsed = match index {
175            IpldIndex::List(i) => i,
176            IpldIndex::Map(ref key) => key
177                .parse()
178                .map_err(|_| IndexError::ParseInteger(key.to_string()))?,
179            IpldIndex::MapRef(key) => key
180                .parse()
181                .map_err(|_| IndexError::ParseInteger(key.to_string()))?,
182        };
183        Ok(parsed)
184    }
185}
186
187impl<'a> From<IpldIndex<'a>> for String {
188    fn from(index: IpldIndex<'a>) -> Self {
189        match index {
190            IpldIndex::Map(ref key) => key.to_string(),
191            IpldIndex::MapRef(key) => key.to_string(),
192            IpldIndex::List(i) => i.to_string(),
193        }
194    }
195}
196
197impl Ipld {
198    /// Convert from an [`Ipld`] object into its kind without any associated values.
199    ///
200    /// This is intentionally not implemented via `From<Ipld>` to prevent accidental conversions by
201    /// making it more explicit.
202    pub fn kind(&self) -> IpldKind {
203        match self {
204            Ipld::Null => IpldKind::Null,
205            Ipld::Bool(_) => IpldKind::Bool,
206            Ipld::Integer(_) => IpldKind::Integer,
207            Ipld::Float(_) => IpldKind::Float,
208            Ipld::String(_) => IpldKind::String,
209            Ipld::Bytes(_) => IpldKind::Bytes,
210            Ipld::List(_) => IpldKind::List,
211            Ipld::Map(_) => IpldKind::Map,
212            Ipld::Link(_) => IpldKind::Link,
213        }
214    }
215
216    /// Destructs an ipld list or map
217    pub fn take<'a, T: Into<IpldIndex<'a>>>(
218        mut self,
219        index: T,
220    ) -> Result<Option<Self>, IndexError> {
221        let index = index.into();
222        match &mut self {
223            Ipld::List(ref mut list) => {
224                let parsed_index = usize::try_from(index)?;
225                if parsed_index < list.len() {
226                    Ok(Some(list.swap_remove(parsed_index)))
227                } else {
228                    Ok(None)
229                }
230            }
231            Ipld::Map(ref mut map) => {
232                let key = String::from(index);
233                Ok(map.remove(&key))
234            }
235            other => Err(IndexError::WrongKind(other.kind())),
236        }
237    }
238
239    /// Indexes into an ipld list or map.
240    pub fn get<'a, T: Into<IpldIndex<'a>>>(&self, index: T) -> Result<Option<&Self>, IndexError> {
241        let index = index.into();
242        match self {
243            Ipld::List(list) => {
244                let parsed_index = usize::try_from(index)?;
245                Ok(list.get(parsed_index))
246            }
247            Ipld::Map(map) => {
248                let key = String::from(index);
249                Ok(map.get(&key))
250            }
251            other => Err(IndexError::WrongKind(other.kind())),
252        }
253    }
254
255    /// Returns an iterator.
256    pub fn iter(&self) -> IpldIter<'_> {
257        IpldIter {
258            stack: vec![Box::new(vec![self].into_iter())],
259        }
260    }
261
262    /// Returns the references to other blocks.
263    pub fn references<E: Extend<Cid>>(&self, set: &mut E) {
264        for ipld in self.iter() {
265            if let Ipld::Link(cid) = ipld {
266                set.extend(core::iter::once(cid.to_owned()));
267            }
268        }
269    }
270}
271
272/// Ipld iterator.
273pub struct IpldIter<'a> {
274    stack: Vec<Box<dyn Iterator<Item = &'a Ipld> + 'a>>,
275}
276
277impl<'a> Iterator for IpldIter<'a> {
278    type Item = &'a Ipld;
279
280    fn next(&mut self) -> Option<Self::Item> {
281        loop {
282            if let Some(iter) = self.stack.last_mut() {
283                if let Some(ipld) = iter.next() {
284                    match ipld {
285                        Ipld::List(list) => {
286                            self.stack.push(Box::new(list.iter()));
287                        }
288                        Ipld::Map(map) => {
289                            self.stack.push(Box::new(map.values()));
290                        }
291                        _ => {}
292                    }
293                    return Some(ipld);
294                } else {
295                    self.stack.pop();
296                }
297            } else {
298                return None;
299            }
300        }
301    }
302}
303
304#[cfg(test)]
305mod tests {
306    use super::*;
307
308    #[test]
309    fn test_ipld_bool_from() {
310        assert_eq!(Ipld::Bool(true), Ipld::from(true));
311        assert_eq!(Ipld::Bool(false), Ipld::from(false));
312    }
313
314    #[test]
315    fn test_ipld_integer_from() {
316        assert_eq!(Ipld::Integer(1), Ipld::from(1i8));
317        assert_eq!(Ipld::Integer(1), Ipld::from(1i16));
318        assert_eq!(Ipld::Integer(1), Ipld::from(1i32));
319        assert_eq!(Ipld::Integer(1), Ipld::from(1i64));
320        assert_eq!(Ipld::Integer(1), Ipld::from(1i128));
321
322        //assert_eq!(Ipld::Integer(1), 1u8.to_ipld().to_owned());
323        assert_eq!(Ipld::Integer(1), Ipld::from(1u16));
324        assert_eq!(Ipld::Integer(1), Ipld::from(1u32));
325        assert_eq!(Ipld::Integer(1), Ipld::from(1u64));
326    }
327
328    #[test]
329    fn test_ipld_float_from() {
330        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f32));
331        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f64));
332    }
333
334    #[test]
335    fn test_ipld_string_from() {
336        assert_eq!(Ipld::String("a string".into()), Ipld::from("a string"));
337        assert_eq!(
338            Ipld::String("a string".into()),
339            Ipld::from("a string".to_string())
340        );
341    }
342
343    #[test]
344    fn test_ipld_bytes_from() {
345        assert_eq!(
346            Ipld::Bytes(vec![0, 1, 2, 3]),
347            Ipld::from(&[0u8, 1u8, 2u8, 3u8][..])
348        );
349        assert_eq!(
350            Ipld::Bytes(vec![0, 1, 2, 3]),
351            Ipld::from(vec![0u8, 1u8, 2u8, 3u8])
352        );
353    }
354
355    #[test]
356    fn test_ipld_link_from() {
357        let cid =
358            Cid::try_from("bafkreie74tgmnxqwojhtumgh5dzfj46gi4mynlfr7dmm7duwzyvnpw7h7m").unwrap();
359        assert_eq!(Ipld::Link(cid), Ipld::from(cid));
360    }
361
362    #[test]
363    fn test_take() {
364        let ipld = Ipld::List(vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)]);
365        assert_eq!(ipld.clone().take(0).unwrap(), Some(Ipld::Integer(0)));
366        assert_eq!(ipld.clone().take(1).unwrap(), Some(Ipld::Integer(1)));
367        assert_eq!(ipld.take(2).unwrap(), Some(Ipld::Integer(2)));
368
369        let mut map = BTreeMap::new();
370        map.insert("a".to_string(), Ipld::Integer(0));
371        map.insert("b".to_string(), Ipld::Integer(1));
372        map.insert("c".to_string(), Ipld::Integer(2));
373        let ipld = Ipld::Map(map);
374        assert_eq!(ipld.take("a").unwrap(), Some(Ipld::Integer(0)));
375    }
376
377    #[test]
378    fn test_get() {
379        let ipld = Ipld::List(vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)]);
380        assert_eq!(ipld.get(0).unwrap(), Some(&Ipld::Integer(0)));
381        assert_eq!(ipld.get(1).unwrap(), Some(&Ipld::Integer(1)));
382        assert_eq!(ipld.get(2).unwrap(), Some(&Ipld::Integer(2)));
383
384        let mut map = BTreeMap::new();
385        map.insert("a".to_string(), Ipld::Integer(0));
386        map.insert("b".to_string(), Ipld::Integer(1));
387        map.insert("c".to_string(), Ipld::Integer(2));
388        let ipld = Ipld::Map(map);
389        assert_eq!(ipld.get("a").unwrap(), Some(&Ipld::Integer(0)));
390    }
391
392    // NaN floats are forbidden in the IPLD Data Model, but still make sure they are treated as
393    // equal in case they accidentally end up there.
394    #[test]
395    fn test_partial_eq_nan() {
396        let invalid_ipld = Ipld::Float(f64::NAN);
397        assert_eq!(invalid_ipld, invalid_ipld);
398    }
399}