use async_trait::async_trait;
use dashmap::DashMap;
use crate::callbacks::Finitomata;
use crate::error::{FinitomataError, PersistencyError};
use crate::state::Lifecycle;
use super::Persistency;
pub struct InMemoryPersistency<F: Finitomata> {
store: DashMap<String, (Lifecycle, F::State, F::Payload)>,
}
impl<F: Finitomata> InMemoryPersistency<F> {
pub fn new() -> Self {
Self {
store: DashMap::new(),
}
}
pub fn len(&self) -> usize {
self.store.len()
}
pub fn is_empty(&self) -> bool {
self.store.is_empty()
}
pub fn contains(&self, id: &str) -> bool {
self.store.contains_key(id)
}
pub fn remove(&self, id: &str) -> Option<(Lifecycle, F::State, F::Payload)> {
self.store.remove(id).map(|(_, v)| v)
}
}
impl<F: Finitomata> Default for InMemoryPersistency<F> {
fn default() -> Self {
Self::new()
}
}
#[async_trait]
impl<F: Finitomata> Persistency<F> for InMemoryPersistency<F>
where
F::State: Clone + Send + Sync,
F::Payload: Clone + Send + Sync,
{
async fn load(
&self,
id: &str,
) -> Result<Option<(Lifecycle, F::State, F::Payload)>, PersistencyError> {
Ok(self.store.get(id).map(|entry| entry.value().clone()))
}
async fn store(
&self,
id: &str,
state: &F::State,
payload: &F::Payload,
) -> Result<(), PersistencyError> {
self.store.insert(
id.to_string(),
(Lifecycle::Running, state.clone(), payload.clone()),
);
Ok(())
}
async fn store_error(
&self,
_id: &str,
_error: &FinitomataError,
) -> Result<(), PersistencyError> {
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::callbacks::{Finitomata, TransitionResult};
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
enum S {
A,
B,
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[allow(dead_code)]
enum E {
Go,
}
#[derive(Debug, Clone)]
struct P(u32);
struct TestFsm;
#[async_trait]
impl Finitomata for TestFsm {
type State = S;
type Event = E;
type Payload = P;
async fn on_transition(
&mut self,
_from: &S,
_event: &E,
_ep: &P,
_sp: &mut P,
) -> TransitionResult<S, P> {
TransitionResult::Ok(S::B)
}
}
#[tokio::test]
async fn test_store_and_load() {
let persist = InMemoryPersistency::<TestFsm>::new();
persist.store("fsm1", &S::A, &P(42)).await.unwrap();
let loaded = persist.load("fsm1").await.unwrap().unwrap();
assert_eq!(loaded.1, S::A);
assert_eq!(loaded.2.0, 42);
}
#[tokio::test]
async fn test_load_missing() {
let persist = InMemoryPersistency::<TestFsm>::new();
let loaded = persist.load("missing").await.unwrap();
assert!(loaded.is_none());
}
}