libipld_base/
ipld.rs

1//! Ipld representation.
2use crate::cid::Cid;
3use std::collections::BTreeMap;
4
5/// Ipld
6#[derive(Clone, Debug, PartialEq)]
7pub enum Ipld {
8    /// Represents the absence of a value or the value undefined.
9    Null,
10    /// Represents a boolean value.
11    Bool(bool),
12    /// Represents an integer.
13    Integer(i128),
14    /// Represents a floating point value.
15    Float(f64),
16    /// Represents an UTF-8 string.
17    String(String),
18    /// Represents a sequence of bytes.
19    Bytes(Vec<u8>),
20    /// Represents a list.
21    List(Vec<Ipld>),
22    /// Represents a map.
23    Map(BTreeMap<String, Ipld>),
24    /// Represents a link to an Ipld node
25    Link(Cid),
26}
27
28/// An index into ipld
29pub enum IpldIndex<'a> {
30    /// An index into an ipld list.
31    List(usize),
32    /// An owned index into an ipld map.
33    Map(String),
34    /// An index into an ipld map.
35    MapRef(&'a str),
36}
37
38impl<'a> From<usize> for IpldIndex<'a> {
39    fn from(index: usize) -> Self {
40        Self::List(index)
41    }
42}
43
44impl<'a> From<String> for IpldIndex<'a> {
45    fn from(key: String) -> Self {
46        Self::Map(key)
47    }
48}
49
50impl<'a> From<&'a str> for IpldIndex<'a> {
51    fn from(key: &'a str) -> Self {
52        Self::MapRef(key)
53    }
54}
55
56impl Ipld {
57    /// Indexes into a ipld list or map.
58    pub fn get<'a, T: Into<IpldIndex<'a>>>(&self, index: T) -> Option<&Ipld> {
59        match self {
60            Ipld::List(l) => match index.into() {
61                IpldIndex::List(i) => l.get(i),
62                _ => None,
63            },
64            Ipld::Map(m) => match index.into() {
65                IpldIndex::Map(ref key) => m.get(key),
66                IpldIndex::MapRef(key) => m.get(key),
67                _ => None,
68            },
69            _ => None,
70        }
71    }
72
73    /// Returns an iterator.
74    pub fn iter(&self) -> IpldIter<'_> {
75        IpldIter {
76            stack: vec![Box::new(vec![self].into_iter())],
77        }
78    }
79}
80
81/// Ipld iterator.
82pub struct IpldIter<'a> {
83    stack: Vec<Box<dyn Iterator<Item = &'a Ipld> + 'a>>,
84}
85
86impl<'a> Iterator for IpldIter<'a> {
87    type Item = &'a Ipld;
88
89    fn next(&mut self) -> Option<Self::Item> {
90        loop {
91            if let Some(iter) = self.stack.last_mut() {
92                if let Some(ipld) = iter.next() {
93                    match ipld {
94                        Ipld::List(list) => {
95                            self.stack.push(Box::new(list.iter()));
96                        }
97                        Ipld::Map(map) => {
98                            self.stack.push(Box::new(map.values()));
99                        }
100                        _ => {}
101                    }
102                    return Some(ipld);
103                } else {
104                    self.stack.pop();
105                }
106            } else {
107                return None;
108            }
109        }
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116    use crate::hash::{Hash, Sha2_256};
117
118    #[test]
119    fn ipld_bool_from() {
120        assert_eq!(Ipld::Bool(true), Ipld::from(true));
121        assert_eq!(Ipld::Bool(false), Ipld::from(false));
122    }
123
124    #[test]
125    fn ipld_integer_from() {
126        assert_eq!(Ipld::Integer(1), Ipld::from(1i8));
127        assert_eq!(Ipld::Integer(1), Ipld::from(1i16));
128        assert_eq!(Ipld::Integer(1), Ipld::from(1i32));
129        assert_eq!(Ipld::Integer(1), Ipld::from(1i64));
130        assert_eq!(Ipld::Integer(1), Ipld::from(1i128));
131
132        //assert_eq!(Ipld::Integer(1), 1u8.to_ipld().to_owned());
133        assert_eq!(Ipld::Integer(1), Ipld::from(1u16));
134        assert_eq!(Ipld::Integer(1), Ipld::from(1u32));
135        assert_eq!(Ipld::Integer(1), Ipld::from(1u64));
136    }
137
138    #[test]
139    fn ipld_float_from() {
140        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f32));
141        assert_eq!(Ipld::Float(1.0), Ipld::from(1.0f64));
142    }
143
144    #[test]
145    fn ipld_string_from() {
146        assert_eq!(Ipld::String("a string".into()), Ipld::from("a string"));
147        assert_eq!(
148            Ipld::String("a string".into()),
149            Ipld::from("a string".to_string())
150        );
151    }
152
153    #[test]
154    fn ipld_bytes_from() {
155        assert_eq!(
156            Ipld::Bytes(vec![0, 1, 2, 3]),
157            Ipld::from(&[0u8, 1u8, 2u8, 3u8][..])
158        );
159        assert_eq!(
160            Ipld::Bytes(vec![0, 1, 2, 3]),
161            Ipld::from(vec![0u8, 1u8, 2u8, 3u8])
162        );
163    }
164
165    #[test]
166    fn ipld_link_from() {
167        let data = vec![0, 1, 2, 3];
168        let hash = Sha2_256::digest(&data);
169        let cid = Cid::new_v0(hash).unwrap();
170        assert_eq!(Ipld::Link(cid.clone()), Ipld::from(cid));
171    }
172
173    #[test]
174    fn index() {
175        let ipld = Ipld::List(vec![Ipld::Integer(0), Ipld::Integer(1), Ipld::Integer(2)]);
176        assert_eq!(ipld.get(0).unwrap(), &Ipld::Integer(0));
177        assert_eq!(ipld.get(1).unwrap(), &Ipld::Integer(1));
178        assert_eq!(ipld.get(2).unwrap(), &Ipld::Integer(2));
179
180        let mut map = BTreeMap::new();
181        map.insert("a".to_string(), Ipld::Integer(0));
182        map.insert("b".to_string(), Ipld::Integer(1));
183        map.insert("c".to_string(), Ipld::Integer(2));
184        let ipld = Ipld::Map(map);
185        assert_eq!(ipld.get("a").unwrap(), &Ipld::Integer(0));
186    }
187}