crates/substrate/gix-ref-store/src/store.rs
store.rshistorycomment on this file
| 1 | //! The write (CAS) half of the `RefStore` seam. |
| 2 | |
| 3 | use crate::{RefEdit, RefStoreRead, Result, TxOutcome}; |
| 4 | |
| 5 | /// The unit of correctness for repository state: a store of named refs, |
| 6 | /// each pointing at an object id, updated only through atomic |
| 7 | /// compare-and-swap transactions. |
| 8 | /// |
| 9 | /// `RefStore` extends [`RefStoreRead`] rather than duplicating its |
| 10 | /// methods, so any code already written against the read half keeps |
| 11 | /// working unchanged when handed a full store. `arch.refstore-read-cas-split` |
| 12 | /// is about restricting what the *gate* is handed, not about the store |
| 13 | /// implementation's own shape: one type legitimately implements both |
| 14 | /// halves, as [`crate::LooseRefStore`] does. |
| 15 | /// |
| 16 | /// # Contract |
| 17 | /// |
| 18 | /// Multi-ref compare-and-swap is contractual, not a capability query. A |
| 19 | /// backend that cannot apply an arbitrary batch of [`RefEdit`]s atomically |
| 20 | /// — every precondition checked against one consistent view, and either |
| 21 | /// every edit applies or none do — does not satisfy this trait, full stop. |
| 22 | /// |
| 23 | /// # Examples |
| 24 | /// |
| 25 | /// ``` |
| 26 | /// use gix_hash::ObjectId; |
| 27 | /// use gix_ref_store::{Expected, LooseRefStore, RefEdit, RefStore, RefStoreRead, TxOutcome}; |
| 28 | /// |
| 29 | /// # fn run(dir: &std::path::Path, oid: ObjectId) -> gix_ref_store::Result<()> { |
| 30 | /// let store = LooseRefStore::open(dir)?; |
| 31 | /// let name: gix::refs::FullName = "refs/meta/config".try_into().expect("valid refname"); |
| 32 | /// let outcome = store.transaction(&[RefEdit { |
| 33 | /// name: name.clone(), |
| 34 | /// expected: Expected::MustNotExist, |
| 35 | /// new: Some(oid), |
| 36 | /// }])?; |
| 37 | /// assert_eq!(outcome, TxOutcome::Applied); |
| 38 | /// assert_eq!(store.get(name.as_ref())?, Some(oid)); |
| 39 | /// # Ok(()) |
| 40 | /// # } |
| 41 | /// ``` |
| 42 | // @relation(arch.refstore-read-cas-split, scope=file) |
| 43 | pub trait RefStore: RefStoreRead { |
| 44 | /// Apply `edits` as one atomic compare-and-swap transaction: every |
| 45 | /// edit's [`crate::Expected`] precondition is checked against the same |
| 46 | /// consistent view of the store, and either every edit applies or none |
| 47 | /// do. See the trait's contract above — this is not optional behavior |
| 48 | /// a backend may approximate. |
| 49 | fn transaction(&self, edits: &[RefEdit]) -> Result<TxOutcome>; |
| 50 | } |