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}