Skip to main content

Crate statum

Crate statum 

Source
Expand description

Compile-time verified typestate workflows for Rust.

Statum is for workflow and protocol models where representational correctness matters. It helps keep invalid, undesirable, or not-yet- validated states out of ordinary code. In the same spirit as Option and Result, it uses the type system to make absence, failure, and workflow legality explicit instead of leaving them in status fields and guard code. It generates typed state markers, typed machines, transition helpers, and typed rehydration from stored data.

§Mental Model

  • state defines the legal phases.
  • machine defines the durable context carried across phases.
  • transition defines the legal edges between phases.
  • validators rebuilds typed machines from persisted data.

§Quick Start

use statum::{machine, state, transition};

#[state]
enum CheckoutState {
    EmptyCart,
    ReadyToPay(OrderDraft),
    Paid,
}

#[derive(Clone)]
struct OrderDraft {
    total_cents: u64,
}

#[machine]
struct Checkout<CheckoutState> {
    id: String,
}

#[transition]
impl Checkout<EmptyCart> {
    fn review(self, total_cents: u64) -> Checkout<ReadyToPay> {
        self.transition_with(OrderDraft { total_cents })
    }
}

#[transition]
impl Checkout<ReadyToPay> {
    fn pay(self) -> Checkout<Paid> {
        self.transition()
    }
}

fn main() {
    let cart = Checkout::<EmptyCart>::builder()
        .id("order-1".to_owned())
        .build();

    let ready = cart.review(4200);
    assert_eq!(ready.state_data.total_cents, 4200);

    let _paid = ready.pay();
}

§Typed Rehydration

#[validators] lets you rebuild persisted rows back into typed machine states:

use statum::{machine, state, validators, Error};

#[state]
enum TaskState {
    Draft,
    InReview(String),
    Published,
}

#[machine]
struct Task<TaskState> {
    id: u64,
}

struct TaskRow {
    id: u64,
    status: &'static str,
    reviewer: Option<String>,
}

#[validators(Task)]
impl TaskRow {
    fn is_draft(&self) -> statum::Result<()> {
        if self.status == "draft" {
            Ok(())
        } else {
            Err(Error::InvalidState)
        }
    }

    fn is_in_review(&self) -> statum::Result<String> {
        if self.status == "in_review" {
            self.reviewer.clone().ok_or(Error::InvalidState)
        } else {
            Err(Error::InvalidState)
        }
    }

    fn is_published(&self) -> statum::Result<()> {
        if self.status == "published" {
            Ok(())
        } else {
            Err(Error::InvalidState)
        }
    }
}

fn main() -> statum::Result<()> {
    let row = TaskRow {
        id: 7,
        status: "in_review",
        reviewer: Some("alice".to_owned()),
    };

    let row_id = row.id;
    let machine = row.into_machine().id(row_id).build()?;
    match machine {
        task::SomeState::InReview(task) => assert_eq!(task.state_data, "alice"),
        _ => panic!("expected in-review task"),
    }
    Ok(())
}

If you want explainable rebuild traces, validators can also return Validation. Then .build_report() and .build_reports() populate RebuildAttempt::reason_key and RebuildAttempt::message for failed matches while keeping the normal .into_result() surface.

§Compile-Time Gating

Methods only exist on states where you define them.

use statum::{machine, state};

#[state]
enum LightState {
    Off,
    On,
}

#[machine]
struct Light<LightState> {}

let light = Light::<Off>::builder().build();
let _ = light.switch_off(); // no such method on Light<Off>

§Machine Introspection

Statum can also expose the static machine structure as typed metadata. This is useful when the same machine definition should drive:

  • CLI explainers
  • generated docs
  • graph exports
  • exact transition assertions in tests
  • runtime replay or debug tooling

The important detail is that the graph is exact at the transition-site level. A consumer can ask for the legal targets of one specific method on one specific source state.

The graph is derived from macro-expanded, cfg-pruned #[transition] method signatures. Supported return shapes are direct machine returns plus Option, Result, and statum::Branch wrappers around machine types. Unsupported custom decision enums are rejected instead of approximated.

For small amounts of human-facing metadata, Statum can also generate a machine::PRESENTATION constant from #[present(...)] attributes. Add #[presentation_types(...)] on the machine when those attributes should carry typed metadata = ... payloads instead of just labels and descriptions.

use statum::{
    machine, state, transition, MachineIntrospection, MachineTransitionRecorder,
};

#[state]
enum FlowState {
    Fetched,
    Accepted,
    Rejected,
}

#[machine]
struct Flow<FlowState> {}

#[transition]
impl Flow<Fetched> {
    fn validate(self, accept: bool) -> Result<Flow<Accepted>, Flow<Rejected>> {
        if accept {
            Ok(self.accept())
        } else {
            Err(self.reject())
        }
    }

    fn accept(self) -> Flow<Accepted> {
        self.transition()
    }

    fn reject(self) -> Flow<Rejected> {
        self.transition()
    }
}

fn main() {
    let graph = <Flow<Fetched> as MachineIntrospection>::GRAPH;
    let validate = graph
        .transition_from_method(flow::StateId::Fetched, "validate")
        .unwrap();

    assert_eq!(
        graph.legal_targets(validate.id).unwrap(),
        &[flow::StateId::Accepted, flow::StateId::Rejected]
    );

    let event = <Flow<Fetched> as MachineTransitionRecorder>::try_record_transition_to::<
        Flow<Accepted>,
    >(Flow::<Fetched>::VALIDATE)
    .unwrap();

    assert_eq!(event.chosen, flow::StateId::Accepted);
}

Transition ids are exact and typed, but they are exposed as generated associated consts on the source-state machine type, such as Flow::<Fetched>::VALIDATE.

§Where To Look Next

  • Start with state, machine, and transition.
  • For stored rows and database rebuilds, read validators.
  • For append-only event logs, use projection before validator rebuilds.
  • The repository README and docs/ directory contain longer guides and showcase applications.

Modules§

projection
Event-stream projection helpers for Statum rebuild flows.

Structs§

MachineDescriptor
Rust-facing identity for a machine family.
MachineGraph
Structural machine graph emitted from macro-generated metadata.
MachinePresentation
Optional human-facing metadata layered on top of a machine graph.
MachinePresentationDescriptor
Optional machine-level presentation metadata.
RebuildAttempt
One validator evaluation recorded during typed rehydration.
RebuildReport
A typed rehydration result plus the validator attempts that produced it.
RecordedTransition
A runtime record of one chosen transition.
Rejection
A structured validator rejection captured during typed rehydration.
StateDescriptor
Static descriptor for one generated state id.
StatePresentation
Optional state-level presentation metadata.
TransitionDescriptor
Static descriptor for one transition site.
TransitionInventory
Runtime accessor for transition descriptors that may be supplied by a distributed registration surface.
TransitionPresentation
Optional transition-level presentation metadata.
TransitionPresentationInventory
Runtime accessor for transition presentation metadata that may be supplied by a distributed registration surface.

Enums§

Branch
A first-class two-way branching transition result.
Error
Errors returned by Statum runtime helpers.

Traits§

CanTransitionMap
A machine that can transition by mapping its current state data into Next.
CanTransitionTo
A machine that can transition directly to Next.
CanTransitionWith
A machine that can transition using Data.
DataState
A generated state marker that carries payload data.
MachineIntrospection
Static introspection surface emitted for a generated Statum machine.
MachineStateIdentity
Identity for one concrete machine state.
MachineTransitionRecorder
Runtime recording helpers layered on top of static machine introspection.
StateMarker
A generated state marker type.
UnitState
A generated state marker with no payload.

Type Aliases§

Result
Convenience result alias used by Statum APIs.
Validation
An opt-in validator result that carries structured rejection details.

Attribute Macros§

machine
Define a typed machine that carries durable context across states.
state
Define the legal lifecycle phases for a machine.
transition
Validate and generate legal transitions for one source state.
validators
Rebuild typed machines from persisted data.