git_internal/internal/pack/
index_entry.rs1use crc32fast::Hasher;
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 errors::GitError,
9 hash::ObjectHash,
10 internal::{
11 metadata::{EntryMeta, MetaAttached},
12 pack::entry::Entry,
13 },
14};
15
16#[derive(Clone, Debug, Serialize, Deserialize)]
18pub struct IndexEntry {
19 pub hash: ObjectHash,
20 pub crc32: u32,
21 pub offset: u64, }
23
24impl TryFrom<&MetaAttached<Entry, EntryMeta>> for IndexEntry {
25 type Error = GitError;
26
27 fn try_from(pack_entry: &MetaAttached<Entry, EntryMeta>) -> Result<Self, GitError> {
28 let offset = pack_entry
29 .meta
30 .pack_offset
31 .ok_or(GitError::ConversionError(String::from(
32 "empty offset in pack entry",
33 )))?;
34 let crc32 = pack_entry
37 .meta
38 .crc32
39 .unwrap_or_else(|| calculate_crc32(&pack_entry.inner.data));
40 Ok(IndexEntry {
41 hash: pack_entry.inner.hash,
42 crc32,
43 offset: offset as u64,
44 })
45 }
46}
47
48impl IndexEntry {
49 pub fn new(entry: &Entry, offset: usize) -> Self {
51 IndexEntry {
52 hash: entry.hash,
53 crc32: calculate_crc32(&entry.data),
54 offset: offset as u64,
55 }
56 }
57}
58
59fn calculate_crc32(bytes: &[u8]) -> u32 {
60 let mut hasher = Hasher::new();
61 hasher.update(bytes);
62 hasher.finalize()
63}
64
65#[cfg(test)]
66mod tests {
67 use super::*;
68 use crate::{
69 hash::{HashKind, ObjectHash, set_hash_kind_for_test},
70 internal::metadata::{EntryMeta, MetaAttached},
71 internal::{object::types::ObjectType, pack::entry::Entry},
72 };
73
74 fn create_test_entry(content: &[u8]) -> Entry {
76 Entry {
77 obj_type: ObjectType::Blob,
78 data: content.to_vec(),
79 hash: ObjectHash::new(content),
80 chain_len: 0,
81 }
82 }
83
84 #[test]
85 fn test_index_entry_new() {
86 let _guard = set_hash_kind_for_test(HashKind::Sha1);
87 let entry = create_test_entry(b"test data");
88 let offset = 123;
89
90 let index_entry = IndexEntry::new(&entry, offset);
91
92 assert_eq!(index_entry.hash, entry.hash);
93 assert_eq!(index_entry.offset, offset as u64);
94
95 let mut hasher = Hasher::new();
96 hasher.update(b"test data");
97 assert_eq!(index_entry.crc32, hasher.finalize());
98 }
99
100 #[test]
101 fn test_try_from_meta_attached_with_crc() {
102 let _guard = set_hash_kind_for_test(HashKind::Sha1);
103 let entry = create_test_entry(b"test data");
104 let meta = EntryMeta {
105 pack_offset: Some(456),
106 crc32: Some(0x12345678),
107 ..Default::default()
108 };
109 let meta_attached = MetaAttached { inner: entry, meta };
110
111 let index_entry = IndexEntry::try_from(&meta_attached).unwrap();
112
113 assert_eq!(index_entry.hash, meta_attached.inner.hash);
114 assert_eq!(index_entry.offset, 456);
115 assert_eq!(index_entry.crc32, 0x12345678);
116 }
117
118 #[test]
119 fn test_try_from_meta_attached_crc_fallback() {
120 let _guard = set_hash_kind_for_test(HashKind::Sha1);
121 let entry_data = b"fallback crc";
122 let entry = create_test_entry(entry_data);
123 let meta = EntryMeta {
124 pack_offset: Some(789),
125 crc32: None, ..Default::default()
127 };
128 let meta_attached = MetaAttached { inner: entry, meta };
129
130 let index_entry = IndexEntry::try_from(&meta_attached).unwrap();
131
132 assert_eq!(index_entry.hash, meta_attached.inner.hash);
133 assert_eq!(index_entry.offset, 789);
134
135 let mut hasher = Hasher::new();
136 hasher.update(entry_data);
137 assert_eq!(index_entry.crc32, hasher.finalize());
138 }
139
140 #[test]
141 fn test_try_from_meta_attached_no_offset() {
142 let _guard = set_hash_kind_for_test(HashKind::Sha1);
143 let entry = create_test_entry(b"no offset");
144 let meta = EntryMeta {
145 pack_offset: None, ..Default::default()
147 };
148 let meta_attached = MetaAttached { inner: entry, meta };
149
150 let result = IndexEntry::try_from(&meta_attached);
151 assert!(result.is_err());
152 match result.unwrap_err() {
153 GitError::ConversionError(msg) => {
154 assert_eq!(msg, "empty offset in pack entry");
155 }
156 _ => panic!("Expected ConversionError"),
157 }
158 }
159}