moduforge-rules-engine 0.5.0

moduforge 引擎规则
Documentation
use crate::handler::node::{
    NodeRequest, NodeResponse, NodeResult, PartialTraceError,
};
use crate::model::{DecisionNodeKind, ExpressionNodeContent};
use ahash::{HashMap, HashMapExt};
use std::sync::Arc;

use anyhow::{anyhow, Context};
use serde::Serialize;
use tokio::sync::Mutex;
use mf_expression::variable::Variable;
use mf_expression::Isolate;

pub struct ExpressionHandler {
    trace: bool,
}

#[derive(Debug, Serialize)]
struct ExpressionTrace {
    result: String,
}

impl ExpressionHandler {
    pub fn new(trace: bool) -> Self {
        Self { trace }
    }

    pub async fn handle(
        &mut self,
        request: NodeRequest,
    ) -> NodeResult {
        let content = match &request.node.kind {
            DecisionNodeKind::ExpressionNode { content } => Ok(content),
            _ => Err(anyhow!("Unexpected node type")),
        }?;

        let inner_handler_mutex =
            Arc::new(Mutex::new(ExpressionHandlerInner::new(self.trace)));

        content
            .transform_attributes
            .run_with(request.input, |input| {
                let inner_handler_mutex = inner_handler_mutex.clone();

                async move {
                    let mut inner_handler_ref =
                        inner_handler_mutex.lock().await;
                    inner_handler_ref.handle(input, content).await
                }
            })
            .await
    }
}

struct ExpressionHandlerInner<'a> {
    isolate: Isolate<'a>,
    trace: bool,
}

impl<'a> ExpressionHandlerInner<'a> {
    pub fn new(trace: bool) -> Self {
        Self { isolate: Isolate::new(), trace }
    }

    async fn handle(
        &mut self,
        input: Variable,
        content: &'a ExpressionNodeContent,
    ) -> NodeResult {
        let result = Variable::empty_object();
        let mut trace_map =
            self.trace.then(|| HashMap::<&str, ExpressionTrace>::new());

        self.isolate.set_environment(input.depth_clone(1));
        for expression in &content.expressions {
            if expression.key.is_empty() || expression.value.is_empty() {
                continue;
            }

            let value = self
                .isolate
                .run_standard(&expression.value)
                .with_context(|| PartialTraceError {
                    trace: trace_map
                        .as_ref()
                        .map(|s| serde_json::to_value(s).ok())
                        .flatten(),
                    message: format!(
                        r#"Failed to evaluate expression: "{}""#,
                        &expression.value
                    ),
                })?;

            if let Some(tmap) = &mut trace_map {
                tmap.insert(
                    &expression.key,
                    ExpressionTrace {
                        result: serde_json::to_string(&value)
                            .unwrap_or("Error".to_owned()),
                    },
                );
            }

            self.isolate.update_environment(|env| {
                let Some(environment) = env else {
                    return;
                };

                let key = format!("$.{}", &expression.key);
                let _ =
                    environment.dot_insert(key.as_str(), value.depth_clone(2));
            });

            result.dot_insert(&expression.key, value);
        }

        Ok(NodeResponse {
            output: result,
            trace_data: trace_map
                .map(|tm| serde_json::to_value(tm).ok())
                .flatten(),
        })
    }
}