1use core::convert::{TryFrom, TryInto};
2use std::collections::BTreeMap;
3
4use bytes::Bytes;
5use ipld_core::{cid::Cid, ipld::Ipld};
6use quick_protobuf::sizeofs::{sizeof_len, sizeof_varint};
7use quick_protobuf::{BytesReader, MessageRead, MessageWrite, Writer, WriterBackend};
8
9use crate::Error;
10
11#[derive(Debug, PartialEq, Eq, Clone)]
13pub struct PbLink {
14 pub cid: Cid,
16 pub name: Option<String>,
18 pub size: Option<u64>,
20}
21
22#[derive(Debug, PartialEq, Eq, Clone, Default)]
24pub struct PbNode {
25 pub links: Vec<PbLink>,
27 pub data: Option<Bytes>,
29}
30
31#[derive(Debug, PartialEq, Eq, Clone, Default)]
33pub(crate) struct PbNodeRef<'a> {
34 links: Vec<PbLink>,
35 data: Option<&'a [u8]>,
36}
37
38impl PbNode {
39 pub fn from_bytes(buf: Bytes) -> Result<Self, Error> {
41 let mut reader = BytesReader::from_bytes(&buf);
42 let node = PbNodeRef::from_reader(&mut reader, &buf)?;
43 let data = node.data.map(|d| buf.slice_ref(d));
44
45 Ok(PbNode {
46 links: node.links,
47 data,
48 })
49 }
50
51 pub fn into_bytes(mut self) -> Vec<u8> {
53 self.links.sort_by(|a, b| {
56 let a = a.name.as_ref().map(|s| s.as_bytes()).unwrap_or(&[][..]);
57 let b = b.name.as_ref().map(|s| s.as_bytes()).unwrap_or(&[][..]);
58 a.cmp(b)
59 });
60
61 let mut buf = Vec::with_capacity(self.get_size());
62 let mut writer = Writer::new(&mut buf);
63 self.write_message(&mut writer)
64 .expect("protobuf to be valid");
65 buf
66 }
67}
68
69impl PbNodeRef<'_> {
70 pub fn into_bytes(mut self) -> Vec<u8> {
72 self.links.sort_by(|a, b| {
75 let a = a.name.as_ref().map(|s| s.as_bytes()).unwrap_or(&[][..]);
76 let b = b.name.as_ref().map(|s| s.as_bytes()).unwrap_or(&[][..]);
77 a.cmp(b)
78 });
79
80 let mut buf = Vec::with_capacity(self.get_size());
81 let mut writer = Writer::new(&mut buf);
82 self.write_message(&mut writer)
83 .expect("protobuf to be valid");
84 buf
85 }
86}
87
88impl From<PbNode> for Ipld {
89 fn from(node: PbNode) -> Self {
90 let mut map = BTreeMap::<String, Ipld>::new();
91 let links = node
92 .links
93 .into_iter()
94 .map(|link| link.into())
95 .collect::<Vec<Ipld>>();
96 map.insert("Links".to_string(), links.into());
97 if let Some(data) = node.data {
98 map.insert("Data".to_string(), Ipld::Bytes(data.to_vec()));
99 }
100 map.into()
101 }
102}
103
104impl From<PbLink> for Ipld {
105 fn from(link: PbLink) -> Self {
106 let mut map = BTreeMap::<String, Ipld>::new();
107 map.insert("Hash".to_string(), link.cid.into());
108
109 if let Some(name) = link.name {
110 map.insert("Name".to_string(), name.into());
111 }
112 if let Some(size) = link.size {
113 map.insert("Tsize".to_string(), size.into());
114 }
115 map.into()
116 }
117}
118
119impl<'a> TryFrom<&'a Ipld> for PbNodeRef<'a> {
120 type Error = Error;
121
122 fn try_from(ipld: &'a Ipld) -> core::result::Result<Self, Self::Error> {
123 let mut node = PbNodeRef::default();
124
125 match ipld {
126 Ipld::Map(map) => {
127 if map.is_empty() {
128 return Err(Error::FromIpld(
129 "DAG-PB must contain links or data".to_string(),
130 ));
131 }
132
133 for (key, value) in map {
134 match (key.as_str(), value) {
135 ("Links", Ipld::List(links)) => {
136 let mut prev_name = "".to_string();
137 for link in links.iter() {
138 match link {
139 Ipld::Map(_) => {
140 let pb_link: PbLink = link.try_into()?;
141 if let Some(ref name) = pb_link.name {
143 if name.as_bytes() < prev_name.as_bytes() {
144 return Err(Error::LinksWrongOrder);
147 }
148 prev_name.clone_from(name)
149 }
150 node.links.push(pb_link)
151 }
152 other => {
153 return Err(Error::FromIpld(format!(
154 "Link entries must be an IPLD map, found: {:?}",
155 other
156 )))
157 }
158 }
159 }
160 }
161 ("Data", Ipld::Bytes(data)) => {
162 node.data = Some(&data[..]);
163 }
164 (_, _) => {
165 return Err(Error::FromIpld(
166 "IPLD cannot be converted into DAG-PB".to_string(),
167 ))
168 }
169 }
170 }
171 }
172 other => {
173 return Err(Error::FromIpld(format!(
174 "Node must be an IPLD map, found: {:?}",
175 other
176 )))
177 }
178 }
179
180 Ok(node)
181 }
182}
183
184impl TryFrom<&Ipld> for PbLink {
185 type Error = Error;
186
187 fn try_from(ipld: &Ipld) -> core::result::Result<PbLink, Self::Error> {
188 if let Ipld::Map(map) = ipld {
189 let mut cid = None;
190 let mut name = None;
191 let mut size = None;
192 for (key, value) in map {
193 match key.as_str() {
194 "Hash" => {
195 cid = if let Ipld::Link(cid) = value {
196 Some(*cid)
197 } else {
198 return Err(Error::FromIpld(format!(
199 "`Hash` must be an IPLD link, found: {:?}",
200 value
201 )));
202 };
203 }
204 "Name" => {
205 name = if let Ipld::String(name) = value {
206 Some(name.clone())
207 } else {
208 return Err(Error::FromIpld(format!(
209 "`Name` must be an IPLD string, found: {:?}",
210 value
211 )));
212 }
213 }
214 "Tsize" => {
215 size = if let Ipld::Integer(size) = value {
216 Some(u64::try_from(*size).map_err(|_| {
217 Error::FromIpld(
218 "`Tsize` must fit into a 64-bit integer".to_string(),
219 )
220 })?)
221 } else {
222 return Err(Error::FromIpld(format!(
223 "`Tsize` must be an IPLD integer, found: {:?}",
224 value
225 )))?;
226 }
227 }
228 other => {
229 return Err(Error::FromIpld(format!(
230 "Only `Hash`, `Name` and `Tsize` are allowed as keys, found: `{}`",
231 other
232 )));
233 }
234 }
235 }
236
237 match cid {
239 Some(cid) => Ok(PbLink { cid, name, size }),
240 None => Err(Error::FromIpld("`Hash` must be set".to_string())),
241 }
242 } else {
243 Err(Error::FromIpld("Links must be an IPLD map".to_string()))
244 }
245 }
246}
247
248impl<'a> MessageRead<'a> for PbLink {
249 fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> quick_protobuf::Result<Self> {
250 let mut cid = None;
251 let mut name = None;
252 let mut size = None;
253
254 while !r.is_eof() {
255 match r.next_tag(bytes) {
256 Ok(10) => {
257 let bytes = r.read_bytes(bytes)?;
258 cid = Some(
259 Cid::try_from(bytes)
260 .map_err(|e| quick_protobuf::Error::Message(e.to_string()))?,
261 );
262 }
263 Ok(18) => name = Some(r.read_string(bytes)?.to_string()),
264 Ok(24) => size = Some(r.read_uint64(bytes)?),
265 Ok(_) => {
266 return Err(quick_protobuf::Error::Message(
267 "unexpected bytes".to_string(),
268 ))
269 }
270 Err(e) => return Err(e),
271 }
272 }
273 Ok(PbLink {
274 cid: cid.ok_or_else(|| quick_protobuf::Error::Message("missing Hash".into()))?,
275 name,
276 size,
277 })
278 }
279}
280
281impl MessageWrite for PbLink {
282 fn get_size(&self) -> usize {
283 let mut size = 0;
284 let l = self.cid.encoded_len();
285 size += 1 + sizeof_len(l);
286
287 if let Some(ref name) = self.name {
288 size += 1 + sizeof_len(name.len());
289 }
290
291 if let Some(tsize) = self.size {
292 size += 1 + sizeof_varint(tsize);
293 }
294 size
295 }
296
297 fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
298 let bytes = self.cid.to_bytes();
299 w.write_with_tag(10, |w| w.write_bytes(&bytes))?;
300
301 if let Some(ref name) = self.name {
302 w.write_with_tag(18, |w| w.write_string(name))?;
303 }
304 if let Some(size) = self.size {
305 w.write_with_tag(24, |w| w.write_uint64(size))?;
306 }
307 Ok(())
308 }
309}
310
311impl<'a> MessageRead<'a> for PbNodeRef<'a> {
312 fn from_reader(r: &mut BytesReader, bytes: &'a [u8]) -> quick_protobuf::Result<Self> {
313 let mut msg = Self::default();
314 let mut links_before_data = false;
315 while !r.is_eof() {
316 match r.next_tag(bytes) {
317 Ok(18) => {
318 if links_before_data {
320 return Err(quick_protobuf::Error::Message(
321 "duplicate Links section".to_string(),
322 ));
323 }
324 msg.links.push(r.read_message::<PbLink>(bytes)?)
325 }
326 Ok(10) => {
327 msg.data = Some(r.read_bytes(bytes)?);
328 if !msg.links.is_empty() {
329 links_before_data = true
330 }
331 }
332 Ok(_) => {
333 return Err(quick_protobuf::Error::Message(
334 "unexpected bytes".to_string(),
335 ))
336 }
337 Err(e) => return Err(e),
338 }
339 }
340 Ok(msg)
341 }
342}
343
344impl MessageWrite for PbNode {
345 fn get_size(&self) -> usize {
346 let mut size = 0;
347 if let Some(ref data) = self.data {
348 size += 1 + sizeof_len(data.len());
349 }
350
351 size += self
352 .links
353 .iter()
354 .map(|s| 1 + sizeof_len((s).get_size()))
355 .sum::<usize>();
356
357 size
358 }
359
360 fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
361 for s in &self.links {
362 w.write_with_tag(18, |w| w.write_message(s))?;
363 }
364
365 if let Some(ref data) = self.data {
366 w.write_with_tag(10, |w| w.write_bytes(data))?;
367 }
368
369 Ok(())
370 }
371}
372
373impl MessageWrite for PbNodeRef<'_> {
374 fn get_size(&self) -> usize {
375 let mut size = 0;
376 if let Some(data) = self.data {
377 size += 1 + sizeof_len(data.len());
378 }
379
380 size += self
381 .links
382 .iter()
383 .map(|s| 1 + sizeof_len((s).get_size()))
384 .sum::<usize>();
385
386 size
387 }
388
389 fn write_message<W: WriterBackend>(&self, w: &mut Writer<W>) -> quick_protobuf::Result<()> {
390 for s in &self.links {
391 w.write_with_tag(18, |w| w.write_message(s))?;
392 }
393
394 if let Some(data) = self.data {
395 w.write_with_tag(10, |w| w.write_bytes(data))?;
396 }
397
398 Ok(())
399 }
400}