use serde::{Deserialize, Serialize};
use super::ContentHash;
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Blob {
content: Vec<u8>,
}
impl Blob {
pub fn new(content: Vec<u8>) -> Self {
Self { content }
}
pub fn from_slice(content: &[u8]) -> Self {
Self {
content: content.to_vec(),
}
}
pub fn content(&self) -> &[u8] {
&self.content
}
pub fn content_str(&self) -> Option<&str> {
std::str::from_utf8(&self.content).ok()
}
pub fn into_content(self) -> Vec<u8> {
self.content
}
pub fn hash(&self) -> ContentHash {
ContentHash::compute_typed("blob", &self.content)
}
pub fn size(&self) -> usize {
self.content.len()
}
pub fn is_empty(&self) -> bool {
self.content.is_empty()
}
}
impl From<Vec<u8>> for Blob {
fn from(content: Vec<u8>) -> Self {
Self::new(content)
}
}
impl From<&[u8]> for Blob {
fn from(content: &[u8]) -> Self {
Self::from_slice(content)
}
}
impl From<String> for Blob {
fn from(content: String) -> Self {
Self::new(content.into_bytes())
}
}
impl From<&str> for Blob {
fn from(content: &str) -> Self {
Self::new(content.as_bytes().to_vec())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_blob_creation() {
let blob = Blob::new(b"hello world".to_vec());
assert_eq!(blob.content(), b"hello world");
assert_eq!(blob.size(), 11);
assert!(!blob.is_empty());
}
#[test]
fn test_blob_hash_deterministic() {
let blob1 = Blob::from("hello");
let blob2 = Blob::from("hello");
assert_eq!(blob1.hash(), blob2.hash());
}
#[test]
fn test_blob_hash_differs_for_different_content() {
let blob1 = Blob::from("hello");
let blob2 = Blob::from("world");
assert_ne!(blob1.hash(), blob2.hash());
}
#[test]
fn test_blob_content_str() {
let blob = Blob::from("hello");
assert_eq!(blob.content_str(), Some("hello"));
let binary_blob = Blob::new(vec![0xff, 0xfe]);
assert_eq!(binary_blob.content_str(), None);
}
#[test]
fn test_empty_blob() {
let blob = Blob::new(vec![]);
assert!(blob.is_empty());
assert_eq!(blob.size(), 0);
}
}