use crate::guard::{Guard, IntoGuard};
use crate::{
action::{Action, IntoAction},
context::Context,
error::Result,
event::{Event, EventTrait},
state::StateTrait,
};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};
use std::fmt::{self, Debug};
use std::marker::PhantomData;
use std::sync::Arc;
use tokio::sync::RwLock;
use uuid::Uuid;
#[derive(Clone, Serialize, Deserialize)]
#[serde(bound(
serialize = "S: Serialize, E: Serialize",
deserialize = "S: DeserializeOwned, E: DeserializeOwned"
))]
pub struct Transition<S = String, C = Context, E = Event>
where
S: StateTrait + Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static + Default + Debug,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
pub source: S,
pub target: Option<S>,
pub event: Option<E>,
#[serde(skip)]
pub guard: Option<Guard<C, E>>,
#[serde(skip)]
pub actions: Vec<Action<C, E>>,
#[serde(default = "uuid::Uuid::new_v4")]
pub(crate) id: Uuid,
pub transition_type: TransitionType,
#[serde(skip)]
_phantom_s: PhantomData<S>,
#[serde(skip)]
_phantom_c: PhantomData<C>,
#[serde(skip)]
_phantom_e: PhantomData<E>,
}
impl<S, C, E> Transition<S, C, E>
where
S: StateTrait + Clone + Send + Sync + 'static,
C: Clone + Send + Sync + 'static + Default + Debug,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
pub fn new(
source: impl Into<S>,
target: Option<impl Into<S>>,
event: Option<E>,
guard: Option<Guard<C, E>>,
actions: Vec<Action<C, E>>,
transition_type: TransitionType,
) -> Self {
Self {
id: Uuid::new_v4(),
source: source.into(),
target: target.map(|t| t.into()),
event,
guard,
actions,
transition_type,
_phantom_s: PhantomData,
_phantom_c: PhantomData,
_phantom_e: PhantomData,
}
}
pub fn self_transition(state: S, event: E) -> Self {
Self {
source: state.clone(),
target: Some(state),
event: Some(event),
guard: None,
actions: Vec::new(),
id: uuid::Uuid::new_v4(),
transition_type: TransitionType::External,
_phantom_s: PhantomData,
_phantom_c: PhantomData,
_phantom_e: PhantomData,
}
}
pub fn internal_transition(source: impl Into<S>, event: E) -> Self
where
S: 'static,
C: Clone + Send + Sync + 'static,
E: EventTrait
+ Send
+ Sync
+ 'static
+ Clone
+ Eq
+ fmt::Debug
+ Serialize
+ DeserializeOwned,
C: Default,
{
Transition::new(
source, None::<S>, Some(event), None, vec![], TransitionType::Internal, )
}
pub fn with_guard(mut self, guard: impl IntoGuard<C, E>) -> Self
where
C: Default + Debug,
{
self.guard = Some(guard.into_guard());
self
}
pub fn with_action(mut self, action: impl IntoAction<C, E>) -> Self
where
C: Default,
{
self.actions.push(action.into_action());
self
}
pub fn matches_event(&self, event: &E) -> bool {
match &self.event {
Some(transition_event) => transition_event == event,
None => true, }
}
pub async fn is_enabled(&self, context: &C, event: &E) -> bool {
self.matches_event(event) && self.check_guard(context, event).await
}
pub async fn execute_actions(&self, context: Arc<RwLock<C>>, event: &E) -> Result<()> {
for action in &self.actions {
let _ = action.execute(context.clone(), event).await;
}
Ok(())
}
pub async fn check_guard(&self, context: &C, event: &E) -> bool {
match &self.guard {
Some(guard) => guard.check(context, event),
None => true, }
}
}
impl<S, C, E> fmt::Debug for Transition<S, C, E>
where
S: StateTrait + fmt::Debug,
C: Clone + Send + Sync + 'static + Debug + Default,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Transition")
.field("source", &self.source)
.field("target", &self.target)
.field("event", &self.event)
.field("guard", &self.guard)
.field("actions", &self.actions)
.field("transition_type", &self.transition_type)
.finish()
}
}
impl<S, C, E> PartialEq for Transition<S, C, E>
where
S: StateTrait + Eq,
C: Clone + Send + Sync + 'static + Default + Debug,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
fn eq(&self, other: &Self) -> bool {
self.source == other.source
&& self.target == other.target
&& self.event == other.event
&& self.transition_type == other.transition_type
}
}
impl<S, C, E> Eq for Transition<S, C, E>
where
S: StateTrait + Eq,
C: Clone + Send + Sync + 'static + Default + Debug,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum TransitionType {
External, Internal, }
impl<C, E> Transition<String, C, E>
where
C: Clone + Send + Sync + 'static + Default + Debug,
E: EventTrait + Send + Sync + 'static + Clone + Eq + fmt::Debug + Serialize + DeserializeOwned,
{
pub fn check(&self, context: &C, event: &E) -> bool {
self.guard.as_ref().is_none_or(|g| g.check(context, event))
}
}