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::{
71 metadata::{EntryMeta, MetaAttached},
72 object::types::ObjectType,
73 pack::entry::Entry,
74 },
75 };
76
77 fn create_test_entry(content: &[u8]) -> Entry {
79 Entry {
80 obj_type: ObjectType::Blob,
81 data: content.to_vec(),
82 hash: ObjectHash::new(content),
83 chain_len: 0,
84 }
85 }
86
87 #[test]
88 fn test_index_entry_new() {
89 let _guard = set_hash_kind_for_test(HashKind::Sha1);
90 let entry = create_test_entry(b"test data");
91 let offset = 123;
92
93 let index_entry = IndexEntry::new(&entry, offset);
94
95 assert_eq!(index_entry.hash, entry.hash);
96 assert_eq!(index_entry.offset, offset as u64);
97
98 let mut hasher = Hasher::new();
99 hasher.update(b"test data");
100 assert_eq!(index_entry.crc32, hasher.finalize());
101 }
102
103 #[test]
104 fn test_try_from_meta_attached_with_crc() {
105 let _guard = set_hash_kind_for_test(HashKind::Sha1);
106 let entry = create_test_entry(b"test data");
107 let meta = EntryMeta {
108 pack_offset: Some(456),
109 crc32: Some(0x12345678),
110 ..Default::default()
111 };
112 let meta_attached = MetaAttached { inner: entry, meta };
113
114 let index_entry = IndexEntry::try_from(&meta_attached).unwrap();
115
116 assert_eq!(index_entry.hash, meta_attached.inner.hash);
117 assert_eq!(index_entry.offset, 456);
118 assert_eq!(index_entry.crc32, 0x12345678);
119 }
120
121 #[test]
122 fn test_try_from_meta_attached_crc_fallback() {
123 let _guard = set_hash_kind_for_test(HashKind::Sha1);
124 let entry_data = b"fallback crc";
125 let entry = create_test_entry(entry_data);
126 let meta = EntryMeta {
127 pack_offset: Some(789),
128 crc32: None, ..Default::default()
130 };
131 let meta_attached = MetaAttached { inner: entry, meta };
132
133 let index_entry = IndexEntry::try_from(&meta_attached).unwrap();
134
135 assert_eq!(index_entry.hash, meta_attached.inner.hash);
136 assert_eq!(index_entry.offset, 789);
137
138 let mut hasher = Hasher::new();
139 hasher.update(entry_data);
140 assert_eq!(index_entry.crc32, hasher.finalize());
141 }
142
143 #[test]
144 fn test_try_from_meta_attached_no_offset() {
145 let _guard = set_hash_kind_for_test(HashKind::Sha1);
146 let entry = create_test_entry(b"no offset");
147 let meta = EntryMeta {
148 pack_offset: None, ..Default::default()
150 };
151 let meta_attached = MetaAttached { inner: entry, meta };
152
153 let result = IndexEntry::try_from(&meta_attached);
154 assert!(result.is_err());
155 match result.unwrap_err() {
156 GitError::ConversionError(msg) => {
157 assert_eq!(msg, "empty offset in pack entry");
158 }
159 _ => panic!("Expected ConversionError"),
160 }
161 }
162}