zen_engine/nodes/custom/
adapter.rs

1use crate::nodes::result::{NodeError, NodeResult};
2use json_dotpath::DotPaths;
3use serde::Serialize;
4use serde_json::Value;
5use std::fmt::Debug;
6use std::future::Future;
7use std::pin::Pin;
8use std::sync::Arc;
9use zen_expression::variable::Variable;
10use zen_tmpl::TemplateRenderError;
11
12pub trait CustomNodeAdapter: Debug + Send {
13    fn handle(&self, request: CustomNodeRequest) -> Pin<Box<dyn Future<Output = NodeResult> + '_>>;
14}
15
16#[derive(Default, Debug)]
17pub struct NoopCustomNode;
18
19impl CustomNodeAdapter for NoopCustomNode {
20    fn handle(&self, request: CustomNodeRequest) -> Pin<Box<dyn Future<Output = NodeResult>>> {
21        Box::pin(async move {
22            Err(NodeError {
23                trace: None,
24                node_id: request.node.id.clone(),
25                source: "Custom node handler not provided".to_string().into(),
26            })
27        })
28    }
29}
30
31#[derive(Serialize)]
32#[serde(rename_all = "camelCase")]
33pub struct CustomNodeRequest {
34    pub input: Variable,
35    pub node: CustomDecisionNode,
36}
37
38impl CustomNodeRequest {
39    pub fn get_field(&self, path: &str) -> Result<Option<Variable>, TemplateRenderError> {
40        let Some(selected_value) = self.get_field_raw(path) else {
41            return Ok(None);
42        };
43
44        let Variable::String(template) = selected_value else {
45            return Ok(Some(selected_value));
46        };
47
48        let template_value = zen_tmpl::render(template.as_ref(), self.input.clone())?;
49        Ok(Some(template_value))
50    }
51
52    fn get_field_raw(&self, path: &str) -> Option<Variable> {
53        self.node.config.dot_get(path).ok().flatten()
54    }
55}
56
57#[derive(Serialize, Clone)]
58#[serde(rename_all = "camelCase")]
59pub struct CustomDecisionNode {
60    pub id: Arc<str>,
61    pub name: Arc<str>,
62    pub kind: Arc<str>,
63    pub config: Arc<Value>,
64}
65
66pub type DynamicCustomNode = Arc<dyn CustomNodeAdapter + Send + Sync>;