pub struct State<Storage, T, S>where
T: StateMachineImpl,
Storage: StateStorage,{ /* private fields */ }Expand description
A state token parameterized by storage, runtime implementation, and state.
The type parameters carry the whole proof:
Storageselects how the runtime value is held, for exampleSOwned,StorageStateOwnedBox, or a mutable guard from shared storage.Tis the runtime implementation type, such asConnection.Sis the current state marker, such asDisconnectedorimpl InOnline.
State is intentionally a deref-like wrapper with very few inherent
methods. State-machine behavior should be implemented on T with
arbitrary self types, not added as generic convenience methods on State.
That keeps the transition capability private to the implementation module.
impl Connection {
fn connect<S>(self: State<S, Self, Disconnected>) -> State<S, Self, Connected>
where
S: SMut,
{
magicstatemachines::transition!(self)
}
fn endpoint<S>(self: &State<S, Self, impl InOnline>) -> &str
where
S: SRef,
{
&self.endpoint
}
}Implementations§
Source§impl<T, S> State<StorageStateOwned, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwned, T, S>where
T: StateMachineImpl,
Sourcepub fn new(value: T) -> Self
pub fn new(value: T) -> Self
Wraps an implementation in an initial directly owned state.
This is the normal entry point for a state machine. It only compiles
when the definition crate declared S as an initial state for
T::Standin. That declaration is a public constructor contract:
anyone with raw T can create State<SOwned, T, S> for an initial
state. States that should only be entered by target-owned conversions
should not be listed as Initial.
use magicstatemachines::{SOwned, State};
use test_def::states::Disconnected;
let disconnected: State<SOwned, Connection, Disconnected> =
State::new(Connection::new("localhost:8080"));To move the state into another owned container later, create that
container from the returned state token instead of constructing raw
T again.
Sourcepub fn from_concrete(value: ConcreteStated<T, S>) -> Selfwhere
S: ConcreteStateTrait,
pub fn from_concrete(value: ConcreteStated<T, S>) -> Selfwhere
S: ConcreteStateTrait,
Constructs a directly owned state from implementation-owned state proof.
ConcreteStated is where the unsafe or private raw construction
decision happens. This function only consumes that proof object and
creates the ordinary owned state wrapper.
Sourcepub fn into_concrete(state: Self) -> ConcreteStated<T, S>where
S: ConcreteStateTrait,
pub fn into_concrete(state: Self) -> ConcreteStated<T, S>where
S: ConcreteStateTrait,
Consumes a directly owned state and returns raw data plus its concrete marker.
This is intended for implementation-owned conversions. The returned
value can be inspected only by consuming it with
ConcreteStated::into_raw.
Source§impl<T, S> State<StorageStateOwnedBox, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwnedBox, T, S>where
T: StateMachineImpl,
Sourcepub fn new(state: State<StorageStateOwned, T, S>) -> Self
pub fn new(state: State<StorageStateOwned, T, S>) -> Self
Moves a directly owned state into Box storage without changing its state.
This mirrors Box::new, but it takes State<SOwned, T, S> instead of
raw T so the current state is preserved:
let owned: State<SOwned, Connection, Connected> = connection.connect();
let boxed: SBox<Connection, Connected> = SBox::new(owned);Sourcepub fn unbox(state: Self) -> State<StorageStateOwned, T, S>
pub fn unbox(state: Self) -> State<StorageStateOwned, T, S>
Moves this boxed state back into direct owned storage.
This consumes the box and returns State<SOwned, T, S>. The state
marker is unchanged, so methods available before boxing remain
available after unboxing.
let boxed: SBox<Connection, Connected> = SBox::new(owned);
let owned_again: State<SOwned, Connection, Connected> = SBox::unbox(boxed);Source§impl<T, S> State<StorageStateOwnedPinBox, T, S>where
T: StateMachineImpl,
impl<T, S> State<StorageStateOwnedPinBox, T, S>where
T: StateMachineImpl,
Sourcepub fn new(state: State<StorageStateOwnedBox, T, S>) -> Self
pub fn new(state: State<StorageStateOwnedBox, T, S>) -> Self
Pins an already boxed state in place without changing its state.
Pinning must happen to the boxed allocation. For that reason this
constructor consumes SBox rather than SOwned:
let boxed: SBox<Connection, Connected> = SBox::new(owned);
let pinned: SPinBox<Connection, Connected> = SPinBox::new(boxed);Sourcepub fn into_boxed(state: Self) -> State<StorageStateOwnedBox, T, S>where
T: Unpin,
pub fn into_boxed(state: Self) -> State<StorageStateOwnedBox, T, S>where
T: Unpin,
Converts pinned box storage back to box storage when the runtime is Unpin.
This is only available for T: Unpin; otherwise the pinning guarantee
must be preserved.
use core::marker::PhantomPinned;
use magicstatemachines::{
Initial, SBox, SOwned, SPinBox, State, StateMachineImpl, States,
};
struct Machine;
struct Runtime {
_pin: PhantomPinned,
}
struct Token;
States! {
Ready;
}
impl Initial<Ready> for Machine {}
impl StateMachineImpl for Runtime {
type Standin = Machine;
type Impl = Self;
type TransitionToken = Token;
}
let ready = State::<SOwned, _, Ready>::new(Runtime {
_pin: PhantomPinned,
});
let pinned: SPinBox<Runtime, Ready> = SPinBox::new(SBox::new(ready));
// `Runtime` is `!Unpin`, so the pinned allocation cannot be turned
// back into an ordinary box.
let _boxed = SPinBox::into_boxed(pinned);Source§impl<Storage, T, Marker> State<SDiscriminated<Storage>, T, StateUnionState<Marker>>
impl<Storage, T, Marker> State<SDiscriminated<Storage>, T, StateUnionState<Marker>>
Sourcepub fn discriminate(
self,
) -> <Marker as StateUnionDiscriminant>::Enum<Storage, T>
pub fn discriminate( self, ) -> <Marker as StateUnionDiscriminant>::Enum<Storage, T>
Recovers the generated enum for this discriminated union state.
This is the runtime branch point: after matching on the enum, each variant can be converted back into its concrete state.
match online.discriminate() {
OnlineEnum::Connected(connected) => {
let authenticated = connected.authenticate("alice");
}
OnlineEnum::Authenticated(authenticated) => {
let connected = authenticated.logout();
}
}