use crate::error::Error;
use cid::multibase;
use cid::multihash;
use cid::multihash::Multihash;
use cid::CidGeneric;
use sha2::{Digest, Sha256};
use std::{fmt::Display, ops::Deref};
const RAW: u64 = 0x55;
const DIGEST_LEN: usize = 32;
const SHA2_256: u64 = 0x12;
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Attribute(cid::CidGeneric<DIGEST_LEN>);
impl Attribute {
pub fn new(val: impl AsRef<[u8]>) -> Self {
attribute(val)
}
pub fn hash(&self) -> &multihash::Multihash<DIGEST_LEN> {
self.0.hash()
}
pub fn digest(&self) -> &[u8] {
self.0.hash().digest()
}
pub fn cid(&self) -> &cid::CidGeneric<DIGEST_LEN> {
&self.0
}
pub fn to_string_of_base(
&self,
base: multibase::Base,
) -> core::result::Result<String, cid::Error> {
self.0.to_string_of_base(base)
}
pub fn from_cid(cid: &cid::CidGeneric<DIGEST_LEN>) -> Option<Self> {
if cid.codec() == RAW
&& cid.hash().code() == SHA2_256
&& cid.hash().digest().len() == DIGEST_LEN
{
Some(Attribute(*cid))
} else {
None
}
}
}
impl Display for Attribute {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub fn attribute(bytes: impl AsRef<[u8]>) -> Attribute {
let input_digest = Sha256::digest(bytes.as_ref());
let mhash = Multihash::<DIGEST_LEN>::wrap(SHA2_256, &input_digest).unwrap();
let cid = CidGeneric::<DIGEST_LEN>::new_v1(RAW, mhash);
Attribute(cid)
}
impl TryFrom<&cid::CidGeneric<DIGEST_LEN>> for Attribute {
type Error = &'static str;
fn try_from(cid: &cid::CidGeneric<DIGEST_LEN>) -> Result<Self, Self::Error> {
if cid.codec() == RAW
&& cid.hash().code() == SHA2_256
&& cid.hash().digest().len() == DIGEST_LEN
{
Ok(Attribute(*cid))
} else {
Err("Invalid Cid")
}
}
}
impl TryFrom<Vec<u8>> for Attribute {
type Error = Error;
fn try_from(bytes: Vec<u8>) -> Result<Self, Self::Error> {
let cid = CidGeneric::try_from(bytes)?;
Ok(Attribute(cid))
}
}
impl Deref for Attribute {
type Target = cid::CidGeneric<DIGEST_LEN>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<String> for Attribute {
fn from(s: String) -> Self {
Attribute::new(s)
}
}
impl From<&str> for Attribute {
fn from(s: &str) -> Self {
Attribute::new(s)
}
}
impl From<Attribute> for cid::CidGeneric<DIGEST_LEN> {
fn from(attribute: Attribute) -> Self {
attribute.0
}
}
impl From<Attribute> for Vec<u8> {
fn from(attribute: Attribute) -> Self {
attribute.0.to_bytes()
}
}
impl From<Attribute> for String {
fn from(attribute: Attribute) -> Self {
attribute.0.to_string()
}
}
impl From<Attribute> for multihash::Multihash<DIGEST_LEN> {
fn from(attribute: Attribute) -> Self {
multihash::Multihash::from_bytes(attribute.0.hash().digest())
.expect("correct length of digest for this multihash")
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_create_attribute() {
let some_test_attr = "read";
let read_attr = Attribute::new(some_test_attr); println!("read_attr: {:?}", read_attr);
let create_attr = Attribute::from(some_test_attr); let update_attr = attribute(some_test_attr);
let attr_from_cid = Attribute::try_from(read_attr.cid()).unwrap();
assert_eq!(read_attr, attr_from_cid);
let attr_from_cid = Attribute::from_cid(&read_attr).unwrap();
assert_eq!(read_attr, attr_from_cid);
assert_eq!(read_attr, create_attr);
assert_eq!(read_attr, update_attr);
assert_eq!(create_attr, update_attr);
}
}