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}