Skip to main content

arkhe_forge_core/
action.rs

1//! `ArkheAction` + `ActionCompute` sealed traits.
2//!
3//! `ArkheAction` is the wire-metadata trait produced by
4//! `#[derive(ArkheAction)]`. `ActionCompute` pairs with it: each Action
5//! implements the `compute()` body directly, consuming a `&mut ActionContext`
6//! to derive entity ids, emit events, and signal rejection via `ActionError`.
7
8use arkhe_kernel::abi::TypeCode;
9use serde::{Deserialize, Serialize};
10
11use crate::context::{ActionContext, ActionError};
12
13/// Determinism band classification.
14///
15/// * `1` — Core (L0 replay-identical).
16/// * `2` — Projection (eventually consistent).
17/// * `3` — Protocol-correctness (shell-level).
18pub type Band = u8;
19
20/// Sealed marker trait for runtime Action types. Implementations come only
21/// from `#[derive(ArkheAction)]`.
22pub trait ArkheAction:
23    crate::__sealed::__Sealed + Serialize + for<'de> Deserialize<'de> + 'static
24{
25    /// Runtime `TypeCode` registry pin.
26    const TYPE_CODE: u32;
27
28    /// Monotone schema version — bump rules identical to `ArkheComponent`.
29    const SCHEMA_VERSION: u16;
30
31    /// Determinism band — `1` (Core) / `2` (Projection) / `3` (Protocol).
32    const BAND: Band;
33
34    /// Opt-in idempotency flag. `true` iff the deriving struct carries an
35    /// `idempotency_key: Option<[u8; 16]>` field (validated at derive time).
36    /// `false` by default — non-idempotent Actions are still legal.
37    const IDEMPOTENT: bool = false;
38
39    /// Convenience `TypeCode` accessor.
40    fn type_code() -> TypeCode {
41        TypeCode(Self::TYPE_CODE)
42    }
43}
44
45/// Compute-phase trait. Each `ArkheAction` implementor provides the body
46/// of this method manually — business logic per Action is unique, so
47/// `compute` is intentionally not derive-generated.
48///
49/// Implementations must be deterministic and side-effect-free outside of
50/// `ctx` (L0 A11 pure-compute succession).
51pub trait ActionCompute: crate::__sealed::__Sealed + ArkheAction {
52    /// Run the compute body. Emit events via `ctx.emit_event`, derive new
53    /// ids via `ctx.next_id`, and return `Err(ActionError::...)` to reject.
54    fn compute<'i>(&self, ctx: &mut ActionContext<'i>) -> Result<(), ActionError>;
55}