yatima_core/
package.rs

1use core::fmt;
2
3use crate::{
4  ipld_error::IpldError,
5  meta::Meta,
6  name::Name,
7  position::Pos,
8};
9
10use sp_cid::Cid;
11use sp_ipld::{
12  dag_cbor::cid,
13  Ipld,
14};
15
16use sp_std::{
17  borrow::ToOwned,
18  vec::Vec,
19};
20
21use alloc::string::{
22  String,
23  ToString,
24};
25
26/// Namespace of definitions and imports
27#[derive(PartialEq, Clone, Debug)]
28pub struct Package {
29  pub pos: Pos,
30  pub name: Name,
31  pub imports: Vec<Import>,
32  pub index: Index,
33}
34
35/// Imported package
36#[derive(PartialEq, Clone, Debug)]
37pub struct Import {
38  pub cid: Cid,
39  pub name: Name,
40  pub alias: Name,
41  pub with: Vec<Name>,
42}
43
44/// Map of names to entries in a package
45#[derive(PartialEq, Clone, Debug)]
46pub struct Index(pub Vec<(Name, Cid)>);
47
48/// IPLD encoding of a def
49#[derive(PartialEq, Clone, Debug)]
50pub struct Entry {
51  pub pos: Pos,
52  pub type_anon: Cid,
53  pub term_anon: Cid,
54  pub type_meta: Meta,
55  pub term_meta: Meta,
56}
57
58impl Entry {
59  /// Converts an Entry into an IPLD object
60  pub fn to_ipld(&self) -> Ipld {
61    Ipld::List(vec![
62      self.pos.to_ipld(),
63      Ipld::Link(self.type_anon),
64      Ipld::Link(self.term_anon),
65      self.type_meta.to_ipld(),
66      self.term_meta.to_ipld(),
67    ])
68  }
69
70  /// Converts an IPLD object into an Entry
71  pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
72    match ipld {
73      Ipld::List(xs) => match xs.as_slice() {
74        #[rustfmt::skip]
75        [ pos,
76          Ipld::Link(type_anon),
77          Ipld::Link(term_anon),
78          type_meta,
79          term_meta,
80        ] => {
81          let pos = Pos::from_ipld(pos)?;
82          let type_meta = Meta::from_ipld(type_meta)?;
83          let term_meta = Meta::from_ipld(term_meta)?;
84          Ok(Entry {
85            pos,
86            type_anon: *type_anon,
87            term_anon: *term_anon,
88            type_meta,
89            term_meta
90            })
91        }
92        xs => Err(IpldError::Entry(Ipld::List(xs.to_owned()))),
93      },
94      xs => Err(IpldError::Entry(xs.to_owned())),
95    }
96  }
97
98  /// Generates a content id for the entry
99  pub fn cid(&self) -> Cid { cid(&self.to_ipld()) }
100}
101
102impl fmt::Display for Entry {
103  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
104    writeln!(f, "Entry")?;
105    writeln!(f, "  Type ({}):", self.type_anon)?;
106    writeln!(f, "  {}", self.type_meta)?;
107    writeln!(f, "  Term ({}):", self.term_anon)?;
108    writeln!(f, "  {}", self.term_meta)?;
109    Ok(())
110  }
111}
112
113impl Index {
114  /// Converts an Index into an IPLD object
115  pub fn to_ipld(&self) -> Ipld {
116    Ipld::List(
117      self
118        .0
119        .iter()
120        .map(|(k, v)| {
121          Ipld::List(vec![Ipld::String(k.to_string()), Ipld::Link(*v)])
122        })
123        .collect(),
124    )
125  }
126
127  /// Converts an IPLD object into an Index
128  pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
129    match ipld {
130      Ipld::List(xs) => {
131        let mut res: Vec<(Name, Cid)> = Vec::new();
132        for x in xs {
133          match x {
134            Ipld::List(xs) => match xs.as_slice() {
135              [Ipld::String(n), Ipld::Link(cid)] => {
136                res.push((Name::from(n.clone()), *cid));
137              }
138              xs => {
139                return Err(IpldError::IndexEntry(Ipld::List(xs.to_owned())));
140              }
141            },
142            x => {
143              return Err(IpldError::IndexEntry(x.to_owned()));
144            }
145          }
146        }
147        Ok(Index(res))
148      }
149      xs => Err(IpldError::Index(xs.to_owned())),
150    }
151  }
152
153  /// Returns a list of names in the index
154  pub fn keys(&self) -> Vec<Name> {
155    let mut res = Vec::new();
156    for (n, _) in &self.0 {
157      res.push(n.clone())
158    }
159    res
160  }
161}
162
163impl fmt::Display for Index {
164  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165    writeln!(f, "Exporting")?;
166    for (n, cid) in self.0.clone() {
167      writeln!(f, " {} ({})", n, cid)?;
168    }
169    Ok(())
170  }
171}
172
173impl Import {
174  /// Converts an import into an IPLD object
175  pub fn to_ipld(&self) -> Ipld {
176    Ipld::List(vec![
177      Ipld::Link(self.cid),
178      Ipld::String(self.name.to_string()),
179      Ipld::String(self.alias.to_string()),
180      Ipld::List(
181        self.with.iter().map(|x| Ipld::String(x.to_string())).collect(),
182      ),
183    ])
184  }
185
186  /// Converts an IPLD object into an import
187  pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
188    match ipld {
189      Ipld::List(xs) => match xs.as_slice() {
190        [Ipld::Link(cid), Ipld::String(name), Ipld::String(alias), Ipld::List(with)] =>
191        {
192          let mut res: Vec<String> = Vec::new();
193          for w in with {
194            match w {
195              Ipld::String(w) => {
196                res.push(w.clone());
197              }
198              w => return Err(IpldError::ImportEntry(w.to_owned())),
199            }
200          }
201          Ok(Self {
202            cid: *cid,
203            name: Name::from(name.clone()),
204            alias: Name::from(alias.clone()),
205            with: res.iter().cloned().map(Name::from).collect(),
206          })
207        }
208        xs => Err(IpldError::Import(Ipld::List(xs.to_owned()))),
209      },
210      xs => Err(IpldError::Import(xs.to_owned())),
211    }
212  }
213}
214
215impl fmt::Display for Import {
216  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
217    if self.alias.is_empty() {
218      writeln!(f, "Importing from {} ({})", self.name, self.cid)?;
219    }
220    else {
221      writeln!(
222        f,
223        "Importing from {} as {} ({})",
224        self.name, self.alias, self.cid
225      )?;
226    }
227    for with_ident in self.with.clone() {
228      writeln!(f, "  {}", with_ident)?;
229    }
230    Ok(())
231  }
232}
233
234/// Returns the local alias for an import
235pub fn import_alias(name: Name, import: &Import) -> Name {
236  if import.with.iter().any(|x| *x == name) {
237    if import.alias.is_empty() {
238      name
239    }
240    else {
241      Name::from(format!("{}.{}", import.alias, name))
242    }
243  }
244  else {
245    Name::from(format!("{}.{}", import.name, name))
246  }
247}
248
249impl Package {
250  /// Converts a package into an IPLD object
251  pub fn to_ipld(&self) -> Ipld {
252    Ipld::List(vec![
253      self.pos.to_ipld(),
254      Ipld::String(self.name.to_string()),
255      Ipld::List(self.imports.iter().map(Import::to_ipld).collect()),
256      self.index.to_ipld(),
257    ])
258  }
259
260  /// Converts an IPLD object into a package
261  pub fn from_ipld(ipld: &Ipld) -> Result<Self, IpldError> {
262    match ipld {
263      Ipld::List(xs) => match xs.as_slice() {
264        [pos, Ipld::String(name), Ipld::List(is), index] => {
265          let pos: Pos = Pos::from_ipld(pos)?;
266          let mut imports: Vec<Import> = Vec::new();
267          for i in is {
268            let i = Import::from_ipld(i)?;
269            imports.push(i);
270          }
271          let index = Index::from_ipld(index)?;
272          Ok(Package { pos, name: Name::from(name.clone()), imports, index })
273        }
274        xs => Err(IpldError::Package(Ipld::List(xs.to_owned()))),
275      },
276      xs => Err(IpldError::Package(xs.to_owned())),
277    }
278  }
279
280  /// Generates a content id for the package
281  pub fn cid(&self) -> Cid { cid(&self.to_ipld()) }
282}
283
284impl fmt::Display for Package {
285  fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
286    writeln!(f, "Package \"{}\"", self.name)?;
287    for i in self.imports.clone() {
288      writeln!(f, "{}", i)?;
289    }
290    writeln!(f, "{}", self.index)?;
291    Ok(())
292  }
293}
294
295#[cfg(test)]
296pub mod tests {
297  use super::*;
298  use quickcheck::{
299    Arbitrary,
300    Gen,
301  };
302
303  use crate::{
304    defs::tests::arbitrary_def,
305    term::tests::arbitrary_name,
306    tests::arbitrary_cid,
307  };
308
309  impl Arbitrary for Entry {
310    fn arbitrary(g: &mut Gen) -> Self { arbitrary_def(g).1 }
311  }
312
313  impl Arbitrary for Index {
314    fn arbitrary(g: &mut Gen) -> Self {
315      let vec: Vec<()> = Arbitrary::arbitrary(g);
316      Index(
317        vec
318          .into_iter()
319          .map(|_| (arbitrary_name(g), arbitrary_cid(g)))
320          .collect(),
321      )
322    }
323  }
324
325  impl Arbitrary for Import {
326    fn arbitrary(g: &mut Gen) -> Self {
327      let vec: Vec<()> = Arbitrary::arbitrary(g);
328      let vec: Vec<Name> = vec.into_iter().map(|_| arbitrary_name(g)).collect();
329      Self {
330        name: Name::from("Test"),
331        cid: arbitrary_cid(g),
332        alias: arbitrary_name(g),
333        with: vec,
334      }
335    }
336  }
337
338  impl Arbitrary for Package {
339    fn arbitrary(g: &mut Gen) -> Self {
340      Package {
341        pos: Pos::None,
342        name: arbitrary_name(g),
343        imports: Arbitrary::arbitrary(g),
344        index: Arbitrary::arbitrary(g),
345      }
346    }
347  }
348
349  #[quickcheck]
350  fn entry_ipld(x: Entry) -> bool {
351    match Entry::from_ipld(&x.to_ipld()) {
352      Ok(y) => x == y,
353      _ => false,
354    }
355  }
356  #[quickcheck]
357  fn index_ipld(x: Index) -> bool {
358    match Index::from_ipld(&x.to_ipld()) {
359      Ok(y) => x == y,
360      _ => false,
361    }
362  }
363  #[quickcheck]
364  fn import_ipld(x: Import) -> bool {
365    match Import::from_ipld(&x.to_ipld()) {
366      Ok(y) => x == y,
367      _ => false,
368    }
369  }
370  #[quickcheck]
371  fn package_ipld(x: Package) -> bool {
372    match Package::from_ipld(&x.to_ipld()) {
373      Ok(y) => x == y,
374      _ => false,
375    }
376  }
377}