rust_ipfs/
block.rs

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/// Container housing the cid and bytes of data
12#[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 codec.
113    Raw,
114    /// Cbor codec.
115    DagCbor,
116    /// Json codec.
117    DagJson,
118    /// Protobuf codec.
119    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}