libipld_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 crate::cid::Cid;
13use crate::error::TypeError;
14
15/// Ipld
16#[derive(Clone, PartialEq)]
17pub enum Ipld {
18    /// Represents the absence of a value or the value undefined.
19    Null,
20    /// Represents a boolean value.
21    Bool(bool),
22    /// Represents an integer.
23    Integer(i128),
24    /// Represents a floating point value.
25    Float(f64),
26    /// Represents an UTF-8 string.
27    String(String),
28    /// Represents a sequence of bytes.
29    Bytes(Vec<u8>),
30    /// Represents a list.
31    List(Vec<Ipld>),
32    /// Represents a map of strings.
33    Map(BTreeMap<String, Ipld>),
34    /// Represents a map of integers.
35    Link(Cid),
36}
37
38impl fmt::Debug for Ipld {
39    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
40        if f.alternate() {
41            match self {
42                Self::Null => write!(f, "Null"),
43                Self::Bool(b) => write!(f, "Bool({:?})", b),
44                Self::Integer(i) => write!(f, "Integer({:?})", i),
45                Self::Float(i) => write!(f, "Float({:?})", i),
46                Self::String(s) => write!(f, "String({:?})", s),
47                Self::Bytes(b) => write!(f, "Bytes({:?})", b),
48                Self::List(l) => write!(f, "List({:#?})", l),
49                Self::Map(m) => write!(f, "Map({:#?})", m),
50                Self::Link(cid) => write!(f, "Link({})", cid),
51            }
52        } else {
53            match self {
54                Self::Null => write!(f, "null"),
55                Self::Bool(b) => write!(f, "{:?}", b),
56                Self::Integer(i) => write!(f, "{:?}", i),
57                Self::Float(i) => write!(f, "{:?}", i),
58                Self::String(s) => write!(f, "{:?}", s),
59                Self::Bytes(b) => write!(f, "{:?}", b),
60                Self::List(l) => write!(f, "{:?}", l),
61                Self::Map(m) => write!(f, "{:?}", m),
62                Self::Link(cid) => write!(f, "{}", cid),
63            }
64        }
65    }
66}
67
68/// An index into ipld
69pub enum IpldIndex<'a> {
70    /// An index into an ipld list.
71    List(usize),
72    /// An owned index into an ipld map.
73    Map(String),
74    /// An index into an ipld map.
75    MapRef(&'a str),
76}
77
78impl<'a> From<usize> for IpldIndex<'a> {
79    fn from(index: usize) -> Self {
80        Self::List(index)
81    }
82}
83
84impl<'a> From<String> for IpldIndex<'a> {
85    fn from(key: String) -> Self {
86        Self::Map(key)
87    }
88}
89
90impl<'a> From<&'a str> for IpldIndex<'a> {
91    fn from(key: &'a str) -> Self {
92        Self::MapRef(key)
93    }
94}
95
96impl Ipld {
97    /// Destructs an ipld list or map
98    pub fn take<'a, T: Into<IpldIndex<'a>>>(mut self, index: T) -> Result<Self, TypeError> {
99        let index = index.into();
100        let ipld = match &mut self {
101            Ipld::List(ref mut l) => match index {
102                IpldIndex::List(i) => Some(i),
103                IpldIndex::Map(ref key) => key.parse().ok(),
104                IpldIndex::MapRef(key) => key.parse().ok(),
105            }
106            .map(|i| {
107                if i < l.len() {
108                    Some(l.swap_remove(i))
109                } else {
110                    None
111                }
112            }),
113            Ipld::Map(ref mut m) => match index {
114                IpldIndex::Map(ref key) => Some(m.remove(key)),
115                IpldIndex::MapRef(key) => Some(m.remove(key)),
116                IpldIndex::List(i) => Some(m.remove(&i.to_string())),
117            },
118            _ => None,
119        };
120        ipld.unwrap_or_default()
121            .ok_or_else(|| TypeError::new(index, self))
122    }
123
124    /// Indexes into an ipld list or map.
125    pub fn get<'a, T: Into<IpldIndex<'a>>>(&self, index: T) -> Result<&Self, TypeError> {
126        let index = index.into();
127        let ipld = match self {
128            Ipld::List(l) => match index {
129                IpldIndex::List(i) => Some(i),
130                IpldIndex::Map(ref key) => key.parse().ok(),
131                IpldIndex::MapRef(key) => key.parse().ok(),
132            }
133            .map(|i| l.get(i)),
134            Ipld::Map(m) => match index {
135                IpldIndex::Map(ref key) => Some(m.get(key)),
136                IpldIndex::MapRef(key) => Some(m.get(key)),
137                IpldIndex::List(i) => Some(m.get(&i.to_string())),
138            },
139            _ => None,
140        };
141        ipld.unwrap_or_default()
142            .ok_or_else(|| TypeError::new(index, self))
143    }
144
145    /// Returns an iterator.
146    pub fn iter(&self) -> IpldIter<'_> {
147        IpldIter {
148            stack: vec![Box::new(vec![self].into_iter())],
149        }
150    }
151
152    /// Returns the references to other blocks.
153    pub fn references<E: Extend<Cid>>(&self, set: &mut E) {
154        for ipld in self.iter() {
155            if let Ipld::Link(cid) = ipld {
156                set.extend(core::iter::once(cid.to_owned()));
157            }
158        }
159    }
160}
161
162/// Ipld iterator.
163pub struct IpldIter<'a> {
164    stack: Vec<Box<dyn Iterator<Item = &'a Ipld> + 'a>>,
165}
166
167impl<'a> Iterator for IpldIter<'a> {
168    type Item = &'a Ipld;
169
170    fn next(&mut self) -> Option<Self::Item> {
171        loop {
172            if let Some(iter) = self.stack.last_mut() {
173                if let Some(ipld) = iter.next() {
174                    match ipld {
175                        Ipld::List(list) => {
176                            self.stack.push(Box::new(list.iter()));
177                        }
178                        Ipld::Map(map) => {
179                            self.stack.push(Box::new(map.values()));
180                        }
181                        _ => {}
182                    }
183                    return Some(ipld);
184                } else {
185                    self.stack.pop();
186                }
187            } else {
188                return None;
189            }
190        }
191    }
192}
193
194#[cfg(test)]
195mod tests {
196    use super::*;
197    use crate::cid::Cid;
198    use crate::multihash::{Code, MultihashDigest};
199
200    #[test]
201    fn test_ipld_bool_from() {
202        assert_eq!(Ipld::Bool(true), Ipld::from(true));
203        assert_eq!(Ipld::Bool(false), Ipld::from(false));
204    }
205
206    #[test]
207    fn test_ipld_integer_from() {
208        assert_eq!(Ipld::Integer(1), Ipld::from(1i8));
209        assert_eq!(Ipld::Integer(1), Ipld::from(1i16));
210        assert_eq!(Ipld::Integer(1), Ipld::from(1i32));
211        assert_eq!(Ipld::Integer(1), Ipld::from(1i64));
212        assert_eq!(Ipld::Integer(1), Ipld::from(1i128));
213
214        //assert_eq!(Ipld::Integer(1), 1u8.to_ipld().to_owned());
215        assert_eq!(Ipld::Integer(1), Ipld::from(1u16));
216        assert_eq!(Ipld::Integer(1), Ipld::from(1u32));
217        assert_eq!(Ipld::Integer(1), Ipld::from(1u64));
218    }
219
220    #[test]
221    fn test_ipld_float_from() {
222        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f32));
223        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f64));
224    }
225
226    #[test]
227    fn test_ipld_string_from() {
228        assert_eq!(Ipld::String("a string".into()), Ipld::from("a string"));
229        assert_eq!(
230            Ipld::String("a string".into()),
231            Ipld::from("a string".to_string())
232        );
233    }
234
235    #[test]
236    fn test_ipld_bytes_from() {
237        assert_eq!(
238            Ipld::Bytes(vec![0, 1, 2, 3]),
239            Ipld::from(&[0u8, 1u8, 2u8, 3u8][..])
240        );
241        assert_eq!(
242            Ipld::Bytes(vec![0, 1, 2, 3]),
243            Ipld::from(vec![0u8, 1u8, 2u8, 3u8])
244        );
245    }
246
247    #[test]
248    fn test_ipld_link_from() {
249        let data = vec![0, 1, 2, 3];
250        let hash = Code::Blake3_256.digest(&data);
251        let cid = Cid::new_v1(0x55, hash);
252        assert_eq!(Ipld::Link(cid), Ipld::from(cid));
253    }
254
255    #[test]
256    fn test_take() {
257        let ipld = Ipld::List(vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)]);
258        assert_eq!(ipld.clone().take(0).unwrap(), Ipld::Integer(0));
259        assert_eq!(ipld.clone().take(1).unwrap(), Ipld::Integer(1));
260        assert_eq!(ipld.take(2).unwrap(), Ipld::Integer(2));
261
262        let mut map = BTreeMap::new();
263        map.insert("a".to_string(), Ipld::Integer(0));
264        map.insert("b".to_string(), Ipld::Integer(1));
265        map.insert("c".to_string(), Ipld::Integer(2));
266        let ipld = Ipld::Map(map);
267        assert_eq!(ipld.take("a").unwrap(), Ipld::Integer(0));
268    }
269
270    #[test]
271    fn test_get() {
272        let ipld = Ipld::List(vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)]);
273        assert_eq!(ipld.get(0).unwrap(), &Ipld::Integer(0));
274        assert_eq!(ipld.get(1).unwrap(), &Ipld::Integer(1));
275        assert_eq!(ipld.get(2).unwrap(), &Ipld::Integer(2));
276
277        let mut map = BTreeMap::new();
278        map.insert("a".to_string(), Ipld::Integer(0));
279        map.insert("b".to_string(), Ipld::Integer(1));
280        map.insert("c".to_string(), Ipld::Integer(2));
281        let ipld = Ipld::Map(map);
282        assert_eq!(ipld.get("a").unwrap(), &Ipld::Integer(0));
283    }
284}