grit_lib/odb/read_store.rs
1//! [`Odb::read_store`]: the whole lookup chain of an [`Odb`] as one [`ObjectStore`].
2
3use std::ops::ControlFlow;
4use std::sync::Arc;
5
6use crate::error::Result;
7use crate::objects::{HashAlgo, Object, ObjectId, ObjectInfo};
8use crate::odb::store::{ObjectStore, ObjectStream};
9
10use super::Odb;
11
12/// Read-only [`ObjectStore`] over a cloned [`Odb`] handle (clones share every cache).
13///
14/// Misses are `Ok(None)` and never start a promisor fetch: a store composed into another
15/// repository answers for what this database holds, not for what a remote could send.
16#[derive(Debug)]
17struct OdbReadStore(Odb);
18
19impl ObjectStore for OdbReadStore {
20 fn hash_algo(&self) -> HashAlgo {
21 self.0.hash_algo()
22 }
23
24 fn read(&self, oid: &ObjectId) -> Result<Option<Object>> {
25 self.0.try_read_once(oid)
26 }
27
28 fn read_info(&self, oid: &ObjectId) -> Result<Option<ObjectInfo>> {
29 self.0.try_read_info_once(oid)
30 }
31
32 fn contains(&self, oid: &ObjectId) -> Result<bool> {
33 self.0.contains(oid)
34 }
35
36 fn contains_many(&self, oids: &[ObjectId]) -> Result<Vec<bool>> {
37 self.0.contains_many(oids)
38 }
39
40 fn open_stream(&self, oid: &ObjectId) -> Result<Option<ObjectStream<'_>>> {
41 self.0.open_stream(oid)
42 }
43
44 fn for_each_object(&self, f: &mut dyn FnMut(&ObjectId) -> ControlFlow<()>) -> Result<()> {
45 self.0.for_each_object(f)
46 }
47
48 fn lookup_prefix(&self, prefix: &str, limit: usize, out: &mut Vec<ObjectId>) -> Result<()> {
49 self.0.lookup_prefix(prefix, limit, out)
50 }
51
52 fn refresh(&self) -> Result<bool> {
53 let odb = &self.0;
54 let mut changed = odb.primary_writable()?.refresh()?;
55 for source in &odb.extra_read_sources {
56 changed |= source.refresh()?;
57 }
58 if odb.alternates_enabled {
59 changed |= odb.sources()?.refresh()?;
60 }
61 Ok(changed)
62 }
63}
64
65impl Odb {
66 /// Everything this database can read, as one [`ObjectStore`].
67 ///
68 /// The store walks the same lookup order as [`Self::read`]: the in-memory overlay, the
69 /// primary (files-backed or a custom [`OdbBuilder::primary`](super::OdbBuilder::primary)),
70 /// each [`OdbBuilder::push_read_source`](super::OdbBuilder::push_read_source) layer, then
71 /// alternate object directories when [`OdbBuilder::alternates`](super::OdbBuilder::alternates)
72 /// is on. Pass it to another repository's `push_read_source` to layer this one under it
73 /// (a read-through cache or mirror), or as the `bases` of
74 /// [`ingest_pack_into`](crate::unpack_objects::ingest_pack_into).
75 ///
76 /// Unlike [`Self::read`], a missing object is `Ok(None)` and never triggers a promisor
77 /// fetch. [`ObjectStore::open_stream`] buffers the payload like [`Self::open_stream`];
78 /// call [`Self::with_stream`] on the `Odb` itself to stream large objects in bounded
79 /// memory. [`ObjectStore::refresh`] refreshes every layer. The returned handle shares
80 /// this `Odb`'s caches and stays valid after the `Odb` is dropped.
81 ///
82 /// [`Self::sources`] is narrower (alternates only) and [`Self::primary`] is files-only;
83 /// use [`Self::primary_writable`] for the write side of a custom primary.
84 ///
85 /// # Examples
86 ///
87 /// ```
88 /// use std::sync::Arc;
89 ///
90 /// use grit_lib::objects::odb::store::MemoryStore;
91 /// use grit_lib::objects::odb::OdbBuilder;
92 /// use grit_lib::objects::{HashAlgo, ObjectKind};
93 ///
94 /// # fn main() -> grit_lib::error::Result<()> {
95 /// let upstream = OdbBuilder::files("/nowhere/upstream/objects")
96 /// .primary(Arc::new(MemoryStore::new(HashAlgo::Sha1)))
97 /// .alternates(false)
98 /// .build();
99 /// let mirror = OdbBuilder::files("/nowhere/mirror/objects")
100 /// .primary(Arc::new(MemoryStore::new(HashAlgo::Sha1)))
101 /// .push_read_source(upstream.read_store())
102 /// .alternates(false)
103 /// .build();
104 /// let oid = upstream.write(ObjectKind::Blob, b"shared")?;
105 /// assert!(mirror.contains(&oid)?);
106 /// # Ok(())
107 /// # }
108 /// ```
109 #[must_use]
110 pub fn read_store(&self) -> Arc<dyn ObjectStore> {
111 Arc::new(OdbReadStore(self.clone()))
112 }
113}