use crate::context::Context;
use crate::error::StateError;
use crate::event::{Event, EventTrait};
use serde::Serialize;
use std::fmt;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
use tokio::sync::RwLock;
pub type AsyncGuardPredicate<C, E> = Arc<
dyn Fn(Arc<RwLock<C>>, &E) -> Pin<Box<dyn Future<Output = Result<bool, StateError>> + Send>>
+ Send
+ Sync,
>;
#[derive(Clone, Serialize)]
pub struct Guard<C = Context, E = Event>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
pub name: String,
#[serde(skip)]
pub condition: GuardCondition<C, E>,
}
pub type GuardCondition<C, E> = Arc<dyn Fn(&C, &E) -> bool + Send + Sync>;
impl<C, E> Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
pub fn new<F>(name: impl Into<String>, condition: F) -> Self
where
F: Fn(&C, &E) -> bool + Send + Sync + 'static,
{
Self {
name: name.into(),
condition: Arc::new(condition),
}
}
pub fn named(name: impl Into<String>) -> Self {
Self {
name: name.into(),
condition: Arc::new(|_ctx, _evt| false),
}
}
pub fn check(&self, context: &C, event: &E) -> bool {
(self.condition)(context, event)
}
}
impl<C, E> fmt::Display for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Guard({})", self.name)
}
}
pub trait IntoGuard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
Self: Sized,
{
fn into_guard(self) -> Guard<C, E>;
}
impl<C, E> IntoGuard<C, E> for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn into_guard(self) -> Guard<C, E> {
self
}
}
impl<C, E, F> IntoGuard<C, E> for (&str, F)
where
F: Fn(&C, &E) -> bool + Send + Sync + 'static,
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn into_guard(self) -> Guard<C, E> {
Guard::new(self.0, self.1)
}
}
impl<C, E> IntoGuard<C, E> for &str
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn into_guard(self) -> Guard<C, E> {
Guard::named(self)
}
}
impl<C, E> IntoGuard<C, E> for String
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn into_guard(self) -> Guard<C, E> {
Guard::named(self)
}
}
impl<C, E> fmt::Debug for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Guard")
.field("name", &self.name)
.field("condition", &"<Fn(&C, &E) -> bool>")
.finish()
}
}
impl<C, E> PartialEq for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn eq(&self, other: &Self) -> bool {
self.name == other.name
}
}
impl<C, E> Eq for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
}
impl<C, E> Default for Guard<C, E>
where
C: Send + Sync + 'static + Default + Clone + fmt::Debug,
E: EventTrait + Send + Sync + 'static,
{
fn default() -> Self {
Self {
name: "default_guard".to_string(),
condition: Arc::new(|_ctx, _evt| true),
}
}
}