mf_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 mf_expression::variable::Variable;
10use mf_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 =
63            mf_template::render(template.as_ref(), self.input.clone())?;
64        Ok(Some(template_value))
65    }
66
67    fn get_field_raw(
68        &self,
69        path: &str,
70    ) -> Option<Variable> {
71        self.node.config.dot_get(path).ok().flatten()
72    }
73}
74
75#[derive(Serialize)]
76#[serde(rename_all = "camelCase")]
77pub struct CustomDecisionNode {
78    pub id: String,
79    pub name: String,
80    pub kind: String,
81    pub config: Arc<Value>,
82}
83
84impl TryFrom<&DecisionNode> for CustomDecisionNode {
85    type Error = ();
86
87    fn try_from(value: &DecisionNode) -> Result<Self, Self::Error> {
88        let DecisionNodeKind::CustomNode { content } = &value.kind else {
89            return Err(());
90        };
91
92        Ok(Self {
93            id: value.id.clone(),
94            name: value.name.clone(),
95            kind: content.kind.clone(),
96            config: content.config.clone(),
97        })
98    }
99}