1use bytes::Bytes;
2use core::hash::{Hash, Hasher};
3use ipld_core::cid::Cid;
4use ipld_core::codec::Codec;
5use ipld_core::ipld::Ipld;
6use ipld_core::serde::to_ipld;
7use multihash_codetable::Code;
8use multihash_derive::MultihashDigest;
9use std::fmt::{Debug, Formatter};
10
11#[derive(Clone)]
13pub struct Block {
14 cid: Cid,
15 data: Bytes,
16}
17impl Block {
18 pub fn new<D: Into<Bytes>>(cid: Cid, data: D) -> std::io::Result<Self> {
19 let block = Self::new_unchecked(cid, data);
20 block.verify()?;
21 Ok(block)
22 }
23
24 pub fn new_unchecked<D: Into<Bytes>>(cid: Cid, data: D) -> Self {
25 let data = data.into();
26 Self { cid, data }
27 }
28
29 pub fn cid(&self) -> &Cid {
30 &self.cid
31 }
32
33 pub fn data(&self) -> &[u8] {
34 &self.data
35 }
36
37 pub fn inner_data(&self) -> &Bytes {
38 &self.data
39 }
40
41 pub fn into_inner(self) -> (Cid, Bytes) {
42 (self.cid, self.data)
43 }
44
45 pub fn verify(&self) -> std::io::Result<()> {
46 let hash = Code::try_from(self.cid.hash().code())
47 .map_err(std::io::Error::other)?
48 .digest(&self.data);
49
50 if hash.digest() != self.cid.hash().digest() {
51 return Err(std::io::ErrorKind::InvalidData.into());
52 }
53
54 Ok(())
55 }
56
57 pub fn to_ipld(&self) -> std::io::Result<Ipld> {
58 let codec = BlockCodec::try_from(self.cid.codec())?;
59 let ipld = match codec {
60 BlockCodec::Raw => to_ipld(&self.data).map_err(std::io::Error::other)?,
61 BlockCodec::DagCbor => {
62 serde_ipld_dagcbor::codec::DagCborCodec::decode_from_slice(&self.data)
63 .map_err(std::io::Error::other)?
64 }
65 BlockCodec::DagJson => {
66 serde_ipld_dagjson::codec::DagJsonCodec::decode_from_slice(&self.data)
67 .map_err(std::io::Error::other)?
68 }
69 BlockCodec::DagPb => ipld_dagpb::to_ipld(&self.data).map_err(std::io::Error::other)?,
70 };
71 Ok(ipld)
72 }
73
74 pub fn references<E: Extend<Cid>>(&self, set: &mut E) -> std::io::Result<()> {
75 let ipld = self.to_ipld()?;
76 ipld.references(set);
77 Ok(())
78 }
79}
80
81impl Debug for Block {
82 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
83 f.debug_struct("Block")
84 .field("cid", &self.cid)
85 .field("data", &format!("{} bytes", self.data.len()))
86 .finish()
87 }
88}
89
90impl Hash for Block {
91 fn hash<H: Hasher>(&self, state: &mut H) {
92 Hash::hash(&self.cid, state)
93 }
94}
95
96impl PartialEq for Block {
97 fn eq(&self, other: &Self) -> bool {
98 self.cid.eq(&other.cid)
99 }
100}
101
102impl Eq for Block {}
103
104impl AsRef<[u8]> for Block {
105 fn as_ref(&self) -> &[u8] {
106 &self.data
107 }
108}
109
110#[derive(Clone, Copy, Debug, PartialEq, Eq)]
111pub enum BlockCodec {
112 Raw,
114 DagCbor,
116 DagJson,
118 DagPb,
120}
121
122impl From<BlockCodec> for u64 {
123 fn from(mc: BlockCodec) -> Self {
124 match mc {
125 BlockCodec::Raw => 0x55,
126 BlockCodec::DagCbor => 0x71,
127 BlockCodec::DagJson => 0x0129,
128 BlockCodec::DagPb => 0x70,
129 }
130 }
131}
132
133impl TryFrom<u64> for BlockCodec {
134 type Error = std::io::Error;
135
136 fn try_from(codec: u64) -> Result<Self, Self::Error> {
137 let codec = match codec {
138 0x55 => Self::Raw,
139 0x71 => Self::DagCbor,
140 0x0129 => Self::DagJson,
141 0x70 => Self::DagPb,
142 _ => return Err(std::io::ErrorKind::Unsupported.into()),
143 };
144 Ok(codec)
145 }
146}