summaryrefslogtreecommitdiffstats
path: root/lib/src/types/node.rs
blob: eb5679b45b10fd30bd085d86dfacbdba4a4438ce (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
use anyhow::Result;

use std::convert::TryFrom;

#[derive(Debug, Eq, PartialEq, getset::Getters)]
pub struct Node {
    /// Version
    #[getset(get = "pub")]
    version: String,

    /// Parent Nodes, identified by cid
    parents: Vec<ipfs::Cid>,

    /// The actual payload of the node, which is stored in another document identified by this cid
    payload: ipfs::Cid,
}

impl Into<ipfs::Ipld> for Node {
    fn into(self) -> ipfs::Ipld {
        let mut map = std::collections::BTreeMap::new();
        map.insert(String::from("version"), ipfs::Ipld::String(self.version));
        map.insert(String::from("parents"), ipfs::Ipld::List(self.parents.into_iter().map(ipfs::Ipld::Link).collect()));
        map.insert(String::from("payload"), ipfs::Ipld::Link(self.payload));
        ipfs::Ipld::Map(map)
    }
}

impl TryFrom<ipfs::Ipld> for Node {
    type Error = anyhow::Error;

    fn try_from(ipld: ipfs::Ipld) -> Result<Self> {
        let missing_field = |name: &'static str| move || anyhow::anyhow!("Missing field {}", name);
        let field_wrong_type = |name: &str, expty: &str| anyhow::bail!("Field {} has wrong type, expected {}", name, expty);
        match ipld {
            ipfs::Ipld::Map(map) => {
                let version = match map.get("version").ok_or_else(missing_field("version"))? {
                    ipfs::Ipld::String(s) => s.to_string(),
                    _ => return field_wrong_type("version", "String")
                };

                let parents = match map.get("parents").ok_or_else(missing_field("parents"))? {
                    ipfs::Ipld::List(s) => {
                        s.into_iter()
                            .map(|parent| -> Result<ipfs::Cid> {
                                match parent {
                                    ipfs::Ipld::Link(cid) => Ok(cid.clone()),
                                    _ => anyhow::bail!("Field in parents has wrong type, expected Link"),
                                }
                            })
                            .collect::<Result<Vec<ipfs::Cid>>>()?
                    },
                    _ => return field_wrong_type("parents", "Vec<Link>")
                };

                let payload = match map.get("payload").ok_or_else(missing_field("payload"))? {
                    ipfs::Ipld::Link(cid) => cid.clone(),
                    _ => return field_wrong_type("payload", "Link")
                };

                Ok(Node {
                    version,
                    parents,
                    payload
                })
            }

            _ => anyhow::bail!("Unexpected type, expected map")
        }
    }
}

impl Node {
    pub fn new(version: String, parents: Vec<ipfs::Cid>, payload: ipfs::Cid) -> Self {
        Self {
            version,
            parents,
            payload,
        }
    }

    pub fn parents(&self) -> Vec<ipfs::Cid> {
        self.parents.clone()
    }

    pub fn payload(&self) -> ipfs::Cid {
        self.payload.clone()
    }
}