zen_engine/handler/
custom_node_adapter.rs1use 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}