MagicStateMachines 0.1.2

Ergonomic typestate wrappers for compiler-enforced state machines with separable contracts
Documentation
use crate::{
    StateMachineImpl, StateStorage, StateTrait, StateUnionDiscriminant, StateUnionState,
    StateUnionTransitionProof, TransitionProof,
};

/// Classifies state marker types.
///
/// End users usually interact with this indirectly through [`States!`](macro@crate::States) and
/// [`StateUnion!`](macro@crate::StateUnion). Concrete states use [`ConcreteStateKind`], while generated
/// union markers use [`UnionStateKind`].
///
/// The kind is what lets generic transition helpers choose different proof
/// machinery without exposing separate public transition methods. A concrete
/// state proves transitions directly from `From -> To`. A union marker proves
/// transitions through the generated union membership/discrimination logic.
///
/// You normally do not implement this trait. If you need custom state markers,
/// prefer wrapping them in [`States!`](macro@crate::States) so the marker kind,
/// erased-state support, and ZST invariants are all wired consistently.
pub trait StateKind: Sized {
    /// Runtime marker actually checked by shared storage for this kind.
    ///
    /// Concrete states resolve to themselves. Union markers resolve to their
    /// generated joint [`StateUnionState`].
    type RuntimeState<Marker>: StateTrait + StateMarker
    where
        Marker: StateRuntimeMarkerFor<Self>;

    /// Proof type used by generated transition helpers for this kind.
    ///
    /// This associated type lets the macro-generated transition code select
    /// concrete-state proof logic or union-state proof logic without exposing
    /// separate public methods.
    type Proof<T, From, Marker, To>
    where
        T: StateMachineImpl,
        From: StateTrait + StateMarker<Kind = Self>,
        Marker: StateUnionDiscriminant,
        To: StateTrait + StateMarker<Kind = ConcreteStateKind>;

    #[doc(hidden)]
    #[must_use]
    fn prove<Storage, T, From, Marker, To>() -> TransitionProof<Storage, T, From, Marker, To, Self>
    where
        Storage: StateStorage,
        T: StateMachineImpl,
        From: StateTrait + StateMarker<Kind = Self>,
        Marker: StateUnionDiscriminant,
        To: StateTrait + StateMarker<Kind = ConcreteStateKind>,
    {
        TransitionProof::new()
    }
}

/// Marker kind for concrete state ZSTs.
///
/// This is the kind assigned by [`States!`](macro@crate::States). Concrete
/// states can be stored as the authoritative state in shared storage and can
/// appear as concrete transition sources or targets.
pub struct ConcreteStateKind;

impl StateKind for ConcreteStateKind {
    type RuntimeState<Marker>
        = <Marker as StateRuntimeMarkerFor<Self>>::RuntimeState
    where
        Marker: StateRuntimeMarkerFor<Self>;

    type Proof<T, From, Marker, To>
        = crate::StateConcreteTransitionProof<T, From, Marker, To>
    where
        T: StateMachineImpl,
        From: StateTrait + StateMarker<Kind = Self>,
        Marker: StateUnionDiscriminant,
        To: StateTrait + StateMarker<Kind = ConcreteStateKind>;
}

/// Marker kind for generated union state markers.
///
/// This is the kind assigned by [`StateUnion!`](macro@crate::StateUnion).
/// Union markers are accepted as borrowed views and generic receiver bounds,
/// but they are not valid committed states in shared storage. Shared storage
/// commits the concrete member marker and checks union borrows against it.
pub struct UnionStateKind;

impl StateKind for UnionStateKind {
    type RuntimeState<Marker>
        = <Marker as StateRuntimeMarkerFor<Self>>::RuntimeState
    where
        Marker: StateRuntimeMarkerFor<Self>;

    type Proof<T, From, Marker, To>
        = StateUnionTransitionProof<T, From, Marker, To>
    where
        T: StateMachineImpl,
        From: StateTrait + StateMarker<Kind = Self>,
        Marker: StateUnionDiscriminant,
        To: StateTrait + StateMarker<Kind = ConcreteStateKind>;
}

/// Common trait implemented by concrete states and generated union markers.
///
/// Concrete state ZSTs generated by [`States!`](macro@crate::States) implement this with
/// [`ConcreteStateKind`]. Union markers generated by [`StateUnion!`](macro@crate::StateUnion) implement
/// it with [`UnionStateKind`].
///
/// Manual implementations are advanced API. The implementation must be a ZST
/// marker and must return a stable erased marker for concrete states. This is
/// why the recommended definition-crate pattern is:
///
/// ```ignore
/// pub mod states {
///     magicstatemachines::States! {
///         /// The machine is idle.
///         Idle;
///         /// The machine is running.
///         Running;
///     }
/// }
/// ```
pub trait StateMarker: 'static {
    /// Whether this marker is a concrete state or a union marker.
    type Kind: StateKind;

    #[doc(hidden)]
    fn erased_state() -> &'static dyn StateTrait
    where
        Self: Sized;
}

impl<Marker> StateMarker for StateUnionState<Marker>
where
    Marker: StateUnionDiscriminant + 'static,
{
    type Kind = UnionStateKind;

    fn erased_state() -> &'static dyn StateTrait {
        panic!("union state markers cannot be stored as ErasedState")
    }
}

#[doc(hidden)]
pub trait StateRuntimeMarkerFor<Kind: StateKind>: StateTrait + StateMarker {
    type RuntimeState: StateTrait + StateMarker;
}

impl<Marker> StateRuntimeMarkerFor<ConcreteStateKind> for Marker
where
    Marker: StateTrait + StateMarker<Kind = ConcreteStateKind>,
{
    type RuntimeState = Marker;
}

impl<Marker> StateRuntimeMarkerFor<UnionStateKind> for Marker
where
    Marker: StateUnionDiscriminant + StateTrait,
    StateUnionState<Marker>: StateTrait,
{
    type RuntimeState = StateUnionState<Marker>;
}

/// Runtime marker used when borrowing `Marker` from shared storage.
///
/// For concrete states this is `Marker`. For union markers this is the
/// generated joint [`StateUnionState<Marker>`].
///
/// This alias is mostly visible in shared-storage return types. For example,
/// `shared.borrow::<Connected>()` returns an immutable
/// `State<StorageStateRef<...>, T, Connected>` view, while
/// `shared.borrow::<Online>()` returns an immutable
/// `State<StorageStateRef<...>, T, StateUnionState<Online>>` view.
pub type RuntimeStateMarker<Marker> =
    <<Marker as StateMarker>::Kind as StateKind>::RuntimeState<Marker>;