rustic_core/index.rs
1use std::{sync::Arc, thread::sleep, time::Duration};
2
3use bytes::Bytes;
4use derive_more::Constructor;
5
6use crate::{
7 backend::{FileType, decrypt::DecryptReadBackend},
8 blob::{BlobId, BlobLocation, BlobType, DataId, tree::TreeId},
9 error::{ErrorKind, RusticError, RusticResult},
10 index::binarysorted::{Index, IndexCollector, IndexType},
11 progress::Progress,
12 repofile::{
13 indexfile::{IndexBlob, IndexFile},
14 packfile::PackId,
15 },
16};
17
18pub(crate) mod binarysorted;
19pub(crate) mod indexer;
20
21/// An entry in the index
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Constructor)]
23pub struct IndexEntry {
24 /// The type of the blob
25 blob_type: BlobType,
26 /// The pack the blob is in
27 pub pack: PackId,
28 /// The location of the blob in the pack
29 pub location: BlobLocation,
30}
31
32impl IndexEntry {
33 /// Create an [`IndexEntry`] from an [`IndexBlob`]
34 ///
35 /// # Arguments
36 ///
37 /// * `blob` - The [`IndexBlob`] to create the [`IndexEntry`] from
38 /// * `pack` - The pack the blob is in
39 #[must_use]
40 pub const fn from_index_blob(blob: &IndexBlob, pack: PackId) -> Self {
41 Self {
42 blob_type: blob.tpe,
43 pack,
44 location: blob.location,
45 }
46 }
47
48 /// Get a blob described by [`IndexEntry`] from the backend
49 ///
50 /// # Arguments
51 ///
52 /// * `be` - The backend to read from
53 ///
54 /// # Errors
55 ///
56 // TODO: add error! This function will return an error if the blob is not found in the backend.
57 pub fn read_data<B: DecryptReadBackend>(&self, be: &B) -> RusticResult<Bytes> {
58 let data = be.read_encrypted_partial(
59 FileType::Pack,
60 &self.pack,
61 self.blob_type.is_cacheable(),
62 self.location,
63 )?;
64
65 Ok(data)
66 }
67
68 /// Get the length of the data described by the [`IndexEntry`]
69 #[must_use]
70 pub const fn data_length(&self) -> u32 {
71 self.location.data_length()
72 }
73}
74
75/// The index of the repository
76///
77/// The index is a list of [`IndexEntry`]s
78pub trait ReadIndex {
79 /// Get an [`IndexEntry`] from the index
80 ///
81 /// # Arguments
82 ///
83 /// * `tpe` - The type of the blob
84 /// * `id` - The id of the blob
85 ///
86 /// # Returns
87 ///
88 /// The [`IndexEntry`] - If it exists otherwise `None`
89 fn get_id(&self, tpe: BlobType, id: &BlobId) -> Option<IndexEntry>;
90
91 /// Get the total size of all blobs of the given type
92 ///
93 /// # Arguments
94 ///
95 /// * `tpe` - The type of the blobs
96 fn total_size(&self, tpe: BlobType) -> u64;
97
98 /// Check if the index contains the given blob
99 ///
100 /// # Arguments
101 ///
102 /// * `tpe` - The type of the blob
103 /// * `id` - The id of the blob
104 fn has(&self, tpe: BlobType, id: &BlobId) -> bool;
105
106 /// Get a tree from the index
107 ///
108 /// # Arguments
109 ///
110 /// * `id` - The id of the tree
111 ///
112 /// # Returns
113 ///
114 /// The [`IndexEntry`] of the tree if it exists otherwise `None`
115 fn get_tree(&self, id: &TreeId) -> Option<IndexEntry> {
116 self.get_id(BlobType::Tree, &BlobId::from(**id))
117 }
118
119 /// Get a data blob from the index
120 ///
121 /// # Arguments
122 ///
123 /// * `id` - The id of the data blob
124 ///
125 /// # Returns
126 ///
127 /// The [`IndexEntry`] of the data blob if it exists otherwise `None`
128 fn get_data(&self, id: &DataId) -> Option<IndexEntry> {
129 self.get_id(BlobType::Data, &BlobId::from(**id))
130 }
131
132 /// Check if the index contains the given tree
133 ///
134 /// # Arguments
135 ///
136 /// * `id` - The id of the tree
137 ///
138 /// # Returns
139 ///
140 /// `true` if the index contains the tree otherwise `false`
141 fn has_tree(&self, id: &TreeId) -> bool {
142 self.has(BlobType::Tree, &BlobId::from(**id))
143 }
144
145 /// Check if the index contains the given data blob
146 ///
147 /// # Arguments
148 ///
149 /// * `id` - The id of the data blob
150 ///
151 /// # Returns
152 ///
153 /// `true` if the index contains the data blob otherwise `false`
154 fn has_data(&self, id: &DataId) -> bool {
155 self.has(BlobType::Data, &BlobId::from(**id))
156 }
157
158 /// Get a blob from the backend
159 ///
160 /// # Arguments
161 ///
162 /// * `tpe` - The type of the blob
163 /// * `id` - The id of the blob
164 ///
165 /// # Errors
166 ///
167 /// * If the blob could not be found in the index
168 fn blob_from_backend(
169 &self,
170 be: &impl DecryptReadBackend,
171 tpe: BlobType,
172 id: &BlobId,
173 ) -> RusticResult<Bytes> {
174 self.get_id(tpe, id).map_or_else(
175 || {
176 Err(RusticError::new(
177 ErrorKind::Internal,
178 "Blob `{id}` with type `{type}` not found in index",
179 )
180 .attach_context("id", id.to_string())
181 .attach_context("type", tpe.to_string()))
182 },
183 |ie| ie.read_data(be),
184 )
185 }
186}
187
188/// A trait for a global index
189pub trait ReadGlobalIndex: ReadIndex + Clone + Sync + Send + 'static {}
190
191/// A global index
192#[derive(Clone, Debug)]
193pub struct GlobalIndex {
194 /// The atomic reference counted, sharable index.
195 index: Arc<Index>,
196}
197
198impl ReadIndex for GlobalIndex {
199 /// Get an [`IndexEntry`] from the index
200 ///
201 /// # Arguments
202 ///
203 /// * `tpe` - The type of the blob
204 /// * `id` - The id of the blob
205 ///
206 /// # Returns
207 ///
208 /// The [`IndexEntry`] - If it exists otherwise `None`
209 fn get_id(&self, tpe: BlobType, id: &BlobId) -> Option<IndexEntry> {
210 self.index.get_id(tpe, id)
211 }
212
213 /// Get the total size of all blobs of the given type
214 ///
215 /// # Arguments
216 ///
217 /// * `tpe` - The type of the blobs
218 fn total_size(&self, tpe: BlobType) -> u64 {
219 self.index.total_size(tpe)
220 }
221
222 /// Check if the index contains the given blob
223 ///
224 /// # Arguments
225 ///
226 /// * `tpe` - The type of the blob
227 /// * `id` - The id of the blob
228 ///
229 /// # Returns
230 ///
231 /// `true` if the index contains the blob otherwise `false`
232 fn has(&self, tpe: BlobType, id: &BlobId) -> bool {
233 self.index.has(tpe, id)
234 }
235}
236
237impl GlobalIndex {
238 /// Create a new [`GlobalIndex`] from an [`Index`]
239 ///
240 /// # Type Parameters
241 ///
242 /// * `BE` - The backend type
243 ///
244 /// # Arguments
245 ///
246 /// * `be` - The backend to read from
247 /// * `index` - The index to use
248 pub fn new_from_index(index: Index) -> Self {
249 Self {
250 index: Arc::new(index),
251 }
252 }
253
254 /// Create a new [`GlobalIndex`] from an [`IndexCollector`]
255 ///
256 /// # Arguments
257 ///
258 /// * `be` - The backend to read from
259 /// * `p` - The progress tracker
260 /// * `collector` - The [`IndexCollector`] to use
261 ///
262 /// # Errors
263 ///
264 /// * If the index could not be read
265 fn new_from_collector(
266 be: &impl DecryptReadBackend,
267 p: &Progress,
268 mut collector: IndexCollector,
269 ) -> RusticResult<Self> {
270 p.set_title("reading index...");
271 for index in be.stream_all::<IndexFile>(p)? {
272 collector.extend(index?.1.packs);
273 }
274
275 p.finish();
276
277 Ok(Self::new_from_index(collector.into_index()))
278 }
279
280 /// Create a new [`GlobalIndex`]
281 ///
282 /// # Arguments
283 ///
284 /// * `be` - The backend to read from
285 /// * `p` - The progress tracker
286 pub fn new(be: &impl DecryptReadBackend, p: &Progress) -> RusticResult<Self> {
287 Self::new_from_collector(be, p, IndexCollector::new(IndexType::Full))
288 }
289
290 /// Create a new [`GlobalIndex`] with only full trees
291 ///
292 /// # Arguments
293 ///
294 /// * `be` - The backend to read from
295 /// * `p` - The progress tracker
296 ///
297 /// # Errors
298 ///
299 /// * If the index could not be read
300 pub fn only_full_trees(be: &impl DecryptReadBackend, p: &Progress) -> RusticResult<Self> {
301 Self::new_from_collector(be, p, IndexCollector::new(IndexType::DataIds))
302 }
303
304 /// Convert the `Arc<Index>` to an Index
305 pub fn into_index(self) -> Index {
306 match Arc::try_unwrap(self.index) {
307 Ok(index) => index,
308 Err(arc) => {
309 // Seems index is still in use; this could be due to some threads using it which didn't yet completely shut down.
310 // sleep a bit to let threads using the index shut down, after this index should be available to unwrap
311 sleep(Duration::from_millis(100));
312 Arc::try_unwrap(arc).expect("index still in use")
313 }
314 }
315 }
316
317 pub(crate) fn drop_data(self) -> Self {
318 Self {
319 index: Arc::new(self.into_index().drop_data()),
320 }
321 }
322}
323
324impl ReadGlobalIndex for GlobalIndex {}