Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

State Trait refactor #7

Merged
merged 8 commits into from
Oct 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rex-sm"
version = "0.6.0"
version = "0.7.0"
edition = "2021"
description = "Hierarchical state machine"
license = "MIT"
Expand Down
12 changes: 6 additions & 6 deletions src/builder.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::time::Duration;
use std::{sync::Arc, time::Duration};

use bigerror::{ConversionError, Report};
use tokio::{
Expand Down Expand Up @@ -97,7 +97,7 @@ where
}

#[must_use]
pub fn with_timeout_manager(
pub const fn with_timeout_manager(
mut self,
timeout_topic: <K::Message as RexMessage>::Topic,
) -> Self {
Expand All @@ -106,7 +106,7 @@ where
}

#[must_use]
pub fn with_tick_rate(mut self, tick_rate: Duration) -> Self {
pub const fn with_tick_rate(mut self, tick_rate: Duration) -> Self {
self.tick_rate = Some(tick_rate);
self
}
Expand Down Expand Up @@ -216,9 +216,9 @@ where
fn default() -> Self {
Self {
notification_queue: NotificationQueue::new(),
signal_queue: Default::default(),
state_machines: Default::default(),
notification_processors: Default::default(),
signal_queue: Arc::default(),
state_machines: Vec::default(),
notification_processors: Vec::default(),
timeout_topic: None,
tick_rate: None,
outbound_tx: None,
Expand Down
86 changes: 22 additions & 64 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#![allow(clippy::module_name_repetitions)]
use std::fmt;

use bigerror::reportable;
Expand Down Expand Up @@ -32,69 +33,24 @@ pub use timeout::Timeout;
/// enumerations or enumerations whose variants only contain field-less enumerations; note that
/// `Copy` is a required supertrait.
pub trait State: fmt::Debug + Send + PartialEq + Copy {
fn get_kind(&self) -> &dyn Kind<State = Self>;
fn fail(&mut self)
where
Self: Sized,
{
*self = self.get_kind().failed_state();
}
fn complete(&mut self)
where
Self: Sized,
{
*self = self.get_kind().completed_state();
}
fn is_completed(&self) -> bool
where
Self: Sized,
for<'a> &'a Self: PartialEq<&'a Self>,
{
self == &self.get_kind().completed_state()
}

fn is_failed(&self) -> bool
where
Self: Sized,
for<'a> &'a Self: PartialEq<&'a Self>,
{
self == &self.get_kind().failed_state()
}
fn is_new(&self) -> bool
where
Self: Sized,
for<'a> &'a Self: PartialEq<&'a Self>,
{
self == &self.get_kind().new_state()
}

/// represents a state that will no longer change
fn is_terminal(&self) -> bool
where
Self: Sized,
{
self.is_failed() || self.is_completed()
}

/// `&dyn Kind<State = Self>` cannot do direct partial comparison
/// due to type opacity
/// so `State::new_state(self)` is called to allow a vtable lookup
fn kind_eq(&self, kind: &dyn Kind<State = Self>) -> bool
where
Self: Sized,
{
self.get_kind().new_state() == kind.new_state()
}
type Input: Send + Sync + 'static + fmt::Debug;
}

/// Acts as a discriminant between various [`State`] enumerations, similar to
/// [`std::mem::Discriminant`].
/// Used to define the scope for [`Signal`]s cycled through a [`StateMachineManager`].
pub trait Kind: fmt::Debug + Send {
type State: State;
pub trait Kind: fmt::Debug + Send + Sized {
type State: State<Input = Self::Input> + AsRef<Self>;
type Input: Send + Sync + 'static + fmt::Debug;

fn new_state(&self) -> Self::State;
fn failed_state(&self) -> Self::State;
fn completed_state(&self) -> Self::State;
// /// represents a state that will no longer change
fn is_terminal(state: Self::State) -> bool {
let kind = state.as_ref();
kind.completed_state() == state || kind.failed_state() == state
}
}

/// Titular trait of the library that enables Hierarchical State Machine (HSM for short) behaviour.
Expand All @@ -109,13 +65,15 @@ pub trait Kind: fmt::Debug + Send {
/// ```text
///
/// Kind -> Rex::Message
/// :: :: ::
/// State Input Topic
/// :: ::
/// State::Input Topic
/// ```
pub trait Rex: Kind + HashKind {
type Input: Send + Sync + 'static + fmt::Debug;
pub trait Rex: Kind + HashKind
where
Self::State: AsRef<Self>,
{
type Message: RexMessage;
fn state_input(&self, state: <Self as Kind>::State) -> Option<Self::Input>;
fn state_input(&self, state: Self::State) -> Option<Self::Input>;
fn timeout_input(&self, instant: Instant) -> Option<Self::Input>;
}

Expand Down Expand Up @@ -147,23 +105,23 @@ where
f,
"{:?}<{}>",
self.kind,
self.is_nil()
(!self.is_nil())
.then(|| bs58::encode(self.uuid).into_string())
.unwrap_or_else(|| "NIL".to_string())
)
}
}

impl<K: Kind> StateId<K> {
pub fn new(kind: K, uuid: Uuid) -> Self {
pub const fn new(kind: K, uuid: Uuid) -> Self {
Self { kind, uuid }
}

pub fn new_rand(kind: K) -> Self {
Self::new(kind, Uuid::new_v4())
}

pub fn nil(kind: K) -> Self {
pub const fn nil(kind: K) -> Self {
Self::new(kind, Uuid::nil())
}
pub fn is_nil(&self) -> bool {
Expand All @@ -172,7 +130,7 @@ impl<K: Kind> StateId<K> {
// for testing purposes, easily distinguish UUIDs
// by numerical value
#[cfg(test)]
pub fn new_with_u128(kind: K, v: u128) -> Self {
pub const fn new_with_u128(kind: K, v: u128) -> Self {
Self {
kind,
uuid: Uuid::from_u128(v),
Expand Down
Loading