crates/kernel/ents-receive/src/outcome.rs
outcome.rshistorycomment on this file
| 1 | //! `receive`'s gate policy (mandatory or advisory) and the outcome it |
| 2 | //! reports: which verdict each proposed transition got, and whether the |
| 3 | //! batch actually landed. |
| 4 | |
| 5 | use gix::refs::FullName; |
| 6 | use gix_hash::ObjectId; |
| 7 | |
| 8 | use ents_gate::Verdict; |
| 9 | |
| 10 | /// Which of the two gate policies `receive.adoc` names governs one call: |
| 11 | /// abort the whole batch on a failing verdict (`gate.mandatory-hosted`), or |
| 12 | /// accept the write regardless and only annotate (`gate.advisory-local`). |
| 13 | /// |
| 14 | /// The gate itself ([`ents_gate::verify`]) is one pure function evaluated |
| 15 | /// identically either way (`gate.call-sites`); `Mode` is the policy |
| 16 | /// [`crate::receive`] applies to a *failing* verdict, which is exactly the |
| 17 | /// orchestration the development plan assigns to this crate — the gate |
| 18 | /// crate never sees a `Mode`, and could not: it has no write path to gate. |
| 19 | /// |
| 20 | /// # Examples |
| 21 | /// |
| 22 | /// ``` |
| 23 | /// use ents_receive::Mode; |
| 24 | /// |
| 25 | /// let mode = Mode::Advisory; |
| 26 | /// assert_eq!(mode, Mode::Advisory); |
| 27 | /// ``` |
| 28 | // @relation(gate.mandatory-hosted, gate.advisory-local, scope=file) |
| 29 | #[derive(Debug, Clone, Copy, PartialEq, Eq)] |
| 30 | pub enum Mode { |
| 31 | /// The hosted policy: a failing verdict against any transition in the |
| 32 | /// batch aborts the whole batch before any ref is updated |
| 33 | /// (`gate.mandatory-hosted`). |
| 34 | Mandatory, |
| 35 | /// The local policy: every transition is written regardless of its |
| 36 | /// verdict; a failing verdict only annotates the result, never blocks |
| 37 | /// it (`gate.advisory-local`). |
| 38 | Advisory, |
| 39 | } |
| 40 | |
| 41 | /// What happened to one [`crate::Proposal`]'s ref-transaction batch. |
| 42 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 43 | pub enum TxResult { |
| 44 | /// Every transition in the batch was written atomically. |
| 45 | Applied, |
| 46 | /// [`Mode::Mandatory`] aborted the whole batch before attempting a |
| 47 | /// write, because at least one transition's verdict failed |
| 48 | /// (`gate.mandatory-hosted`). See [`crate::Outcome::verdicts`] for |
| 49 | /// which one and why. |
| 50 | Refused, |
| 51 | /// The underlying store rejected the compare-and-swap: `name`'s |
| 52 | /// current value no longer matched the precondition read at |
| 53 | /// evaluation time — a genuine race, reported in the gate's own |
| 54 | /// vocabulary (`Requirement::AtomicCas`) as the gate crate's docs |
| 55 | /// anticipate for exactly this caller. |
| 56 | Rejected { |
| 57 | /// The ref whose precondition was stale. |
| 58 | name: FullName, |
| 59 | }, |
| 60 | /// The batch introduced an object matching a previously recorded |
| 61 | /// redaction target; the whole batch was refused before any verdict |
| 62 | /// was even evaluated, so a redacted hole cannot be silently refilled |
| 63 | /// by re-pushing the same bytes (`receive.redaction-ingest`). |
| 64 | Redacted { |
| 65 | /// The offending object id. |
| 66 | oid: ObjectId, |
| 67 | }, |
| 68 | } |
| 69 | |
| 70 | /// The result of one [`crate::receive`] call: every transition's verdict, |
| 71 | /// and what happened to the batch as a whole. |
| 72 | /// |
| 73 | /// # Examples |
| 74 | /// |
| 75 | /// ``` |
| 76 | /// use ents_receive::{Outcome, TxResult}; |
| 77 | /// |
| 78 | /// let outcome = Outcome { |
| 79 | /// verdicts: vec![], |
| 80 | /// result: TxResult::Applied, |
| 81 | /// }; |
| 82 | /// assert_eq!(outcome.result, TxResult::Applied); |
| 83 | /// ``` |
| 84 | #[derive(Debug, Clone, PartialEq, Eq)] |
| 85 | pub struct Outcome { |
| 86 | /// Each proposed transition's refname and the gate's verdict on it, |
| 87 | /// in proposal order. Present under both [`Mode`]s: mandatory callers |
| 88 | /// use it to see which refusal aborted the batch |
| 89 | /// (`gate.verdict-reason`); advisory callers render it to the user |
| 90 | /// regardless of [`Outcome::result`] (`gate.advisory-local`). |
| 91 | pub verdicts: Vec<(FullName, Verdict)>, |
| 92 | /// What happened to the batch. |
| 93 | pub result: TxResult, |
| 94 | } |