1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
//! CBOR codec.
use libipld_base::cid;
pub use libipld_base::codec::Codec;
use libipld_base::error::BlockError;
pub use libipld_base::error::IpldError;
use libipld_base::ipld::Ipld;
use thiserror::Error;

pub mod decode;
pub mod encode;

pub use decode::ReadCbor;
pub use encode::WriteCbor;

/// CBOR codec.
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
pub struct DagCborCodec;

impl Codec for DagCborCodec {
    const VERSION: cid::Version = cid::Version::V1;
    const CODEC: cid::Codec = cid::Codec::DagCBOR;

    type Error = CborError;

    fn encode(ipld: &Ipld) -> Result<Box<[u8]>, Self::Error> {
        let mut bytes = Vec::new();
        ipld.write_cbor(&mut bytes)?;
        Ok(bytes.into_boxed_slice())
    }

    fn decode(mut data: &[u8]) -> Result<Ipld, Self::Error> {
        Ipld::read_cbor(&mut data)
    }
}

/// CBOR error.
#[derive(Debug, Error)]
pub enum CborError {
    /// Number larger than u64.
    #[error("Number larger than u64.")]
    NumberOutOfRange,
    /// Length larger than usize.
    #[error("Length out of range.")]
    LengthOutOfRange,
    /// Unexpected cbor code.
    #[error("Unexpected cbor code.")]
    UnexpectedCode,
    /// Unknown cbor tag.
    #[error("Unkown cbor tag.")]
    UnknownTag,
    /// Unexpected key.
    #[error("Wrong key.")]
    UnexpectedKey,
    /// Unexpected eof.
    #[error("Unexpected end of file.")]
    UnexpectedEof,
    /// Io error.
    #[error("{0}")]
    Io(#[from] std::io::Error),
    /// Utf8 error.
    #[error("{0}")]
    Utf8(#[from] std::str::Utf8Error),
    /// Cid error.
    #[error("{0}")]
    Cid(#[from] cid::Error),
    /// Ipld error.
    #[error("{0}")]
    Ipld(#[from] IpldError),
}

impl From<CborError> for BlockError {
    fn from(err: CborError) -> Self {
        Self::CodecError(err.into())
    }
}

/// CBOR result.
pub type CborResult<T> = Result<T, CborError>;

#[cfg(test)]
mod tests {
    use super::*;
    use libipld_base::cid::Cid;
    use libipld_macro::ipld;

    #[test]
    fn test_encode_decode_cbor() {
        let cid = Cid::new_v0(multihash::Sha2_256::digest(b"cid")).unwrap();
        let ipld = ipld!({
          "number": 1,
          "list": [true, null, false],
          "bytes": vec![0, 1, 2, 3],
          "map": { "float": 0.0, "string": "hello" },
          "link": cid,
        });
        let bytes = DagCborCodec::encode(&ipld).unwrap();
        let ipld2 = DagCborCodec::decode(&bytes).unwrap();
        assert_eq!(ipld, ipld2);
    }
}