moduforge_rules_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 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}