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]);
}
}
|