mf_engine/handler/expression/
mod.rs

1use crate::handler::node::{
2    NodeRequest, NodeResponse, NodeResult, PartialTraceError,
3};
4use crate::model::{DecisionNodeKind, ExpressionNodeContent};
5use ahash::{HashMap, HashMapExt};
6use std::sync::Arc;
7
8use anyhow::{anyhow, Context};
9use serde::Serialize;
10use tokio::sync::Mutex;
11use mf_expression::variable::Variable;
12use mf_expression::Isolate;
13
14pub struct ExpressionHandler {
15    trace: bool,
16}
17
18#[derive(Debug, Serialize)]
19struct ExpressionTrace {
20    result: String,
21}
22
23impl ExpressionHandler {
24    pub fn new(trace: bool) -> Self {
25        Self { trace }
26    }
27
28    pub async fn handle(
29        &mut self,
30        request: NodeRequest,
31    ) -> NodeResult {
32        let content = match &request.node.kind {
33            DecisionNodeKind::ExpressionNode { content } => Ok(content),
34            _ => Err(anyhow!("Unexpected node type")),
35        }?;
36
37        let inner_handler_mutex =
38            Arc::new(Mutex::new(ExpressionHandlerInner::new(self.trace)));
39
40        content
41            .transform_attributes
42            .run_with(request.input, |input| {
43                let inner_handler_mutex = inner_handler_mutex.clone();
44
45                async move {
46                    let mut inner_handler_ref =
47                        inner_handler_mutex.lock().await;
48                    inner_handler_ref.handle(input, content).await
49                }
50            })
51            .await
52    }
53}
54
55struct ExpressionHandlerInner<'a> {
56    isolate: Isolate<'a>,
57    trace: bool,
58}
59
60impl<'a> ExpressionHandlerInner<'a> {
61    pub fn new(trace: bool) -> Self {
62        Self { isolate: Isolate::new(), trace }
63    }
64
65    async fn handle(
66        &mut self,
67        input: Variable,
68        content: &'a ExpressionNodeContent,
69    ) -> NodeResult {
70        let result = Variable::empty_object();
71        let mut trace_map =
72            self.trace.then(|| HashMap::<&str, ExpressionTrace>::new());
73
74        self.isolate.set_environment(input.depth_clone(1));
75        for expression in &content.expressions {
76            if expression.key.is_empty() || expression.value.is_empty() {
77                continue;
78            }
79
80            let value = self
81                .isolate
82                .run_standard(&expression.value)
83                .with_context(|| PartialTraceError {
84                    trace: trace_map
85                        .as_ref()
86                        .map(|s| serde_json::to_value(s).ok())
87                        .flatten(),
88                    message: format!(
89                        r#"Failed to evaluate expression: "{}""#,
90                        &expression.value
91                    ),
92                })?;
93
94            if let Some(tmap) = &mut trace_map {
95                tmap.insert(
96                    &expression.key,
97                    ExpressionTrace {
98                        result: serde_json::to_string(&value)
99                            .unwrap_or("Error".to_owned()),
100                    },
101                );
102            }
103
104            self.isolate.update_environment(|env| {
105                let Some(environment) = env else {
106                    return;
107                };
108
109                let key = format!("$.{}", &expression.key);
110                let _ =
111                    environment.dot_insert(key.as_str(), value.depth_clone(2));
112            });
113
114            result.dot_insert(&expression.key, value);
115        }
116
117        Ok(NodeResponse {
118            output: result,
119            trace_data: trace_map
120                .map(|tm| serde_json::to_value(tm).ok())
121                .flatten(),
122        })
123    }
124}