git_internal/internal/object/
types.rs

1use std::fmt::Display;
2
3use serde::{Deserialize, Serialize};
4
5use crate::errors::GitError;
6
7/// In Git, each object type is assigned a unique integer value, which is used to identify the
8/// type of the object in Git repositories.
9///
10/// * `Blob` (1): A Git object that stores the content of a file.
11/// * `Tree` (2): A Git object that represents a directory or a folder in a Git repository.
12/// * `Commit` (3): A Git object that represents a commit in a Git repository, which contains
13///   information such as the author, committer, commit message, and parent commits.
14/// * `Tag` (4): A Git object that represents a tag in a Git repository, which is used to mark a
15///   specific point in the Git history.
16/// * `OffsetDelta` (6): A Git object that represents a delta between two objects, where the delta
17///   is stored as an offset to the base object.
18/// * `HashDelta` (7): A Git object that represents a delta between two objects, where the delta
19///   is stored as a hash of the base object.
20///
21/// By assigning unique integer values to each Git object type, Git can easily and efficiently
22/// identify the type of an object and perform the appropriate operations on it. when parsing a Git
23/// repository, Git can use the integer value of an object's type to determine how to parse
24/// the object's content.
25#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, Serialize, Deserialize)]
26pub enum ObjectType {
27    Commit = 1,
28    Tree,
29    Blob,
30    Tag,
31    OffsetZstdelta, // Private extension for Zstandard-compressed delta objects
32    OffsetDelta,
33    HashDelta,
34}
35
36const COMMIT_OBJECT_TYPE: &[u8] = b"commit";
37const TREE_OBJECT_TYPE: &[u8] = b"tree";
38const BLOB_OBJECT_TYPE: &[u8] = b"blob";
39const TAG_OBJECT_TYPE: &[u8] = b"tag";
40
41/// Display trait for Git objects type
42impl Display for ObjectType {
43    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
44        match self {
45            ObjectType::Blob => write!(f, "blob"),
46            ObjectType::Tree => write!(f, "tree"),
47            ObjectType::Commit => write!(f, "commit"),
48            ObjectType::Tag => write!(f, "tag"),
49            ObjectType::OffsetZstdelta => write!(f, "OffsetZstdelta"),
50            ObjectType::OffsetDelta => write!(f, "OffsetDelta"),
51            ObjectType::HashDelta => write!(f, "HashDelta"),
52        }
53    }
54}
55
56/// Display trait for Git objects type
57impl ObjectType {
58    pub fn to_bytes(&self) -> &[u8] {
59        match self {
60            ObjectType::Commit => COMMIT_OBJECT_TYPE,
61            ObjectType::Tree => TREE_OBJECT_TYPE,
62            ObjectType::Blob => BLOB_OBJECT_TYPE,
63            ObjectType::Tag => TAG_OBJECT_TYPE,
64            _ => panic!("can put compute the delta hash value"),
65        }
66    }
67
68    /// Parses a string representation of a Git object type and returns an ObjectType value
69    pub fn from_string(s: &str) -> Result<ObjectType, GitError> {
70        match s {
71            "blob" => Ok(ObjectType::Blob),
72            "tree" => Ok(ObjectType::Tree),
73            "commit" => Ok(ObjectType::Commit),
74            "tag" => Ok(ObjectType::Tag),
75            _ => Err(GitError::InvalidObjectType(s.to_string())),
76        }
77    }
78
79    /// Convert an object type to a byte array.
80    pub fn to_data(self) -> Result<Vec<u8>, GitError> {
81        match self {
82            ObjectType::Blob => Ok(vec![0x62, 0x6c, 0x6f, 0x62]),
83            ObjectType::Tree => Ok(vec![0x74, 0x72, 0x65, 0x65]),
84            ObjectType::Commit => Ok(vec![0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74]),
85            ObjectType::Tag => Ok(vec![0x74, 0x61, 0x67]),
86            _ => Err(GitError::InvalidObjectType(self.to_string())),
87        }
88    }
89
90    /// Convert an object type to a number.
91    pub fn to_u8(&self) -> u8 {
92        match self {
93            ObjectType::Commit => 1,
94            ObjectType::Tree => 2,
95            ObjectType::Blob => 3,
96            ObjectType::Tag => 4,
97            ObjectType::OffsetZstdelta => 5, // Type 5 is reserved in standard Git packs; we use it for Zstd delta objects.
98            ObjectType::OffsetDelta => 6,
99            ObjectType::HashDelta => 7,
100        }
101    }
102
103    /// Convert a number to an object type.
104    pub fn from_u8(number: u8) -> Result<ObjectType, GitError> {
105        match number {
106            1 => Ok(ObjectType::Commit),
107            2 => Ok(ObjectType::Tree),
108            3 => Ok(ObjectType::Blob),
109            4 => Ok(ObjectType::Tag),
110            5 => Ok(ObjectType::OffsetZstdelta),
111            6 => Ok(ObjectType::OffsetDelta),
112            7 => Ok(ObjectType::HashDelta),
113            _ => Err(GitError::InvalidObjectType(format!(
114                "Invalid object type number: {number}"
115            ))),
116        }
117    }
118
119    pub fn is_base(&self) -> bool {
120        match self {
121            ObjectType::Commit => true,
122            ObjectType::Tree => true,
123            ObjectType::Blob => true,
124            ObjectType::Tag => true,
125            ObjectType::HashDelta => false,
126            ObjectType::OffsetZstdelta => false,
127            ObjectType::OffsetDelta => false,
128        }
129    }
130}
131
132#[cfg(test)]
133mod tests {
134    use crate::internal::object::types::ObjectType;
135
136    #[test]
137    fn test_object_type_to_data() {
138        let blob = ObjectType::Blob;
139        let blob_bytes = blob.to_data().unwrap();
140        assert_eq!(blob_bytes, vec![0x62, 0x6c, 0x6f, 0x62]);
141    }
142
143    #[test]
144    fn test_object_type_from_string() {
145        let tree = ObjectType::from_string("tree").unwrap();
146        assert_eq!(tree, ObjectType::Tree);
147    }
148
149    #[test]
150    fn test_object_type_to_u8() {
151        let commit = ObjectType::Commit;
152        let commit_number = commit.to_u8();
153        assert_eq!(commit_number, 1);
154    }
155
156    #[test]
157    fn test_object_type_from_u8() {
158        let tag_number = 4;
159        let tag = ObjectType::from_u8(tag_number).unwrap();
160        assert_eq!(tag, ObjectType::Tag);
161    }
162}