ipfs-embed 0.3.1

small embeddable ipfs implementation
Documentation
use core::convert::TryFrom;
use libipld::cid::Cid;
use sled::IVec;
use std::collections::HashSet;

#[derive(Clone, Copy, Debug)]
#[repr(u8)]
pub enum Key {
    Block,
    Pin,
    Alias,
    Public,
    Want,
    Refs,
    Refer,
}

impl Key {
    pub fn prefix(self) -> IVec {
        (&[self as u8]).into()
    }

    fn byte_key(self, bytes: &[u8]) -> IVec {
        let mut key = Vec::with_capacity(bytes.len() + 1);
        key.push(self as u8);
        key.extend_from_slice(bytes);
        key.into()
    }

    fn cid_key(self, cid: &Cid) -> IVec {
        self.byte_key(&cid.to_bytes())
    }

    pub fn block(cid: &Cid) -> IVec {
        Self::Block.cid_key(cid)
    }

    pub fn pin(cid: &Cid) -> IVec {
        Self::Pin.cid_key(cid)
    }

    pub fn alias(bytes: &[u8]) -> IVec {
        Self::Alias.byte_key(bytes)
    }

    pub fn public(cid: &Cid) -> IVec {
        Self::Public.cid_key(cid)
    }

    pub fn want(cid: &Cid) -> IVec {
        Self::Want.cid_key(cid)
    }

    pub fn refs(cid: &Cid) -> IVec {
        Self::Refs.cid_key(cid)
    }

    pub fn refer(cid: &Cid) -> IVec {
        Self::Refer.cid_key(cid)
    }
}

#[derive(Clone, Debug)]
pub struct Value(IVec);

impl From<Value> for IVec {
    fn from(value: Value) -> Self {
        value.0
    }
}

impl From<IVec> for Value {
    fn from(ivec: IVec) -> Self {
        Self(ivec)
    }
}

impl From<bool> for Value {
    fn from(flag: bool) -> Self {
        if flag {
            Self(IVec::from(&[]))
        } else {
            panic!("remove the key instead");
        }
    }
}

impl From<Value> for bool {
    fn from(_: Value) -> bool {
        true
    }
}

impl From<u32> for Value {
    fn from(n: u32) -> Self {
        let bytes = n.to_le_bytes();
        Self(IVec::from(&bytes[..]))
    }
}

impl From<Value> for u32 {
    fn from(value: Value) -> Self {
        let mut buf = [0u8; 4];
        buf.copy_from_slice(&value.0);
        u32::from_le_bytes(buf)
    }
}

impl From<Cid> for Value {
    fn from(cid: Cid) -> Self {
        Self(cid.to_bytes().into())
    }
}

impl From<Value> for Cid {
    fn from(value: Value) -> Self {
        Cid::try_from(value.0.as_ref()).expect("valid cid")
    }
}

impl<'a> From<&'a HashSet<Cid>> for Value {
    fn from(refs: &'a HashSet<Cid>) -> Self {
        let mut buf = vec![];
        buf.extend(&(refs.len() as u64).to_le_bytes());
        for cid in refs.iter() {
            let bytes = cid.to_bytes();
            buf.extend(&[bytes.len() as u8]);
            buf.extend(bytes);
        }
        Self(buf.into())
    }
}

impl From<Value> for HashSet<Cid> {
    fn from(value: Value) -> Self {
        let buf = value.0.as_ref();
        let mut len_buf = [0u8; 8];
        len_buf.copy_from_slice(&buf[..8]);
        let len = u64::from_le_bytes(len_buf);
        let mut refs = HashSet::with_capacity(len as _);
        let mut pos = 8;
        for _ in 0..len {
            let len = buf[pos] as usize;
            pos += 1;
            let cid = Cid::try_from(&buf[pos..(pos + len)]).expect("valid refs");
            pos += len;
            refs.insert(cid);
        }
        refs
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use libipld::cid::Codec;
    use libipld::multihash::Sha2_256;

    fn create_block(bytes: &[u8]) -> (Cid, IVec) {
        let hash = Sha2_256::digest(&bytes);
        let cid = Cid::new_v1(Codec::Raw, hash);
        (cid, bytes.into())
    }

    #[test]
    fn test_key_prefix() {
        assert_eq!(&Key::Block.prefix(), &[0]);
        assert_eq!(&Key::Want.prefix(), &[4]);
    }

    #[test]
    fn test_refs() {
        let (cid1, _) = create_block(b"a");
        let (cid2, _) = create_block(b"b");
        let (cid3, _) = create_block(b"c");
        let mut refs = HashSet::new();
        refs.insert(cid1);
        refs.insert(cid2);
        refs.insert(cid3);
        let value = Value::from(&refs);
        let refs2 = HashSet::from(value);
        assert_eq!(refs, refs2);
    }
}