|
| 1 | +// SPDX-License-Identifier: Apache-2.0 |
| 2 | +use crate::{Block as TBlock, Error}; |
| 3 | +use async_trait::async_trait; |
| 4 | +use multibase::Base; |
| 5 | +use multicid::{cid, Cid, EncodedCid}; |
| 6 | +use multicodec::Codec; |
| 7 | +use multihash::mh; |
| 8 | +use multiutil::{BaseEncoder, DetectedEncoder, EncodingInfo}; |
| 9 | +use std::marker::Unpin; |
| 10 | +use tokio::io::{AsyncRead, AsyncReadExt}; |
| 11 | + |
| 12 | +/// The hash function we used when hashing blocks |
| 13 | +pub const BLOCK_HASH: Codec = Codec::Blake2B256; |
| 14 | + |
| 15 | +/// Filesystem stored block |
| 16 | +#[derive(Clone, Debug, PartialEq)] |
| 17 | +pub struct Block { |
| 18 | + /// The block cid |
| 19 | + pub key: EncodedCid, |
| 20 | + /// The block data |
| 21 | + pub data: Vec<u8>, |
| 22 | +} |
| 23 | + |
| 24 | +#[async_trait] |
| 25 | +impl<'a> TBlock<'a, EncodedCid> for Block { |
| 26 | + /// Return a reference to the data |
| 27 | + async fn data(&'a self) -> &'a [u8] { |
| 28 | + &self.data |
| 29 | + } |
| 30 | + |
| 31 | + /// Get the name of the block |
| 32 | + async fn key(&self) -> EncodedCid { |
| 33 | + self.key.clone() |
| 34 | + } |
| 35 | +} |
| 36 | + |
| 37 | +/// Builder for creating blocks from readers |
| 38 | +#[derive(Clone, Debug)] |
| 39 | +pub struct Builder<R: AsyncRead> { |
| 40 | + /// The base encoding |
| 41 | + base: Option<Base>, |
| 42 | + // The block cid |
| 43 | + cid: Option<Cid>, |
| 44 | + // The reader for the block data |
| 45 | + reader: R, |
| 46 | +} |
| 47 | + |
| 48 | +impl<R: AsyncRead + Unpin> Builder<R> { |
| 49 | + /// Create a new block builder |
| 50 | + pub fn new(reader: R) -> Self { |
| 51 | + Self { |
| 52 | + base: None, |
| 53 | + cid: None, |
| 54 | + reader, |
| 55 | + } |
| 56 | + } |
| 57 | + |
| 58 | + /// Set the base encoding |
| 59 | + pub fn base(mut self, base: Base) -> Self { |
| 60 | + self.base = Some(base); |
| 61 | + self |
| 62 | + } |
| 63 | + |
| 64 | + /// Set the cid |
| 65 | + pub fn cid(mut self, cid: Cid) -> Self { |
| 66 | + self.cid = Some(cid); |
| 67 | + self |
| 68 | + } |
| 69 | + |
| 70 | + /// Build the block |
| 71 | + pub async fn try_build(mut self) -> Result<Block, Error> { |
| 72 | + // get the base encoding |
| 73 | + let base = self.base.unwrap_or(DetectedEncoder::preferred_encoding( |
| 74 | + Cid::preferred_encoding(), |
| 75 | + )); |
| 76 | + |
| 77 | + // read the data from the reader |
| 78 | + let mut data = Vec::new(); |
| 79 | + self.reader.read_to_end(&mut data).await?; |
| 80 | + |
| 81 | + // calculate the cid from the data if not provided |
| 82 | + let cid = match self.cid { |
| 83 | + Some(cid) => cid, |
| 84 | + None => cid::Builder::new(Codec::Cidv1) |
| 85 | + .with_target_codec(Codec::DagCbor) |
| 86 | + .with_hash(&mh::Builder::new_from_bytes(BLOCK_HASH, &data)?.try_build()?) |
| 87 | + .try_build()?, |
| 88 | + }; |
| 89 | + |
| 90 | + // encode the cid into the key |
| 91 | + let key = EncodedCid::new(base, cid); |
| 92 | + |
| 93 | + Ok(Block { key, data }) |
| 94 | + } |
| 95 | +} |
0 commit comments