moduforge_rules_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 moduforge_rules_expression::variable::Variable;
10use moduforge_rules_template::TemplateRenderError;
11
12pub trait CustomNodeAdapter {
13 fn handle(
14 &self,
15 request: CustomNodeRequest,
16 ) -> impl std::future::Future<Output = NodeResult>;
17}
18
19#[derive(Default, Debug)]
20pub struct NoopCustomNode;
21
22impl CustomNodeAdapter for NoopCustomNode {
23 async fn handle(
24 &self,
25 _: CustomNodeRequest,
26 ) -> NodeResult {
27 Err(anyhow!("Custom node handler not provided"))
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 TryFrom<NodeRequest> for CustomNodeRequest {
39 type Error = ();
40
41 fn try_from(value: NodeRequest) -> Result<Self, Self::Error> {
42 Ok(Self {
43 input: value.input.clone(),
44 node: value.node.deref().try_into()?,
45 })
46 }
47}
48
49impl CustomNodeRequest {
50 pub fn get_field(
51 &self,
52 path: &str,
53 ) -> Result<Option<Variable>, TemplateRenderError> {
54 let Some(selected_value) = self.get_field_raw(path) else {
55 return Ok(None);
56 };
57
58 let Variable::String(template) = selected_value else {
59 return Ok(Some(selected_value));
60 };
61
62 let template_value = moduforge_rules_template::render(
63 template.as_ref(),
64 self.input.clone(),
65 )?;
66 Ok(Some(template_value))
67 }
68
69 fn get_field_raw(
70 &self,
71 path: &str,
72 ) -> Option<Variable> {
73 self.node.config.dot_get(path).ok().flatten()
74 }
75}
76
77#[derive(Serialize)]
78#[serde(rename_all = "camelCase")]
79pub struct CustomDecisionNode {
80 pub id: String,
81 pub name: String,
82 pub kind: String,
83 pub config: Arc<Value>,
84}
85
86impl TryFrom<&DecisionNode> for CustomDecisionNode {
87 type Error = ();
88
89 fn try_from(value: &DecisionNode) -> Result<Self, Self::Error> {
90 let DecisionNodeKind::CustomNode { content } = &value.kind else {
91 return Err(());
92 };
93
94 Ok(Self {
95 id: value.id.clone(),
96 name: value.name.clone(),
97 kind: content.kind.clone(),
98 config: content.config.clone(),
99 })
100 }
101}