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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
use blake2::Blake2bVar;
use bytes::Bytes;
use data_encoding::BASE64;
use digest::{Update, VariableOutput};
use serde::de::{self, Deserialize, Deserializer};
use serde::ser::{Serialize, Serializer};
use std::result;

#[derive(Debug, Clone, PartialEq)]
pub struct Blob {
    pub id: String,
    pub bytes: Bytes,
}

impl Blob {
    pub fn create(bytes: Bytes) -> Blob {
        let id = Self::hash(&bytes);
        Blob { id, bytes }
    }

    pub fn hash(bytes: &[u8]) -> String {
        let mut h = Blake2bVar::new(32).unwrap();
        h.update(bytes);
        let output = h.finalize_boxed();
        Self::encode_hash(&output)
    }

    #[inline]
    fn encode_hash(bytes: &[u8]) -> String {
        let x = bs58::encode(bytes).into_string();
        format!("{:0<44}", x)
    }
}

impl Serialize for Blob {
    #[inline]
    fn serialize<S>(&self, serializer: S) -> result::Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        let s = BASE64.encode(&self.bytes);
        serializer.serialize_str(&s)
    }
}

impl<'de> Deserialize<'de> for Blob {
    #[inline]
    fn deserialize<D>(deserializer: D) -> result::Result<Self, D::Error>
    where
        D: Deserializer<'de>,
    {
        let s = String::deserialize(deserializer)?;
        let bytes = BASE64.decode(s.as_bytes()).map_err(de::Error::custom)?;
        Ok(Blob::create(Bytes::from(bytes)))
    }
}

pub trait BlobState {
    fn register_blob(&self, blob: Blob) -> String;
}

#[cfg(test)]
mod tests {
    use super::*;
    use serde_json;

    #[inline]
    fn blob() -> (Bytes, Blob) {
        let bytes = Bytes::from(&b"asdf"[..]);
        (bytes.clone(), Blob::create(bytes))
    }

    #[test]
    fn verify_create_blob() {
        let (bytes, blob) = blob();
        assert_eq!(
            blob,
            Blob {
                id: String::from("DTTV3EjpHBNJx3Zw7eJsVPm4bYXKmNkJQpVNkcvTtTSz"),
                bytes,
            }
        );
    }

    #[test]
    fn test_serialize() {
        let (_, blob) = blob();
        let json = serde_json::to_string(&blob).unwrap();
        assert_eq!(&json, "\"YXNkZg==\"");
    }

    #[test]
    fn test_deserialize() {
        let (_, blob1) = blob();
        let blob2: Blob = serde_json::from_str("\"YXNkZg==\"").unwrap();
        assert_eq!(blob1, blob2);
    }

    #[test]
    fn test_hash_encoding() {
        let x = bs58::decode("22es54J4FbFtpb5D1MtBazVuum4TcqCQ7M9JkmYdmJ8W")
            .into_vec()
            .unwrap();
        let x = Blob::encode_hash(&x);
        assert_eq!(x.len(), 44);
        assert_eq!(x, "22es54J4FbFtpb5D1MtBazVuum4TcqCQ7M9JkmYdmJ8W");
    }

    #[test]
    fn test_hash_encoding_padding() {
        let x = bs58::decode("r6edvU326yvpXLubYacXXSxf2HzqCgzqHUQvpWyNwei")
            .into_vec()
            .unwrap();
        let x = Blob::encode_hash(&x);
        assert_eq!(x.len(), 44);
        assert_eq!(x, "r6edvU326yvpXLubYacXXSxf2HzqCgzqHUQvpWyNwei0");
    }
}