1use crate::cid::Cid;
3use std::collections::BTreeMap;
4
5#[derive(Clone, Debug, PartialEq)]
7pub enum Ipld {
8 Null,
10 Bool(bool),
12 Integer(i128),
14 Float(f64),
16 String(String),
18 Bytes(Vec<u8>),
20 List(Vec<Ipld>),
22 Map(BTreeMap<String, Ipld>),
24 Link(Cid),
26}
27
28pub enum IpldIndex<'a> {
30 List(usize),
32 Map(String),
34 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 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 pub fn iter(&self) -> IpldIter<'_> {
75 IpldIter {
76 stack: vec![Box::new(vec![self].into_iter())],
77 }
78 }
79}
80
81pub 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), 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}