crates/substrate/gix-ref-store/src/read.rs
read.rshistorycomment on this file
| 1 | //! The read half of the `RefStore` seam. |
| 2 | //! |
| 3 | //! `arch.refstore-read-cas-split` requires that a consumer able to check |
| 4 | //! ref state never automatically gains the ability to change it. The gate |
| 5 | //! (`gate.adoc`) is the reason this split exists: it is a pure function |
| 6 | //! over ref-store reads and must be statically incapable of writing. |
| 7 | |
| 8 | use gix::refs::FullNameRef; |
| 9 | use gix_hash::ObjectId; |
| 10 | |
| 11 | use crate::{RefIter, Result}; |
| 12 | |
| 13 | /// The read half of a `RefStore`: everything needed to evaluate the gate |
| 14 | /// (`gate.adoc`) or render a UI, with no path to mutation. |
| 15 | /// |
| 16 | /// A type that also supports writes implements [`crate::RefStore`], which |
| 17 | /// extends this trait with [`crate::RefStore::transaction`]. Code that only |
| 18 | /// ever needs to read — the gate above all — should be written against |
| 19 | /// `RefStoreRead` (or `&dyn RefStoreRead`) so it is impossible, not just |
| 20 | /// disciplined, for it to write. |
| 21 | /// |
| 22 | /// # Examples |
| 23 | /// |
| 24 | /// ``` |
| 25 | /// use gix_ref_store::{LooseRefStore, RefStoreRead}; |
| 26 | /// |
| 27 | /// # fn open(dir: &std::path::Path) -> gix_ref_store::Result<()> { |
| 28 | /// let store = LooseRefStore::open(dir)?; |
| 29 | /// let read: &dyn RefStoreRead = &store; |
| 30 | /// let name: gix::refs::FullName = "refs/heads/does-not-exist".try_into().expect("valid refname"); |
| 31 | /// assert_eq!(read.get(name.as_ref())?, None); |
| 32 | /// # Ok(()) |
| 33 | /// # } |
| 34 | /// ``` |
| 35 | // @relation(arch.refstore-read-cas-split, scope=file) |
| 36 | pub trait RefStoreRead: Send + Sync { |
| 37 | /// The object id `name` currently points at, or `None` if `name` does |
| 38 | /// not exist. |
| 39 | fn get(&self, name: &FullNameRef) -> Result<Option<ObjectId>>; |
| 40 | |
| 41 | /// Every ref under `prefix` (for example `refs/meta/`), with its |
| 42 | /// current tip. |
| 43 | fn iter_prefix(&self, prefix: &str) -> Result<RefIter>; |
| 44 | } |
| 45 | |
| 46 | /// Blanket impl so a `RefStoreRead` behind any indirection remains usable |
| 47 | /// as `RefStoreRead` itself — `&T`, `Box<T>`, and `std::sync::Arc<T>` all |
| 48 | /// forward transparently. |
| 49 | impl<T: RefStoreRead + ?Sized> RefStoreRead for &T { |
| 50 | fn get(&self, name: &FullNameRef) -> Result<Option<ObjectId>> { |
| 51 | (**self).get(name) |
| 52 | } |
| 53 | |
| 54 | fn iter_prefix(&self, prefix: &str) -> Result<RefIter> { |
| 55 | (**self).iter_prefix(prefix) |
| 56 | } |
| 57 | } |
| 58 | |
| 59 | impl<T: RefStoreRead + ?Sized> RefStoreRead for std::sync::Arc<T> { |
| 60 | fn get(&self, name: &FullNameRef) -> Result<Option<ObjectId>> { |
| 61 | (**self).get(name) |
| 62 | } |
| 63 | |
| 64 | fn iter_prefix(&self, prefix: &str) -> Result<RefIter> { |
| 65 | (**self).iter_prefix(prefix) |
| 66 | } |
| 67 | } |