git_internal/internal/object/blob.rs
1//! In Git, a blob (binary large object) is a type of Git object that stores the contents of a file.
2//! A blob object contains the binary data of a file, but does not contain any metadata such as
3//! the file name or permissions. The structure of a Git blob object is as follows:
4//!
5//! ```bash
6//! blob <content-length>\0<content>
7//! ```
8//!
9//! - `blob` is the object type, indicating that this is a blob object.
10//! - `<content-length>` is the length of the content in bytes, encoded as a string of decimal digits.
11//! - `\0` is a null byte, which separates the header from the content.
12//! - `<content>` is the binary data of the file, represented as a sequence of bytes.
13//!
14//! We can create a Git blob object for this file by running the following command:
15//!
16//! ```bash
17//! $ echo "Hello, world!" | git hash-object -w --stdin
18//! ```
19//!
20//! This will output an SHA-1 hash, which is the ID of the newly created blob object.
21//! The contents of the blob object would look something like this:
22//!
23//! ```bash
24//! blob 13\0Hello, world!
25//! ```
26//! Git uses blobs to store the contents of files in a repository. Each version of a file is
27//! represented by a separate blob object, which can be linked together using Git's commit and tree
28//! objects to form a version history of the repository.
29//!
30use std::fmt::Display;
31
32use crate::errors::GitError;
33use crate::hash::SHA1;
34use crate::internal::object::ObjectTrait;
35use crate::internal::object::types::ObjectType;
36
37/// **The Blob Object**
38#[derive(Eq, Debug, Clone)]
39pub struct Blob {
40 pub id: SHA1,
41 pub data: Vec<u8>,
42}
43
44impl PartialEq for Blob {
45 /// The Blob object is equal to another Blob object if their IDs are equal.
46 fn eq(&self, other: &Self) -> bool {
47 self.id == other.id
48 }
49}
50
51impl Display for Blob {
52 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
53 writeln!(f, "Type: Blob").unwrap();
54 writeln!(f, "Size: {}", self.data.len())
55 }
56}
57
58impl ObjectTrait for Blob {
59 /// Creates a new object from a byte slice.
60 fn from_bytes(data: &[u8], hash: SHA1) -> Result<Self, GitError>
61 where
62 Self: Sized,
63 {
64 Ok(Blob {
65 id: hash,
66 data: data.to_vec(),
67 })
68 }
69
70 /// Returns the Blob type
71 fn get_type(&self) -> ObjectType {
72 ObjectType::Blob
73 }
74
75 fn get_size(&self) -> usize {
76 self.data.len()
77 }
78
79 fn to_data(&self) -> Result<Vec<u8>, GitError> {
80 Ok(self.data.clone())
81 }
82}
83
84impl Blob {
85 /// Create a new Blob object from the given content string.
86 /// - This is a convenience method for creating a Blob from a string.
87 /// - It converts the string to bytes and then calls `from_content_bytes`.
88 pub fn from_content(content: &str) -> Self {
89 let content = content.as_bytes().to_vec();
90 Blob::from_content_bytes(content)
91 }
92
93 /// Create a new Blob object from the given content bytes.
94 /// - some file content can't be represented as a string (UTF-8), so we need to use bytes.
95 pub fn from_content_bytes(content: Vec<u8>) -> Self {
96 Blob {
97 // Calculate the SHA1 hash from the type and content
98 id: SHA1::from_type_and_data(ObjectType::Blob, &content),
99 data: content,
100 }
101 }
102}
103
104#[cfg(test)]
105mod tests {
106 use super::*;
107 #[test]
108 fn test_blob_from_content() {
109 let content = "Hello, world!";
110 let blob = Blob::from_content(content);
111 assert_eq!(
112 blob.id.to_string(),
113 "5dd01c177f5d7d1be5346a5bc18a569a7410c2ef"
114 );
115 }
116}