1use alloc::{
2 string::String,
3};
4use sp_cid::Cid;
5use alloc::{
6 borrow::ToOwned,
7 boxed::Box,
8 collections::btree_map::BTreeMap,
9 vec::Vec,
10};
11
12#[derive(Clone, PartialEq)]
14pub enum Ipld {
15 Null,
17 Bool(bool),
19 Integer(i128),
21 Float(f64),
23 String(String),
25 Bytes(Vec<u8>),
27 List(Vec<Ipld>),
29 StringMap(BTreeMap<String, Ipld>),
31 Link(Cid),
33}
34
35impl core::fmt::Debug for Ipld {
36 fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
37 use Ipld::*;
38 match self {
39 Null => write!(f, "null"),
40 Bool(b) => write!(f, "{:?}", b),
41 Integer(i) => write!(f, "{:?}", i),
42 Float(i) => write!(f, "{:?}", i),
43 String(s) => write!(f, "{:?}", s),
44 Bytes(b) => write!(f, "{:?}", b),
45 List(l) => write!(f, "{:?}", l),
46 StringMap(m) => write!(f, "{:?}", m),
47 Link(cid) => write!(f, "{}", cid),
48 }
49 }
50}
51
52impl Ipld {
53 pub fn iter(&self) -> IpldIter<'_> {
55 IpldIter { stack: vec![Box::new(vec![self].into_iter())] }
56 }
57
58 pub fn references<E: Extend<Cid>>(&self, set: &mut E) {
60 for ipld in self.iter() {
61 if let Ipld::Link(cid) = ipld {
62 set.extend(core::iter::once(cid.to_owned()));
63 }
64 }
65 }
66}
67
68impl<'a> Iterator for IpldIter<'a> {
69 type Item = &'a Ipld;
70
71 fn next(&mut self) -> Option<Self::Item> {
72 loop {
73 if let Some(iter) = self.stack.last_mut() {
74 if let Some(ipld) = iter.next() {
75 match ipld {
76 Ipld::List(list) => {
77 self.stack.push(Box::new(list.iter()));
78 }
79 Ipld::StringMap(map) => {
80 self.stack.push(Box::new(map.values()));
81 }
82 #[cfg(feature = "unleashed")]
83 Ipld::IntegerMap(map) => {
84 self.stack.push(Box::new(map.values()));
85 }
86 #[cfg(feature = "unleashed")]
87 Ipld::Tag(_, ipld) => {
88 self.stack.push(Box::new(ipld.iter()));
89 }
90 _ => {}
91 }
92 return Some(ipld);
93 }
94 else {
95 self.stack.pop();
96 }
97 }
98 else {
99 return None;
100 }
101 }
102 }
103}
104
105pub struct IpldIter<'a> {
107 stack: Vec<Box<dyn Iterator<Item = &'a Ipld> + 'a>>,
108}
109
110#[cfg(test)]
111pub mod tests {
112 use super::*;
113 use crate::rand::Rng;
114 use quickcheck::{
115 Arbitrary,
116 Gen,
117 };
118 use sp_multihash::{
119 Code,
120 MultihashDigest,
121 };
122 use alloc::boxed::Box;
123
124 pub(crate) fn arbitrary_cid(g: &mut Gen) -> Cid {
125 let mut bytes: [u8; 32] = [0; 32];
126 for x in bytes.iter_mut() {
127 *x = Arbitrary::arbitrary(g);
128 }
129 Cid::new_v1(0x55, Code::Blake2b256.digest(&bytes))
130 }
131
132 fn frequency<T, F: Fn(&mut Gen) -> T>(g: &mut Gen, gens: Vec<(i64, F)>) -> T {
133 if gens.iter().any(|(v, _)| *v < 0) {
134 panic!("Negative weight");
135 }
136 let sum: i64 = gens.iter().map(|x| x.0).sum();
137 let mut rng = rand::thread_rng();
138 let mut weight: i64 = rng.gen_range(1..=sum);
139 for gen in gens {
140 if weight - gen.0 <= 0 {
141 return gen.1(g);
142 }
143 else {
144 weight -= gen.0;
145 }
146 }
147 panic!("Calculation error for weight = {}", weight);
148 }
149
150 fn arbitrary_null() -> Box<dyn Fn(&mut Gen) -> Ipld> {
151 Box::new(move |_: &mut Gen| Ipld::Null)
152 }
153
154 fn arbitrary_bool() -> Box<dyn Fn(&mut Gen) -> Ipld> {
155 Box::new(move |g: &mut Gen| Ipld::Bool(Arbitrary::arbitrary(g)))
156 }
157
158 fn arbitrary_link() -> Box<dyn Fn(&mut Gen) -> Ipld> {
159 Box::new(move |g: &mut Gen| Ipld::Link(arbitrary_cid(g)))
160 }
161
162 pub fn arbitrary_i128() -> Box<dyn Fn(&mut Gen) -> i128> {
163 Box::new(move |g: &mut Gen| i64::arbitrary(g) as i128)
164 }
165
166 pub fn arbitrary_integer() -> Box<dyn Fn(&mut Gen) -> Ipld> {
167 Box::new(move |g: &mut Gen| Ipld::Integer(arbitrary_i128()(g)))
168 }
169
170 fn arbitrary_string() -> Box<dyn Fn(&mut Gen) -> Ipld> {
171 Box::new(move |g: &mut Gen| Ipld::String(Arbitrary::arbitrary(g)))
172 }
173
174 fn arbitrary_bytes() -> Box<dyn Fn(&mut Gen) -> Ipld> {
175 Box::new(move |g: &mut Gen| Ipld::Bytes(Arbitrary::arbitrary(g)))
176 }
177
178 pub fn arbitrary_list() -> Box<dyn Fn(&mut Gen) -> Ipld> {
179 Box::new(move |g: &mut Gen| {
180 let mut rng = rand::thread_rng();
181 let size = rng.gen_range(0..5);
182 Ipld::List((0..size).map(|_| Arbitrary::arbitrary(g)).collect())
183 })
184 }
185
186 pub fn arbitrary_stringmap() -> Box<dyn Fn(&mut Gen) -> Ipld> {
187 Box::new(move |g: &mut Gen| {
188 let mut rng = rand::thread_rng();
189 let size = rng.gen_range(0..5);
190 Ipld::StringMap((0..size).map(|_| Arbitrary::arbitrary(g)).collect())
191 })
192 }
193
194 impl Arbitrary for Ipld {
195 fn arbitrary(g: &mut Gen) -> Self {
196 frequency(g, vec![
197 (100, arbitrary_null()),
198 (100, arbitrary_bool()),
199 (100, arbitrary_link()),
200 (100, arbitrary_integer()),
201 (100, arbitrary_string()),
202 (100, arbitrary_bytes()),
203 (30, arbitrary_list()),
204 (30, arbitrary_stringmap()),
205 ])
206 }
207 }
208}