dag_pb/
codec.rs

1use crate::ProtobufError as Error;
2use core::convert::{TryFrom, TryInto};
3use libipld_base::cid::Cid;
4use libipld_base::error::IpldError;
5use libipld_base::ipld::Ipld;
6use std::collections::BTreeMap;
7
8mod dag_pb {
9    include!(concat!(env!("OUT_DIR"), "/dag_pb.rs"));
10}
11
12pub struct PbLink {
13    pub cid: Cid,
14    pub name: String,
15    pub size: u64,
16}
17
18pub struct PbNode {
19    pub links: Vec<PbLink>,
20    pub data: Vec<u8>,
21}
22
23use prost::Message;
24
25impl PbNode {
26    pub fn from_bytes(bytes: &[u8]) -> Result<Self, Error> {
27        let proto: dag_pb::PbNode = dag_pb::PbNode::decode(bytes)?;
28        let data = proto.data;
29        let mut links = Vec::new();
30        for link in proto.links {
31            let cid = Cid::try_from(link.hash)?;
32            let name = link.name;
33            let size = link.tsize;
34            links.push(PbLink { cid, name, size });
35        }
36        Ok(PbNode { links, data })
37    }
38
39    pub fn into_bytes(self) -> Box<[u8]> {
40        let links = self
41            .links
42            .into_iter()
43            .map(|link| dag_pb::PbLink {
44                hash: link.cid.to_bytes(),
45                name: link.name,
46                tsize: link.size,
47            })
48            .collect::<Vec<_>>();
49        let proto = dag_pb::PbNode {
50            data: self.data,
51            links,
52        };
53
54        let mut res = Vec::with_capacity(proto.encoded_len());
55        proto
56            .encode(&mut res)
57            .expect("there is no situation in which the protobuf message can be invalid");
58        res.into_boxed_slice()
59    }
60}
61
62impl Into<Ipld> for PbNode {
63    fn into(self) -> Ipld {
64        let mut map = BTreeMap::<String, Ipld>::new();
65        let links = self
66            .links
67            .into_iter()
68            .map(|link| link.into())
69            .collect::<Vec<Ipld>>();
70        map.insert("Links".to_string(), links.into());
71        map.insert("Data".to_string(), self.data.into());
72        map.into()
73    }
74}
75
76impl Into<Ipld> for PbLink {
77    fn into(self) -> Ipld {
78        let mut map = BTreeMap::<String, Ipld>::new();
79        map.insert("Hash".to_string(), self.cid.into());
80        map.insert("Name".to_string(), self.name.into());
81        map.insert("Tsize".to_string(), self.size.into());
82        map.into()
83    }
84}
85
86impl TryFrom<&Ipld> for PbNode {
87    type Error = IpldError;
88
89    fn try_from(ipld: &Ipld) -> Result<PbNode, Self::Error> {
90        let links = if let Ipld::List(links) = ipld.get("Links").ok_or(IpldError::KeyNotFound)? {
91            links
92                .iter()
93                .map(|link| link.try_into())
94                .collect::<Result<_, _>>()?
95        } else {
96            return Err(IpldError::NotList);
97        };
98        let data = if let Ipld::Bytes(data) = ipld.get("Data").ok_or(IpldError::KeyNotFound)? {
99            data.clone()
100        } else {
101            return Err(IpldError::NotBytes);
102        };
103        Ok(PbNode { links, data })
104    }
105}
106
107impl TryFrom<&Ipld> for PbLink {
108    type Error = IpldError;
109
110    fn try_from(ipld: &Ipld) -> Result<PbLink, Self::Error> {
111        let cid = if let Ipld::Link(cid) = ipld.get("Hash").ok_or(IpldError::KeyNotFound)? {
112            cid.clone()
113        } else {
114            return Err(IpldError::NotLink);
115        };
116        let name = if let Ipld::String(name) = ipld.get("Name").ok_or(IpldError::KeyNotFound)? {
117            name.clone()
118        } else {
119            return Err(IpldError::NotString);
120        };
121        let size = if let Ipld::Integer(size) = ipld.get("Tsize").ok_or(IpldError::KeyNotFound)? {
122            *size as u64
123        } else {
124            return Err(IpldError::NotInteger);
125        };
126        Ok(PbLink { cid, name, size })
127    }
128}