use std::collections::HashMap;
use std::sync::Arc;
use async_trait::async_trait;
use crate::context::NodeContext;
use crate::error::GraphError;
use crate::graph::{Node, NodeOutput, State};
pub struct ChildRunner<S: State> {
pub(crate) nodes: HashMap<String, Arc<dyn Node<S>>>,
}
impl<S: State> ChildRunner<S> {
pub async fn run_child(&self, child_name: &str, ctx: &NodeContext, state: S) -> Result<NodeOutput<S>, GraphError> {
let node = self
.nodes
.get(child_name)
.ok_or_else(|| GraphError::Fatal(format!("Child node '{}' not found in graph", child_name)))?;
let child_ctx = NodeContext::new(
ctx.thread_id.clone(),
child_name.to_string(),
0, ctx.last_checkpoint_id.clone(),
ctx.budget_remaining_eur,
ctx.cancellation.clone(),
None, );
match node.call(child_ctx, state).await {
Err(GraphError::Yield(request)) => Err(GraphError::YieldInDynamicNode {
interrupt_id: request.interrupt_id,
}),
other => other,
}
}
pub async fn run_child_state(&self, child_name: &str, ctx: &NodeContext, state: S) -> Result<S, GraphError> {
self.run_child(child_name, ctx, state).await.map(|output| output.state)
}
}
#[async_trait]
pub trait DynamicNode<S: State>: Send + Sync {
async fn call(&self, ctx: NodeContext, state: S, runner: &ChildRunner<S>) -> Result<NodeOutput<S>, GraphError>;
}
pub struct DynamicFnNode<F> {
pub(crate) f: F,
}
#[async_trait]
impl<S, F, Fut> DynamicNode<S> for DynamicFnNode<F>
where
S: State,
F: Fn(NodeContext, S, &ChildRunner<S>) -> Fut + Send + Sync,
Fut: std::future::Future<Output = Result<NodeOutput<S>, GraphError>> + Send,
{
async fn call(&self, ctx: NodeContext, state: S, runner: &ChildRunner<S>) -> Result<NodeOutput<S>, GraphError> {
(self.f)(ctx, state, runner).await
}
}