crates/substrate/gix-ref-store/src/edit.rs
edit.rshistorycomment on this file
| 1 | //! The vocabulary of a [`crate::RefStore::transaction`] call: what a |
| 2 | //! [`RefEdit`] expects a ref to hold, what a batch of them can do |
| 3 | //! atomically, and how the store reports which one failed. |
| 4 | |
| 5 | use gix::refs::FullName; |
| 6 | use gix_hash::ObjectId; |
| 7 | |
| 8 | /// The compare-and-swap precondition a [`RefEdit`] requires of a ref's |
| 9 | /// current value before the edit is allowed to apply. |
| 10 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 11 | pub enum Expected { |
| 12 | /// No requirement: set unconditionally. |
| 13 | Any, |
| 14 | /// The ref must not currently exist. |
| 15 | MustNotExist, |
| 16 | /// The ref must currently exist and equal the given object id. |
| 17 | MustExistAndMatch(ObjectId), |
| 18 | } |
| 19 | |
| 20 | /// One ref's half of a [`crate::RefStore::transaction`] batch: what `name` |
| 21 | /// is expected to hold, and what it should become. `new: None` deletes the |
| 22 | /// ref. |
| 23 | /// |
| 24 | /// # Examples |
| 25 | /// |
| 26 | /// ``` |
| 27 | /// use gix_hash::ObjectId; |
| 28 | /// use gix_ref_store::{Expected, RefEdit}; |
| 29 | /// |
| 30 | /// let oid = ObjectId::null(gix_hash::Kind::Sha1); |
| 31 | /// let edit = RefEdit { |
| 32 | /// name: "refs/meta/config".try_into().expect("valid refname"), |
| 33 | /// expected: Expected::MustNotExist, |
| 34 | /// new: Some(oid), |
| 35 | /// }; |
| 36 | /// assert_eq!(edit.new, Some(oid)); |
| 37 | /// ``` |
| 38 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 39 | pub struct RefEdit { |
| 40 | /// The ref this edit applies to. |
| 41 | pub name: FullName, |
| 42 | /// The compare-and-swap precondition checked against `name`'s current |
| 43 | /// value before the edit applies. |
| 44 | pub expected: Expected, |
| 45 | /// The value to set `name` to, or `None` to delete it. |
| 46 | pub new: Option<ObjectId>, |
| 47 | } |
| 48 | |
| 49 | /// The result of a [`crate::RefStore::transaction`] call that itself |
| 50 | /// completed (returned `Ok`): either every edit applied, or none did. |
| 51 | /// |
| 52 | /// A `Rejected` outcome is not an [`crate::Error`] — a stale |
| 53 | /// compare-and-swap precondition is an expected, checkable result, not a |
| 54 | /// backend fault. |
| 55 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 56 | pub enum TxOutcome { |
| 57 | /// Every edit in the batch applied atomically. |
| 58 | Applied, |
| 59 | /// The transaction did not apply: `name`'s current value did not match |
| 60 | /// its edit's [`Expected`] precondition. No edit in the batch took |
| 61 | /// effect — compare-and-swap is all-or-nothing, per the trait's |
| 62 | /// contract. |
| 63 | Rejected { |
| 64 | /// The first ref whose precondition failed. |
| 65 | name: FullName, |
| 66 | }, |
| 67 | } |
| 68 | |
| 69 | /// An iterator over `(name, tip)` pairs from a |
| 70 | /// [`crate::RefStoreRead::iter_prefix`] query, wrapping whatever iterator |
| 71 | /// the backend produces so the trait itself stays object-safe. |
| 72 | pub struct RefIter(Box<dyn Iterator<Item = crate::Result<(FullName, ObjectId)>> + Send>); |
| 73 | |
| 74 | impl RefIter { |
| 75 | /// Wrap `iter` as a [`RefIter`]. |
| 76 | pub fn new( |
| 77 | iter: impl Iterator<Item = crate::Result<(FullName, ObjectId)>> + Send + 'static, |
| 78 | ) -> Self { |
| 79 | Self(Box::new(iter)) |
| 80 | } |
| 81 | } |
| 82 | |
| 83 | impl Iterator for RefIter { |
| 84 | type Item = crate::Result<(FullName, ObjectId)>; |
| 85 | |
| 86 | fn next(&mut self) -> Option<Self::Item> { |
| 87 | self.0.next() |
| 88 | } |
| 89 | } |
| 90 | |
| 91 | impl std::fmt::Debug for RefIter { |
| 92 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
| 93 | f.write_str("RefIter(..)") |
| 94 | } |
| 95 | } |