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}