libvctrl_handler 5.0.0

Fundamental contracts for building a version control system – no implementations, only traits and types
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
//! Index (staging area) trait.
//!
//! # Architecture
//! This module defines the abstract contract for managing the Git index, commonly
//! known as the staging area. The index acts as the crucial intermediate state
//! between the working directory and the object database, tracking planned changes
//! for the next commit.
//!
//! # Design Rationale: Associated Types over Generics
//! The trait uses associated types (`type Entry`, `type Path`, `type TreeId`)
//! rather than generic parameters. This design ties the data representations
//! directly to the specific `Index` implementation. An in-memory index might use
//! `Rc<TreeEntry>` and `String`, while a disk-backed index might use `TreeEntry`
//! and `PathBuf`. This prevents type mismatches at compile time and simplifies
//! the API by removing the need for verbose generic annotations at every call site.

use crate::errors::VctrlError;

/// A trait for managing a Git index (staging area).
///
/// # Why this exists
/// The staging area allows users to stage partial changes (hunks) before committing
/// them to history. By abstracting this into a trait, the crate allows the core
/// engine to orchestrate commits, diffs, and merges without being tied to a specific
/// binary format (like the `.git/index` file) or an in-memory representation.
///
/// # How it works
/// The index maintains a mapping between file paths and their staged object entries.
/// It supports adding, removing, and querying entries. The `write_tree` method
/// serializes the current state into one or more tree objects in the object database,
/// returning the root tree identifier. `read_tree` performs the inverse, populating
/// the index from an existing tree.
///
/// # Design Rationale: `&self` on `write_tree`
/// Note that `write_tree` takes `&self` instead of `&mut self`. This is because
/// writing a tree does not mutate the logical state of the index itself. The
/// implementor is responsible for handling any necessary interior mutability
/// (e.g., using `RefCell` or `Mutex`) when interacting with the underlying
/// `ObjectStore` to persist the tree objects.
///
/// # Examples
///
/// Implementing the trait for a mock in-memory store:
///
/// ```
/// # use libvctrl_handler::traits::core::index::Index;
/// # use libvctrl_handler::VctrlError;
/// # use std::collections::HashMap;
/// #
/// #[derive(Default)]
/// struct MockIndex {
///     data: HashMap<String, String>,
/// }
///
/// impl Index for MockIndex {
///     type Entry = String;
///     type Path = String;
///     type TreeId = u32;
///
///     fn add(&mut self, entry: Self::Entry) -> Result<(), VctrlError> {
///         self.data.insert(entry.clone(), entry);
///         Ok(())
///     }
///
///     fn remove(&mut self, path: &Self::Path) -> Result<(), VctrlError> {
///         self.data.remove(path);
///         Ok(())
///     }
///
///     fn clear(&mut self) -> Result<(), VctrlError> {
///         self.data.clear();
///         Ok(())
///     }
///
///     fn get(&self, path: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> {
///         Ok(self.data.get(path).cloned())
///     }
///
///     fn contains(&self, path: &Self::Path) -> Result<bool, VctrlError> {
///         Ok(self.data.contains_key(path))
///     }
///
///     fn len(&self) -> Result<usize, VctrlError> {
///         Ok(self.data.len())
///     }
///
///     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> {
///         Ok(self.data.values().cloned().collect())
///     }
///
///     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> {
///         // In a real impl, this would write to an ObjectStore.
///         Ok(1)
///     }
///
///     fn read_tree(&mut self, _tree: &Self::TreeId) -> Result<(), VctrlError> {
///         // Mock implementation
///         Ok(())
///     }
/// }
///
/// let mut index = MockIndex::default();
/// index.add("file.txt".to_string())?;
/// assert_eq!(index.len()?, 1);
/// assert!(index.contains(&"file.txt".to_string())?);
/// # Ok::<(), VctrlError>(())
/// ```
pub trait Index: Send + Sync {
    /// The entry type used by the index.
    ///
    /// # Why this exists
    /// Allows the backend to define its own representation of a staged file, which
    /// might include mode bits, object hashes, and filesystem stat data (mtime, ctime)
    /// for optimization.
    type Entry: Send + Sync;

    /// The path type used by the index.
    ///
    /// # Why this exists
    /// Decouples the path representation. While typically a `String` or `PathBuf`,
    /// this allows backends to use interned strings or OS-specific paths.
    type Path: Send + Sync;

    /// The tree identifier type.
    ///
    /// # Why this exists
    /// Matches the identifier type used by the backend's `ObjectStore` or `TreeDiffer`,
    /// ensuring seamless interoperability when writing or reading trees.
    type TreeId: Send + Sync;

    /// Adds an entry to the index.
    ///
    /// # How it works
    /// Inserts or updates the entry in the index. If an entry with the same path already
    /// exists, it is overwritten. Requires `&mut self` as it mutates the logical state
    /// of the staging area.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage fails to persist the update
    /// or if the entry is invalid.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("new_file.txt".to_string())?;
    /// assert_eq!(index.len()?, 1);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn add(&mut self, entry: Self::Entry) -> Result<(), VctrlError>;

    /// Removes an entry from the index by path.
    ///
    /// # How it works
    /// Locates the entry by its path and removes it. If the path does not exist,
    /// this operation is typically idempotent and returns `Ok(())`.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage fails to persist the deletion.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("file.txt".to_string())?;
    /// index.remove(&"file.txt".to_string())?;
    /// assert!(index.is_empty()?);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn remove(&mut self, path: &Self::Path) -> Result<(), VctrlError>;

    /// Clears all entries from the index.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be cleared.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("a".to_string())?;
    /// index.clear()?;
    /// assert_eq!(index.len()?, 0);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn clear(&mut self) -> Result<(), VctrlError>;

    /// Retrieves an entry by path.
    ///
    /// # How it works
    /// Performs a lookup. Returns `Ok(None)` if the path is not staged, maintaining
    /// a clear distinction between "not staged" and "I/O error".
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be read.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("file.txt".to_string())?;
    /// assert!(index.get(&"file.txt".to_string())?.is_some());
    /// assert!(index.get(&"missing.txt".to_string())?.is_none());
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn get(&self, path: &Self::Path) -> Result<Option<Self::Entry>, VctrlError>;

    /// Checks if an entry exists by path.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be read.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("file.txt".to_string())?;
    /// assert!(index.contains(&"file.txt".to_string())?);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn contains(&self, path: &Self::Path) -> Result<bool, VctrlError>;

    /// Returns the number of entries in the index.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be read.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("a".to_string())?;
    /// index.add("b".to_string())?;
    /// assert_eq!(index.len()?, 2);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn len(&self) -> Result<usize, VctrlError>;

    /// Returns `true` if the index is empty.
    ///
    /// # How it works
    /// This is a provided method that default-implements by calling `len()`. It
    /// exists to provide ergonomic, self-documenting code at call sites.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be read.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let index = MockIndex::default();
    /// assert!(index.is_empty()?);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn is_empty(&self) -> Result<bool, VctrlError> {
        Ok(self.len()? == 0)
    }

    /// Returns all entries in the index.
    ///
    /// # How it works
    /// Collects all staged entries into a `Vec`. This requires heap allocation.
    /// Callers should prefer `get` or `contains` if they only need to query a
    /// specific path, to avoid the overhead of collecting the entire index.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the underlying storage cannot be read.
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("a".to_string())?;
    /// let entries = index.entries()?;
    /// assert_eq!(entries.len(), 1);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError>;

    /// Writes the current index to a tree object and returns its identifier.
    ///
    /// # How it works
    /// Traverses the staged entries, recursively building tree objects for directories.
    /// It persists these trees to the `ObjectStore` (handled internally by the implementor)
    /// and returns the hash (or ID) of the root tree. This is the final step before
    /// creating a commit object.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the tree cannot be constructed or persisted, typically
    /// due to I/O failures or invalid index states (e.g., unsorted entries).
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(42) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.add("file.txt".to_string())?;
    /// let tree_id = index.write_tree()?;
    /// assert_eq!(tree_id, 42);
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn write_tree(&self) -> Result<Self::TreeId, VctrlError>;

    /// Reads a tree into the index.
    ///
    /// # How it works
    /// Clears the current index state and populates it with the entries from the
    /// specified tree object. This is commonly used during `checkout` or `reset`
    /// operations to synchronize the staging area with a specific commit's state.
    ///
    /// # Errors
    ///
    /// Returns [`VctrlError`] if the tree cannot be found or if the index cannot be
    /// mutated (e.g., I/O errors).
    ///
    /// # Examples
    ///
    /// ```
    /// # use libvctrl_handler::traits::core::index::Index;
    /// # use libvctrl_handler::VctrlError;
    /// # use std::collections::HashMap;
    /// # #[derive(Default)]
    /// # struct MockIndex { data: HashMap<String, String> }
    /// # impl Index for MockIndex {
    /// #     type Entry = String; type Path = String; type TreeId = u32;
    /// #     fn add(&mut self, e: Self::Entry) -> Result<(), VctrlError> { self.data.insert(e.clone(), e); Ok(()) }
    /// #     fn remove(&mut self, p: &Self::Path) -> Result<(), VctrlError> { self.data.remove(p); Ok(()) }
    /// #     fn clear(&mut self) -> Result<(), VctrlError> { self.data.clear(); Ok(()) }
    /// #     fn get(&self, p: &Self::Path) -> Result<Option<Self::Entry>, VctrlError> { Ok(self.data.get(p).cloned()) }
    /// #     fn contains(&self, p: &Self::Path) -> Result<bool, VctrlError> { Ok(self.data.contains_key(p)) }
    /// #     fn len(&self) -> Result<usize, VctrlError> { Ok(self.data.len()) }
    /// #     fn entries(&self) -> Result<Vec<Self::Entry>, VctrlError> { Ok(self.data.values().cloned().collect()) }
    /// #     fn write_tree(&self) -> Result<Self::TreeId, VctrlError> { Ok(1) }
    /// #     fn read_tree(&mut self, _t: &Self::TreeId) -> Result<(), VctrlError> { Ok(()) }
    /// # }
    /// let mut index = MockIndex::default();
    /// index.read_tree(&99)?;
    /// assert!(index.is_empty()?); // Mock implementation does not populate
    /// # Ok::<(), VctrlError>(())
    /// ```
    fn read_tree(&mut self, tree: &Self::TreeId) -> Result<(), VctrlError>;
}