Skip to main content

sim_lib_operation_gate/
lifecycle_record.rs

1//! Verified projected state for one durable operation.
2
3use sim_kernel::Datum;
4
5use crate::{
6    OperationAttempt, OperationError, OperationGrant, OperationId, OperationIntent,
7    lifecycle::{
8        FencedDispatch, LifecycleReceipt, OUTCOME_TAG, OperationLease, OperationObservation,
9        OperationOutcome, OperationOutcomeId, OperationStep,
10    },
11    lifecycle_wire::{outcome_datum, outcome_from_datum},
12    operation_wire::{content_id, field, id_from_datum, node_fields},
13};
14
15/// Durable identified outcome stored by the lifecycle.
16#[derive(Clone, Debug, PartialEq, Eq)]
17pub(super) struct IdentifiedOutcome {
18    pub(super) id: OperationOutcomeId,
19    pub(super) operation: OperationId,
20    pub(super) outcome: OperationOutcome,
21}
22
23impl IdentifiedOutcome {
24    pub(super) fn new(
25        operation: OperationId,
26        outcome: OperationOutcome,
27    ) -> Result<Self, OperationError> {
28        let datum = outcome_datum(&operation, &outcome);
29        Ok(Self {
30            id: OperationOutcomeId(content_id(&datum)?),
31            operation,
32            outcome,
33        })
34    }
35    pub(super) fn from_datum(datum: &Datum) -> Result<Self, OperationError> {
36        let fields = node_fields(datum, OUTCOME_TAG, 3)?;
37        let operation = OperationId(id_from_datum(field(fields, "operation")?)?);
38        let outcome = outcome_from_datum(field(fields, "value")?)?;
39        let value = Self::new(operation, outcome)?;
40        if value.canonical_datum() != *datum {
41            return Err(OperationError::NonCanonical("operation outcome"));
42        }
43        Ok(value)
44    }
45    pub(super) fn canonical_datum(&self) -> Datum {
46        outcome_datum(&self.operation, &self.outcome)
47    }
48}
49
50/// Complete verified lifecycle history reconstructed only from the journal.
51#[derive(Clone, Debug, PartialEq, Eq)]
52pub struct OperationLifecycleRecord {
53    pub(super) intent: OperationIntent,
54    pub(super) grant: OperationGrant,
55    pub(super) leases: Vec<OperationLease>,
56    pub(super) attempts: Vec<OperationAttempt>,
57    pub(super) dispatches: Vec<FencedDispatch>,
58    pub(super) receipts: Vec<LifecycleReceipt>,
59    pub(super) observations: Vec<OperationObservation>,
60    pub(super) outcomes: Vec<IdentifiedOutcome>,
61    pub(super) last_step: OperationStep,
62}
63
64impl OperationLifecycleRecord {
65    /// Returns the immutable intent.
66    pub const fn intent(&self) -> &OperationIntent {
67        &self.intent
68    }
69    /// Returns the separately recorded grant.
70    pub const fn grant(&self) -> &OperationGrant {
71        &self.grant
72    }
73    /// Returns every bounded lease in durable order.
74    pub fn leases(&self) -> &[OperationLease] {
75        &self.leases
76    }
77    /// Returns every attempt in durable order.
78    pub fn attempts(&self) -> &[OperationAttempt] {
79        &self.attempts
80    }
81    /// Returns every dispatch in durable order.
82    pub fn dispatches(&self) -> &[FencedDispatch] {
83        &self.dispatches
84    }
85    /// Returns every raw receipt in durable order.
86    pub fn receipts(&self) -> &[LifecycleReceipt] {
87        &self.receipts
88    }
89    /// Returns every independent observation in durable order.
90    pub fn observations(&self) -> &[OperationObservation] {
91        &self.observations
92    }
93    /// Returns the latest durable outcome.
94    pub fn outcome(&self) -> Option<&OperationOutcome> {
95        self.outcomes.last().map(|value| &value.outcome)
96    }
97    /// Returns the semantic identity of the latest durable outcome.
98    pub fn outcome_id(&self) -> Option<&OperationOutcomeId> {
99        self.outcomes.last().map(|value| &value.id)
100    }
101    pub(super) fn last_step(&self) -> OperationStep {
102        self.last_step
103    }
104}