gix_odb/store_impls/dynamic/structure.rs
1use std::path::PathBuf;
2
3use crate::{store::load_index, types::IndexAndPacks, Store};
4
5/// A record of a structural element of an object database.
6#[derive(Debug, Clone, PartialEq, Eq)]
7#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
8pub enum Record {
9 /// A loose object database.
10 LooseObjectDatabase {
11 /// The root of the object database.
12 objects_directory: PathBuf,
13 /// The amount of object files.
14 num_objects: usize,
15 },
16 /// A pack index file
17 Index {
18 /// The location of the index file,
19 path: PathBuf,
20 /// Whether or not the index is mapped into memory.
21 state: IndexState,
22 },
23 /// A multi-index file
24 MultiIndex {
25 /// The location of the multi-index file,
26 path: PathBuf,
27 /// Whether or not the index is mapped into memory.
28 state: IndexState,
29 },
30 /// An empty slot was encountered, this is possibly happening as the ODB changes during query with
31 /// a file being removed.
32 Empty,
33}
34
35#[derive(Debug, Clone, PartialEq, Eq)]
36#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
37/// Possible stats of pack indices.
38pub enum IndexState {
39 /// The index is active in memory because a mapping exists.
40 Loaded,
41 /// The index couldn't be unloaded as it was still in use, but that can happen another time.
42 Disposable,
43 /// The index isn't loaded/memory mapped.
44 Unloaded,
45}
46
47impl Store {
48 /// Return information about all files known to us as well as their loading state.
49 ///
50 /// Note that this call is expensive as it gathers additional information about loose object databases.
51 /// Note that it may change as we collect information due to the highly volatile nature of the
52 /// implementation. The likelihood of actual changes is low though as these still depend on something
53 /// changing on disk and somebody reading at the same time.
54 pub fn structure(&self) -> Result<Vec<Record>, load_index::Error> {
55 let _span = gix_features::trace::detail!("gix_odb::Store::structure()");
56 let index = self.index.load();
57 if !index.is_initialized() {
58 self.consolidate_with_disk_state(true, false /*load one new index*/)?;
59 }
60 let index = self.index.load();
61 let mut res: Vec<_> = index
62 .loose_dbs
63 .iter()
64 .map(|db| Record::LooseObjectDatabase {
65 objects_directory: db.path.clone(),
66 num_objects: db.iter().count(),
67 })
68 .collect();
69
70 for slot in index.slot_indices.iter().map(|idx| &self.files[*idx]) {
71 let files = slot.files.load();
72 let record = match &**files {
73 Some(index) => {
74 let state = if index.is_disposable() {
75 IndexState::Disposable
76 } else if index.index_is_loaded() {
77 IndexState::Loaded
78 } else {
79 IndexState::Unloaded
80 };
81 match index {
82 IndexAndPacks::Index(b) => Record::Index {
83 path: b.index.path().into(),
84 state,
85 },
86 IndexAndPacks::MultiIndex(b) => Record::MultiIndex {
87 path: b.multi_index.path().into(),
88 state,
89 },
90 }
91 }
92 None => Record::Empty,
93 };
94 res.push(record);
95 }
96 Ok(res)
97 }
98
99 /// Provide a list of all `objects` directories of `alternate` object database paths.
100 /// This list might be empty if there are no alternates.
101 ///
102 /// Read more about alternates in the documentation of the [`resolve`][crate::alternate::resolve()] function.
103 pub fn alternate_db_paths(&self) -> Result<Vec<PathBuf>, load_index::Error> {
104 let index = self.index.load();
105 if !index.is_initialized() {
106 self.consolidate_with_disk_state(true, false /*load one new index*/)?;
107 }
108 let index = self.index.load();
109 Ok(index
110 .loose_dbs
111 .iter()
112 .skip(
113 1, /* first odb is always the primary one, all the follows is alternates */
114 )
115 .map(|db| db.path.clone())
116 .collect())
117 }
118}