mkit_server/pipeline/
durable_outcome.rs1use core::time::Duration;
4
5use crate::error::Redacted;
6use crate::store::codec::{AbortReason, OutcomeRef, ReservationV1};
7
8#[derive(Debug, Clone, PartialEq, Eq)]
10#[non_exhaustive]
11pub struct Outcome {
12 pub reservation_id: String,
14 pub audience: String,
16 pub repository: String,
18 pub occurred_unix_ms: i64,
20 pub kind: OutcomeKind,
22}
23
24#[derive(Debug, Clone, PartialEq, Eq)]
26#[non_exhaustive]
27pub enum OutcomeKind {
28 Committed {
30 bytes_stored: u64,
31 new_to_repo: u64,
32 new_to_store: u64,
33 refs: Vec<OutcomeRef>,
34 },
35 Aborted { reason: AbortReason, detail: String },
37 Expired,
39 ReadServed { object: [u8; 32], bytes_served: u64 },
41}
42
43impl Outcome {
44 pub fn from_reservation(
49 reservation_id: String,
50 audience: String,
51 row: ReservationV1,
52 ) -> Result<Self, &'static str> {
53 let (repository, occurred, kind) = match row {
54 ReservationV1::Committed {
55 repository,
56 occurred_at_ms,
57 bytes_stored,
58 new_to_repo,
59 new_to_store,
60 refs,
61 } => (
62 repository,
63 occurred_at_ms,
64 OutcomeKind::Committed {
65 bytes_stored,
66 new_to_repo,
67 new_to_store,
68 refs,
69 },
70 ),
71 ReservationV1::Aborted {
72 repository,
73 occurred_at_ms,
74 reason,
75 detail,
76 } => (
77 repository,
78 occurred_at_ms,
79 OutcomeKind::Aborted { reason, detail },
80 ),
81 ReservationV1::Expired {
82 repository,
83 occurred_at_ms,
84 } => (repository, occurred_at_ms, OutcomeKind::Expired),
85 ReservationV1::ReadServed {
86 repository,
87 occurred_at_ms,
88 object,
89 bytes_served,
90 } => (
91 repository,
92 occurred_at_ms,
93 OutcomeKind::ReadServed {
94 object,
95 bytes_served,
96 },
97 ),
98 ReservationV1::Pending { .. } | ReservationV1::Ticketed { .. } => {
99 return Err("outcome row is not terminal");
100 }
101 };
102 Ok(Self {
103 reservation_id,
104 audience,
105 repository,
106 occurred_unix_ms: i64::try_from(occurred).map_err(|_| "outcome timestamp overflow")?,
107 kind,
108 })
109 }
110}
111
112#[derive(Debug, Clone)]
114#[non_exhaustive]
115pub struct DeliveryError {
116 pub reason: Redacted,
118 pub retry_after: Option<Duration>,
120}
121
122impl core::fmt::Display for DeliveryError {
123 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
124 f.write_str("outcome delivery failed")
125 }
126}
127
128impl std::error::Error for DeliveryError {}
129
130impl DeliveryError {
131 #[must_use]
133 pub fn new(reason: impl Into<String>, retry_after: Option<Duration>) -> Self {
134 Self {
135 reason: Redacted::new(reason),
136 retry_after,
137 }
138 }
139}