use std::{sync::Arc, time::Duration};
use dashmap::DashMap;
use eyre::Result;
use tracing::warn;
use crate::{
node::base::BaseNode,
types::{ActionKey, DEFAULT_ACTION, Params, SharedState},
};
pub struct Node<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
params: Params,
successors: DashMap<ActionKey, Arc<dyn BaseNode>>,
exec_fn: F,
max_retries: usize,
wait_ms: u64,
}
impl<F> Node<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
pub fn new(exec_fn: F) -> Self {
Self {
params: Default::default(),
successors: DashMap::new(),
exec_fn,
max_retries: 1,
wait_ms: 0,
}
}
pub fn set_params(&mut self, params: Params) -> &mut Self {
self.params = params;
self
}
pub fn max_retries(&mut self, retries: usize) -> &mut Self {
self.max_retries = retries;
self
}
pub fn wait_ms(&mut self, wait: u64) -> &mut Self {
self.wait_ms = wait;
self
}
pub fn next(&mut self, node: Arc<dyn BaseNode>, action: &str) -> &mut Self {
let action_key = action.to_string();
if self.successors.contains_key(&action_key) {
warn!("Overwriting successor for action '{}'", action);
}
self.successors.insert(action_key, node);
self
}
pub fn get_param<T>(&self, key: &str) -> Option<T>
where
T: serde::de::DeserializeOwned,
{
self.params
.get(key)
.and_then(|v| serde_json::from_value(v.clone()).ok())
}
}
impl<F> BaseNode for Node<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
fn prep(&self, _shared: &SharedState) -> Result<serde_json::Value> {
Ok(serde_json::to_value(&self.params)?)
}
fn exec(&self, prep_res: &serde_json::Value) -> Result<serde_json::Value> {
let mut last_error = None;
for retry in 0..self.max_retries {
match (self.exec_fn)(prep_res) {
Ok(result) => return Ok(result),
Err(e) => {
last_error = Some(e);
if retry < self.max_retries - 1 && self.wait_ms > 0 {
std::thread::sleep(Duration::from_millis(self.wait_ms));
}
}
}
}
Err(last_error.unwrap_or_else(|| eyre::eyre!("Execution failed with unknown error")))
}
fn post(
&self,
_shared: &SharedState,
_prep_res: &serde_json::Value,
_exec_res: &serde_json::Value,
) -> Result<String> {
Ok(DEFAULT_ACTION.to_string())
}
}
pub struct BatchNode<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
inner: Node<F>,
}
impl<F> BatchNode<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
pub fn new(exec_fn: F) -> Self {
Self {
inner: Node::new(exec_fn),
}
}
pub fn set_params(&mut self, params: Params) -> &mut Self {
self.inner.set_params(params);
self
}
pub fn max_retries(&mut self, retries: usize) -> &mut Self {
self.inner.max_retries(retries);
self
}
pub fn wait_ms(&mut self, wait: u64) -> &mut Self {
self.inner.wait_ms(wait);
self
}
pub fn next(&mut self, node: Arc<dyn BaseNode>, action: &str) -> &mut Self {
self.inner.next(node, action);
self
}
}
impl<F> BaseNode for BatchNode<F>
where
F: Fn(&serde_json::Value) -> Result<serde_json::Value> + Send + Sync + 'static,
{
fn prep(&self, shared: &SharedState) -> Result<serde_json::Value> {
self.inner.prep(shared)
}
fn exec(&self, prep_res: &serde_json::Value) -> Result<serde_json::Value> {
let items = prep_res
.as_array()
.ok_or_else(|| eyre::eyre!("Batch input must be an array"))?;
let mut results = Vec::with_capacity(items.len());
for item in items {
results.push(self.inner.exec(item)?);
}
Ok(serde_json::to_value(results)?)
}
fn post(
&self,
shared: &SharedState,
prep_res: &serde_json::Value,
exec_res: &serde_json::Value,
) -> Result<String> {
self.inner.post(shared, prep_res, exec_res)
}
}