MagicStateMachines 0.1.1

Ergonomic typestate wrappers for compiler-enforced state machines with separable contracts
Documentation
use super::{
    MayTransition, SMapRuntime, SMove, SMut, SPinMut, SPinRef, SRef, State, StateStorage,
    StateStorageNew, TransitionCallsite,
};
use crate::state::owned::{StateOwned, complete_transition};
use crate::{Initial, StateMachineImpl, Transition};
use core::marker::PhantomData;
use core::pin::Pin;
#[cfg(feature = "unique-rc-arc")]
use std::rc::UniqueRc;
#[cfg(feature = "unique-rc-arc")]
use std::sync::UniqueArc;

/// Backend for directly owned values.
pub struct StorageStateOwned;

/// Short alias for [`StorageStateOwned`].
pub type SOwned = StorageStateOwned;

/// Backend for `Box<T>` owned values.
pub struct StorageStateOwnedBox;

/// Backend for `Pin<Box<T>>` owned values.
pub struct StorageStateOwnedPinBox;

/// Backend for `UniqueRc<T>` owned values.
#[cfg(feature = "unique-rc-arc")]
pub struct StorageStateOwnedUniqueRc;

/// Backend for `UniqueArc<T>` owned values.
#[cfg(feature = "unique-rc-arc")]
pub struct StorageStateOwnedUniqueArc;

impl StateStorage for StorageStateOwned {
    type Inner<T, S>
        = StateOwned<T, S>
    where
        T: StateMachineImpl;
    type Machine<T>
        = T
    where
        T: StateMachineImpl;

    fn retag<T, From, To>(inner: Self::Inner<T, From>) -> Self::Inner<T, To>
    where
        T: StateMachineImpl,
    {
        super::retag_owned(inner)
    }
}

impl MayTransition for StorageStateOwned {
    fn complete_transition<T, From, To, Args>(
        state: State<Self, T, From>,
        _args: Args,
        callsite: TransitionCallsite,
    ) -> State<Self, T, To>
    where
        T: StateMachineImpl,
        From: crate::StateTrait,
        To: crate::ConcreteStateTrait,
        T::Standin: Transition<From, To>,
        <T::Standin as Transition<From, To>>::F: crate::TransitionSignature<Args>,
    {
        State {
            inner: complete_transition(state.inner, callsite),
            marker: PhantomData,
        }
    }

    fn complete_transition_after_effect<T, From, To>(
        state: State<Self, T, From>,
        callsite: TransitionCallsite,
    ) -> State<Self, T, To>
    where
        T: StateMachineImpl,
        From: crate::StateTrait,
        To: crate::ConcreteStateTrait,
    {
        State {
            inner: complete_transition(state.inner, callsite),
            marker: PhantomData,
        }
    }
}

impl StateStorageNew for StorageStateOwned {
    fn new<T, S>(value: T) -> Self::Inner<T, S>
    where
        T: StateMachineImpl,
        T::Standin: Initial<S>,
    {
        StateOwned::new(value)
    }
}

impl SRef for StorageStateOwned {
    fn s_ref<T, S>(inner: &Self::Inner<T, S>) -> &T
    where
        T: StateMachineImpl,
    {
        &inner.value
    }
}

impl SMut for StorageStateOwned {
    fn s_mut<T, S>(inner: &mut Self::Inner<T, S>) -> &mut T
    where
        T: StateMachineImpl,
    {
        &mut inner.value
    }
}

impl SMove for StorageStateOwned {}

impl<FromRuntime, ToRuntime> SMapRuntime<FromRuntime, ToRuntime> for StorageStateOwned
where
    FromRuntime: StateMachineImpl,
    ToRuntime: StateMachineImpl,
{
    fn map_runtime<S, F>(state: State<Self, FromRuntime, S>, f: F) -> State<Self, ToRuntime, S>
    where
        F: FnOnce(FromRuntime) -> ToRuntime,
    {
        State {
            inner: StateOwned {
                value: f(state.inner.value),
                state: PhantomData,
                #[cfg(feature = "tracing")]
                trace: state.inner.trace,
            },
            marker: PhantomData,
        }
    }
}

macro_rules! indirect_owned_storage {
    ($storage:ty, $wrapper:ident) => {
        impl StateStorage for $storage {
            type Inner<T, S>
                = StateOwned<$wrapper<T>, S>
            where
                T: StateMachineImpl;
            type Machine<T>
                = $wrapper<T>
            where
                T: StateMachineImpl;

            fn retag<T, From, To>(inner: Self::Inner<T, From>) -> Self::Inner<T, To>
            where
                T: StateMachineImpl,
            {
                super::retag_owned(inner)
            }
        }

        impl MayTransition for $storage {
            fn complete_transition<T, From, To, Args>(
                state: State<Self, T, From>,
                _args: Args,
                callsite: TransitionCallsite,
            ) -> State<Self, T, To>
            where
                T: StateMachineImpl,
                From: crate::StateTrait,
                To: crate::ConcreteStateTrait,
                T::Standin: Transition<From, To>,
                <T::Standin as Transition<From, To>>::F: crate::TransitionSignature<Args>,
            {
                State {
                    inner: complete_transition(state.inner, callsite),
                    marker: PhantomData,
                }
            }

            fn complete_transition_after_effect<T, From, To>(
                state: State<Self, T, From>,
                callsite: TransitionCallsite,
            ) -> State<Self, T, To>
            where
                T: StateMachineImpl,
                From: crate::StateTrait,
                To: crate::ConcreteStateTrait,
            {
                State {
                    inner: complete_transition(state.inner, callsite),
                    marker: PhantomData,
                }
            }
        }

        impl StateStorageNew for $storage {
            fn new<T, S>(value: T) -> Self::Inner<T, S>
            where
                T: StateMachineImpl,
                <Self::Machine<T> as StateMachineImpl>::Standin: Initial<S>,
            {
                StateOwned::new($wrapper::new(value))
            }
        }

        impl SRef for $storage {
            fn s_ref<T, S>(inner: &Self::Inner<T, S>) -> &T
            where
                T: StateMachineImpl,
            {
                &inner.value
            }
        }

        impl SMut for $storage {
            fn s_mut<T, S>(inner: &mut Self::Inner<T, S>) -> &mut T
            where
                T: StateMachineImpl,
            {
                &mut inner.value
            }
        }

        impl SMove for $storage {}
    };

    ($storage:ty, $wrapper:ident, $map:path) => {
        indirect_owned_storage!($storage, $wrapper);
        impl<FromRuntime, ToRuntime> SMapRuntime<FromRuntime, ToRuntime> for $storage
        where
            FromRuntime: StateMachineImpl,
            ToRuntime: StateMachineImpl,
        {
            fn map_runtime<S, F>(
                state: State<Self, FromRuntime, S>,
                f: F,
            ) -> State<Self, ToRuntime, S>
            where
                F: FnOnce(FromRuntime) -> ToRuntime,
            {
                State {
                    inner: StateOwned {
                        value: $map(state.inner.value, f),
                        state: PhantomData,
                        #[cfg(feature = "tracing")]
                        trace: state.inner.trace,
                    },
                    marker: PhantomData,
                }
            }
        }
    };
}

fn map_box<FromRuntime, ToRuntime, F>(value: Box<FromRuntime>, f: F) -> Box<ToRuntime>
where
    F: FnOnce(FromRuntime) -> ToRuntime,
{
    Box::new(f(*value))
}

indirect_owned_storage!(StorageStateOwnedBox, Box, map_box);

#[cfg(feature = "unique-rc-arc")]
indirect_owned_storage!(StorageStateOwnedUniqueRc, UniqueRc);
#[cfg(feature = "unique-rc-arc")]
indirect_owned_storage!(StorageStateOwnedUniqueArc, UniqueArc);

impl StateStorage for StorageStateOwnedPinBox {
    type Inner<T, S>
        = StateOwned<Pin<Box<T>>, S>
    where
        T: StateMachineImpl;
    type Machine<T>
        = Pin<Box<T>>
    where
        T: StateMachineImpl;

    fn retag<T, From, To>(inner: Self::Inner<T, From>) -> Self::Inner<T, To>
    where
        T: StateMachineImpl,
    {
        super::retag_owned(inner)
    }
}

impl MayTransition for StorageStateOwnedPinBox {
    fn complete_transition<T, From, To, Args>(
        state: State<Self, T, From>,
        _args: Args,
        callsite: TransitionCallsite,
    ) -> State<Self, T, To>
    where
        T: StateMachineImpl,
        From: crate::StateTrait,
        To: crate::ConcreteStateTrait,
        T::Standin: Transition<From, To>,
        <T::Standin as Transition<From, To>>::F: crate::TransitionSignature<Args>,
    {
        State {
            inner: complete_transition(state.inner, callsite),
            marker: PhantomData,
        }
    }

    fn complete_transition_after_effect<T, From, To>(
        state: State<Self, T, From>,
        callsite: TransitionCallsite,
    ) -> State<Self, T, To>
    where
        T: StateMachineImpl,
        From: crate::StateTrait,
        To: crate::ConcreteStateTrait,
    {
        State {
            inner: complete_transition(state.inner, callsite),
            marker: PhantomData,
        }
    }
}

impl StateStorageNew for StorageStateOwnedPinBox {
    fn new<T, S>(value: T) -> Self::Inner<T, S>
    where
        T: StateMachineImpl,
        <Self::Machine<T> as StateMachineImpl>::Standin: Initial<S>,
    {
        StateOwned::new(Box::pin(value))
    }
}

impl SRef for StorageStateOwnedPinBox {
    fn s_ref<T, S>(inner: &Self::Inner<T, S>) -> &T
    where
        T: StateMachineImpl,
    {
        &inner.value
    }
}

impl SPinRef for StorageStateOwnedPinBox {
    fn s_pin_ref<T, S>(inner: &Self::Inner<T, S>) -> Pin<&T>
    where
        T: StateMachineImpl,
    {
        inner.value.as_ref()
    }
}

impl SPinMut for StorageStateOwnedPinBox {
    fn s_pin_mut<T, S>(inner: &mut Self::Inner<T, S>) -> Pin<&mut T>
    where
        T: StateMachineImpl,
    {
        inner.value.as_mut()
    }
}

impl SMove for StorageStateOwnedPinBox {}

impl<FromRuntime, ToRuntime> SMapRuntime<FromRuntime, ToRuntime> for StorageStateOwnedPinBox
where
    FromRuntime: StateMachineImpl + Unpin,
    ToRuntime: StateMachineImpl,
{
    fn map_runtime<S, F>(state: State<Self, FromRuntime, S>, f: F) -> State<Self, ToRuntime, S>
    where
        F: FnOnce(FromRuntime) -> ToRuntime,
    {
        State {
            inner: StateOwned {
                value: Box::pin(f(*Pin::into_inner(state.inner.value))),
                state: PhantomData,
                #[cfg(feature = "tracing")]
                trace: state.inner.trace,
            },
            marker: PhantomData,
        }
    }
}