sp_ipld/
ipld.rs

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/// IPLD data format
13#[derive(Clone, PartialEq)]
14pub enum Ipld {
15  /// Represents the absence of a value or the value undefined.
16  Null,
17  /// Represents a boolean value.
18  Bool(bool),
19  /// Represents an integer.
20  Integer(i128),
21  /// Represents a floating point value.
22  Float(f64),
23  /// Represents an UTF-8 string.
24  String(String),
25  /// Represents a sequence of bytes.
26  Bytes(Vec<u8>),
27  /// Represents a list.
28  List(Vec<Ipld>),
29  /// Represents a map of strings.
30  StringMap(BTreeMap<String, Ipld>),
31  /// Represents a link to an Ipld node.
32  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  /// Returns an iterator.
54  pub fn iter(&self) -> IpldIter<'_> {
55    IpldIter { stack: vec![Box::new(vec![self].into_iter())] }
56  }
57
58  /// Returns the references to other blocks.
59  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
105/// Ipld iterator.
106pub 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}