zen_engine/handler/
custom_node_adapter.rs

1use crate::handler::node::{NodeRequest, NodeResult};
2use crate::model::{DecisionNode, DecisionNodeKind};
3use anyhow::anyhow;
4use json_dotpath::DotPaths;
5use serde::Serialize;
6use serde_json::Value;
7use std::ops::Deref;
8use std::sync::Arc;
9use zen_expression::variable::Variable;
10use zen_tmpl::TemplateRenderError;
11
12pub trait CustomNodeAdapter {
13    fn handle(&self, request: CustomNodeRequest) -> impl std::future::Future<Output = NodeResult>;
14}
15
16#[derive(Default, Debug)]
17pub struct NoopCustomNode;
18
19impl CustomNodeAdapter for NoopCustomNode {
20    async fn handle(&self, _: CustomNodeRequest) -> NodeResult {
21        Err(anyhow!("Custom node handler not provided"))
22    }
23}
24
25#[derive(Serialize)]
26#[serde(rename_all = "camelCase")]
27pub struct CustomNodeRequest {
28    pub input: Variable,
29    pub node: CustomDecisionNode,
30}
31
32impl TryFrom<NodeRequest> for CustomNodeRequest {
33    type Error = ();
34
35    fn try_from(value: NodeRequest) -> Result<Self, Self::Error> {
36        Ok(Self {
37            input: value.input.clone(),
38            node: value.node.deref().try_into()?,
39        })
40    }
41}
42
43impl CustomNodeRequest {
44    pub fn get_field(&self, path: &str) -> Result<Option<Variable>, TemplateRenderError> {
45        let Some(selected_value) = self.get_field_raw(path) else {
46            return Ok(None);
47        };
48
49        let Variable::String(template) = selected_value else {
50            return Ok(Some(selected_value));
51        };
52
53        let template_value = zen_tmpl::render(template.as_ref(), self.input.clone())?;
54        Ok(Some(template_value))
55    }
56
57    fn get_field_raw(&self, path: &str) -> Option<Variable> {
58        self.node.config.dot_get(path).ok().flatten()
59    }
60}
61
62#[derive(Serialize)]
63#[serde(rename_all = "camelCase")]
64pub struct CustomDecisionNode {
65    pub id: String,
66    pub name: String,
67    pub kind: String,
68    pub config: Arc<Value>,
69}
70
71impl TryFrom<&DecisionNode> for CustomDecisionNode {
72    type Error = ();
73
74    fn try_from(value: &DecisionNode) -> Result<Self, Self::Error> {
75        let DecisionNodeKind::CustomNode { content } = &value.kind else {
76            return Err(());
77        };
78
79        Ok(Self {
80            id: value.id.clone(),
81            name: value.name.clone(),
82            kind: content.kind.clone(),
83            config: content.config.clone(),
84        })
85    }
86}