Skip to main content

runifold_effect/
record.rs

1use runifold_core::{EffectRequest, RunError};
2use serde::{Deserialize, Serialize};
3use serde_json::Value;
4
5/// Persisted external-effect state.
6#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
7#[serde(tag = "state", rename_all = "snake_case")]
8#[non_exhaustive]
9pub enum EffectStatus {
10    /// The request is durable and no handler has been started.
11    Prepared,
12    /// The handler may be executing or may have executed.
13    Started,
14    /// The effect completed with a durable output.
15    Completed {
16        /// Canonical effect output.
17        output: Value,
18    },
19    /// The handler returned a durable failure.
20    Failed {
21        /// Structured terminal handler error.
22        error: RunError,
23    },
24}
25
26/// Revisioned write-ahead record for one logical effect.
27#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
28pub struct EffectRecord {
29    /// Monotonic compare-and-swap revision.
30    pub revision: u64,
31    /// Original canonical request.
32    pub request: EffectRequest,
33    /// Current persisted state.
34    pub status: EffectStatus,
35}
36
37impl EffectRecord {
38    /// Creates a prepared revision-zero record.
39    pub const fn prepared(request: EffectRequest) -> Self {
40        Self {
41            revision: 0,
42            request,
43            status: EffectStatus::Prepared,
44        }
45    }
46
47    /// Creates the next revision.
48    pub(crate) fn next(&self, status: EffectStatus) -> Result<Self, crate::EffectExecutorError> {
49        Ok(Self {
50            revision: self.revision.checked_add(1).ok_or_else(|| {
51                crate::EffectExecutorError::new(
52                    crate::EffectExecutorErrorKind::Store,
53                    "effect revision overflow",
54                )
55            })?,
56            request: self.request.clone(),
57            status,
58        })
59    }
60}