File size: 3,346 Bytes
8ef2d83
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
//! # Blob
//!
//! Raw payload data attached to a point.
//!
//! ARMS doesn't interpret this data - it's yours.
//! Could be: tensor bytes, text, compressed state, anything.
//!
//! Separation of concerns:
//! - Point = WHERE (position in space)
//! - Blob = WHAT (the actual data)

/// Raw data attached to a point
///
/// ARMS stores this opaquely. You define what it means.
#[derive(Clone, Debug, PartialEq)]
pub struct Blob {
    data: Vec<u8>,
}

impl Blob {
    /// Create a new blob from bytes
    ///
    /// # Example
    /// ```
    /// use arms::Blob;
    /// let blob = Blob::new(vec![1, 2, 3, 4]);
    /// assert_eq!(blob.size(), 4);
    /// ```
    pub fn new(data: Vec<u8>) -> Self {
        Self { data }
    }

    /// Create an empty blob
    ///
    /// Useful when you only care about position, not payload.
    pub fn empty() -> Self {
        Self { data: vec![] }
    }

    /// Create a blob from a string (UTF-8 bytes)
    ///
    /// # Example
    /// ```
    /// use arms::Blob;
    /// let blob = Blob::from_str("hello");
    /// assert_eq!(blob.as_str(), Some("hello"));
    /// ```
    pub fn from_str(s: &str) -> Self {
        Self {
            data: s.as_bytes().to_vec(),
        }
    }

    /// Get the raw bytes
    pub fn data(&self) -> &[u8] {
        &self.data
    }

    /// Get the size in bytes
    pub fn size(&self) -> usize {
        self.data.len()
    }

    /// Check if the blob is empty
    pub fn is_empty(&self) -> bool {
        self.data.is_empty()
    }

    /// Try to interpret as UTF-8 string
    pub fn as_str(&self) -> Option<&str> {
        std::str::from_utf8(&self.data).ok()
    }

    /// Consume and return the inner data
    pub fn into_inner(self) -> Vec<u8> {
        self.data
    }
}

impl From<Vec<u8>> for Blob {
    fn from(data: Vec<u8>) -> Self {
        Self::new(data)
    }
}

impl From<&[u8]> for Blob {
    fn from(data: &[u8]) -> Self {
        Self::new(data.to_vec())
    }
}

impl From<&str> for Blob {
    fn from(s: &str) -> Self {
        Self::from_str(s)
    }
}

impl From<String> for Blob {
    fn from(s: String) -> Self {
        Self::new(s.into_bytes())
    }
}

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

    #[test]
    fn test_blob_new() {
        let blob = Blob::new(vec![1, 2, 3]);
        assert_eq!(blob.data(), &[1, 2, 3]);
        assert_eq!(blob.size(), 3);
    }

    #[test]
    fn test_blob_empty() {
        let blob = Blob::empty();
        assert!(blob.is_empty());
        assert_eq!(blob.size(), 0);
    }

    #[test]
    fn test_blob_from_str() {
        let blob = Blob::from_str("hello world");
        assert_eq!(blob.as_str(), Some("hello world"));
    }

    #[test]
    fn test_blob_as_str_invalid_utf8() {
        let blob = Blob::new(vec![0xff, 0xfe]);
        assert_eq!(blob.as_str(), None);
    }

    #[test]
    fn test_blob_from_conversions() {
        let blob1: Blob = vec![1, 2, 3].into();
        assert_eq!(blob1.size(), 3);

        let blob2: Blob = "test".into();
        assert_eq!(blob2.as_str(), Some("test"));

        let blob3: Blob = String::from("test").into();
        assert_eq!(blob3.as_str(), Some("test"));
    }

    #[test]
    fn test_blob_into_inner() {
        let blob = Blob::new(vec![1, 2, 3]);
        let data = blob.into_inner();
        assert_eq!(data, vec![1, 2, 3]);
    }
}