use anyhow::{anyhow, Result};
use async_trait::async_trait;
use slotmap::{new_key_type, SlotMap};
use std::collections::HashMap;
use std::any::Any;
new_key_type! {
struct NodeKey;
}
pub enum EngineControl {
Continue,
JumpToNode(String), AddNode(String, Box<dyn AnyNode>), End,
}
#[async_trait]
pub trait AnyNode: Send + Sync {
async fn enter(&self, ctx: &mut (dyn Any + Send)) -> Result<EngineControl>;
}
#[async_trait]
pub trait NodeTrait<C: Send>: Send + Sync {
async fn enter(&self, ctx: &mut C) -> Result<EngineControl>;
}
pub struct NodeWrapper<N, C> {
node: N,
_phantom: std::marker::PhantomData<C>,
}
impl<N, C> NodeWrapper<N, C> {
pub fn new(node: N) -> Self {
Self {
node,
_phantom: std::marker::PhantomData,
}
}
}
#[async_trait]
impl<N, C> AnyNode for NodeWrapper<N, C>
where
N: NodeTrait<C> + Send + Sync,
C: Send + Sync + 'static + Any,
{
async fn enter(&self, ctx: &mut (dyn Any + Send)) -> Result<EngineControl> {
let ctx = ctx.downcast_mut::<C>().ok_or_else(|| {
anyhow!("Context type mismatch in node execution")
})?;
self.node.enter(ctx).await
}
}
pub struct Engine<C: Send + Sync> {
nodes: SlotMap<NodeKey, Box<dyn AnyNode>>,
node_names: HashMap<String, NodeKey>,
current: Option<NodeKey>,
pub ctx: C,
}
impl<C: Send + Sync + 'static> Engine<C> {
pub fn new(ctx: C) -> Self {
Self {
nodes: SlotMap::with_key(),
node_names: HashMap::new(),
current: None,
ctx,
}
}
pub fn add_node<N>(&mut self, name: &str, node: N) -> Result<()>
where
N: NodeTrait<C> + 'static + Send + Sync,
{
if self.node_names.contains_key(name) {
return Err(anyhow!("Node with name '{}' already exists", name));
}
let wrapper = NodeWrapper {
node,
_phantom: std::marker::PhantomData,
};
let key = self.nodes.insert(Box::new(wrapper));
self.node_names.insert(name.to_string(), key);
Ok(())
}
pub fn set_start_node(&mut self, name: &str) -> Result<()> {
let key = self.node_names.get(name)
.ok_or_else(|| anyhow!("Node '{}' not found", name))?;
self.current = Some(*key);
Ok(())
}
pub async fn run(&mut self) -> Result<()> {
while let Some(curr_key) = self.current {
let node = self.nodes.get(curr_key)
.ok_or_else(|| anyhow!("Node key not found in engine"))?;
let ctrl = node.enter(&mut self.ctx as &mut (dyn Any + Send)).await?;
match ctrl {
EngineControl::Continue => {
}
EngineControl::JumpToNode(target_name) => {
let target_key = self.node_names.get(&target_name)
.ok_or_else(|| anyhow!("Target node '{}' not found", target_name))?;
self.current = Some(*target_key);
}
EngineControl::AddNode(name, new_node) => {
if self.node_names.contains_key(&name) {
return Err(anyhow!("Node with name '{}' already exists", name));
}
let key = self.nodes.insert(new_node);
self.node_names.insert(name, key);
}
EngineControl::End => {
self.current = None;
}
}
}
Ok(())
}
}