1use serde::{Deserialize, Serialize};
5
6use super::ContentHash;
7
8#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
13pub struct Blob {
14 content: Vec<u8>,
15}
16
17impl Blob {
18 pub fn new(content: Vec<u8>) -> Self {
20 Self { content }
21 }
22
23 pub fn from_slice(content: &[u8]) -> Self {
25 Self {
26 content: content.to_vec(),
27 }
28 }
29
30 pub fn content(&self) -> &[u8] {
32 &self.content
33 }
34
35 pub fn content_str(&self) -> Option<&str> {
37 std::str::from_utf8(&self.content).ok()
38 }
39
40 pub fn into_content(self) -> Vec<u8> {
42 self.content
43 }
44
45 pub fn hash(&self) -> ContentHash {
47 ContentHash::compute_typed("blob", &self.content)
48 }
49
50 pub fn size(&self) -> usize {
52 self.content.len()
53 }
54
55 pub fn is_empty(&self) -> bool {
57 self.content.is_empty()
58 }
59}
60
61impl From<Vec<u8>> for Blob {
62 fn from(content: Vec<u8>) -> Self {
63 Self::new(content)
64 }
65}
66
67impl From<&[u8]> for Blob {
68 fn from(content: &[u8]) -> Self {
69 Self::from_slice(content)
70 }
71}
72
73impl From<String> for Blob {
74 fn from(content: String) -> Self {
75 Self::new(content.into_bytes())
76 }
77}
78
79impl From<&str> for Blob {
80 fn from(content: &str) -> Self {
81 Self::new(content.as_bytes().to_vec())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_blob_creation() {
91 let blob = Blob::new(b"hello world".to_vec());
92 assert_eq!(blob.content(), b"hello world");
93 assert_eq!(blob.size(), 11);
94 assert!(!blob.is_empty());
95 }
96
97 #[test]
98 fn test_blob_hash_deterministic() {
99 let blob1 = Blob::from("hello");
100 let blob2 = Blob::from("hello");
101 assert_eq!(blob1.hash(), blob2.hash());
102 }
103
104 #[test]
105 fn test_blob_hash_differs_for_different_content() {
106 let blob1 = Blob::from("hello");
107 let blob2 = Blob::from("world");
108 assert_ne!(blob1.hash(), blob2.hash());
109 }
110
111 #[test]
112 fn test_blob_content_str() {
113 let blob = Blob::from("hello");
114 assert_eq!(blob.content_str(), Some("hello"));
115
116 let binary_blob = Blob::new(vec![0xff, 0xfe]);
117 assert_eq!(binary_blob.content_str(), None);
118 }
119
120 #[test]
121 fn test_empty_blob() {
122 let blob = Blob::new(vec![]);
123 assert!(blob.is_empty());
124 assert_eq!(blob.size(), 0);
125 }
126}