use super::state::State;
use std::marker::PhantomData;
use std::sync::Arc;
pub struct Guard<S: State> {
predicate: Arc<dyn Fn(&S) -> bool + Send + Sync>,
_phantom: PhantomData<S>,
}
impl<S: State> Clone for Guard<S> {
fn clone(&self) -> Self {
Guard {
predicate: Arc::clone(&self.predicate),
_phantom: PhantomData,
}
}
}
impl<S: State> Guard<S> {
pub fn new<F>(predicate: F) -> Self
where
F: Fn(&S) -> bool + Send + Sync + 'static,
{
Guard {
predicate: Arc::new(predicate),
_phantom: PhantomData,
}
}
pub fn check(&self, state: &S) -> bool {
(self.predicate)(state)
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde::{Deserialize, Serialize};
#[derive(Clone, PartialEq, Debug, Serialize, Deserialize)]
enum TestState {
Initial,
Processing,
Complete,
Failed,
}
impl State for TestState {
fn name(&self) -> &str {
match self {
Self::Initial => "Initial",
Self::Processing => "Processing",
Self::Complete => "Complete",
Self::Failed => "Failed",
}
}
fn is_final(&self) -> bool {
matches!(self, Self::Complete | Self::Failed)
}
fn is_error(&self) -> bool {
matches!(self, Self::Failed)
}
}
#[test]
fn guard_allows_matching_states() {
let guard = Guard::new(|s: &TestState| matches!(s, TestState::Initial));
assert!(guard.check(&TestState::Initial));
assert!(!guard.check(&TestState::Processing));
}
#[test]
fn guard_checks_non_final_states() {
let guard = Guard::new(|s: &TestState| !s.is_final());
assert!(guard.check(&TestState::Initial));
assert!(guard.check(&TestState::Processing));
assert!(!guard.check(&TestState::Complete));
assert!(!guard.check(&TestState::Failed));
}
#[test]
fn guard_checks_non_error_states() {
let guard = Guard::new(|s: &TestState| !s.is_error());
assert!(guard.check(&TestState::Initial));
assert!(guard.check(&TestState::Processing));
assert!(guard.check(&TestState::Complete));
assert!(!guard.check(&TestState::Failed));
}
#[test]
fn guard_is_deterministic() {
let state = TestState::Processing;
let guard = Guard::new(|s: &TestState| !s.is_final());
let result1 = guard.check(&state);
let result2 = guard.check(&state);
assert_eq!(result1, result2);
}
#[test]
fn guard_can_use_complex_predicates() {
let guard =
Guard::new(|s: &TestState| matches!(s, TestState::Initial | TestState::Processing));
assert!(guard.check(&TestState::Initial));
assert!(guard.check(&TestState::Processing));
assert!(!guard.check(&TestState::Complete));
assert!(!guard.check(&TestState::Failed));
}
}