mirror of
https://github.com/gorules/zen.git
synced 2026-10-04 08:02:28 +00:00
189 lines
5.2 KiB
Rust
189 lines
5.2 KiB
Rust
use crate::decision::Decision;
|
|
use crate::decision_graph::graph::DecisionGraphResponse;
|
|
use crate::loader::{ClosureLoader, DynamicLoader, LoaderResponse, LoaderResult, NoopLoader};
|
|
use crate::model::DecisionContent;
|
|
use crate::nodes::custom::{DynamicCustomNode, NoopCustomNode};
|
|
use crate::nodes::function::http_handler::DynamicHttpHandler;
|
|
use crate::EvaluationError;
|
|
use serde_json::Value;
|
|
use std::fmt::Debug;
|
|
use std::future::Future;
|
|
use std::sync::Arc;
|
|
use strum::{EnumString, IntoStaticStr};
|
|
use zen_expression::variable::Variable;
|
|
|
|
/// Structure used for generating and evaluating JDM decisions
|
|
#[derive(Debug, Clone)]
|
|
pub struct DecisionEngine {
|
|
loader: DynamicLoader,
|
|
adapter: DynamicCustomNode,
|
|
http_handler: DynamicHttpHandler,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct EvaluationOptions {
|
|
pub trace: bool,
|
|
pub max_depth: u8,
|
|
}
|
|
|
|
impl Default for EvaluationOptions {
|
|
fn default() -> Self {
|
|
Self {
|
|
trace: false,
|
|
max_depth: 10,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct EvaluationSerializedOptions {
|
|
pub trace: EvaluationTraceKind,
|
|
pub max_depth: u8,
|
|
}
|
|
|
|
impl Default for EvaluationSerializedOptions {
|
|
fn default() -> Self {
|
|
Self {
|
|
trace: EvaluationTraceKind::None,
|
|
max_depth: 10,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Debug, Default, PartialEq, Eq, EnumString, IntoStaticStr)]
|
|
#[strum(serialize_all = "camelCase")]
|
|
pub enum EvaluationTraceKind {
|
|
#[default]
|
|
None,
|
|
Default,
|
|
String,
|
|
Reference,
|
|
ReferenceString,
|
|
}
|
|
|
|
impl EvaluationTraceKind {
|
|
pub fn serialize_trace(&self, trace: &Variable) -> Value {
|
|
match self {
|
|
EvaluationTraceKind::None => Value::Null,
|
|
EvaluationTraceKind::Default => serde_json::to_value(&trace).unwrap_or_default(),
|
|
EvaluationTraceKind::String => {
|
|
Value::String(serde_json::to_string(&trace).unwrap_or_default())
|
|
}
|
|
EvaluationTraceKind::Reference => {
|
|
serde_json::to_value(&trace.serialize_ref()).unwrap_or_default()
|
|
}
|
|
EvaluationTraceKind::ReferenceString => {
|
|
Value::String(serde_json::to_string(&trace.serialize_ref()).unwrap_or_default())
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
impl Default for DecisionEngine {
|
|
fn default() -> Self {
|
|
Self {
|
|
loader: Arc::new(NoopLoader::default()),
|
|
adapter: Arc::new(NoopCustomNode::default()),
|
|
http_handler: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl DecisionEngine {
|
|
pub fn new(loader: DynamicLoader, adapter: DynamicCustomNode) -> Self {
|
|
Self {
|
|
loader,
|
|
adapter,
|
|
http_handler: None,
|
|
}
|
|
}
|
|
|
|
pub fn with_adapter(mut self, adapter: DynamicCustomNode) -> Self {
|
|
self.adapter = adapter;
|
|
self
|
|
}
|
|
|
|
pub fn with_loader(mut self, loader: DynamicLoader) -> Self {
|
|
self.loader = loader;
|
|
self
|
|
}
|
|
|
|
pub fn with_http_handler(mut self, http_handler: DynamicHttpHandler) -> Self {
|
|
self.http_handler = http_handler;
|
|
self
|
|
}
|
|
|
|
pub fn with_closure_loader<F, O>(mut self, loader: F) -> Self
|
|
where
|
|
F: Fn(String) -> O + Sync + Send + 'static,
|
|
O: Future<Output = LoaderResponse> + Send,
|
|
{
|
|
self.loader = Arc::new(ClosureLoader::new(loader));
|
|
self
|
|
}
|
|
|
|
/// Evaluates a decision through loader using a key
|
|
pub async fn evaluate<K>(
|
|
&self,
|
|
key: K,
|
|
context: Variable,
|
|
) -> Result<DecisionGraphResponse, Box<EvaluationError>>
|
|
where
|
|
K: AsRef<str>,
|
|
{
|
|
self.evaluate_with_opts(key, context, Default::default())
|
|
.await
|
|
}
|
|
|
|
/// Evaluates a decision through loader using a key with advanced options
|
|
pub async fn evaluate_with_opts<K>(
|
|
&self,
|
|
key: K,
|
|
context: Variable,
|
|
options: EvaluationOptions,
|
|
) -> Result<DecisionGraphResponse, Box<EvaluationError>>
|
|
where
|
|
K: AsRef<str>,
|
|
{
|
|
let content = self.loader.load(key.as_ref()).await?;
|
|
let decision = self.create_decision(content);
|
|
decision.evaluate_with_opts(context, options).await
|
|
}
|
|
|
|
pub async fn evaluate_serialized<K>(
|
|
&self,
|
|
key: K,
|
|
context: Variable,
|
|
options: EvaluationSerializedOptions,
|
|
) -> Result<Value, Value>
|
|
where
|
|
K: AsRef<str>,
|
|
{
|
|
let content = self
|
|
.loader
|
|
.load(key.as_ref())
|
|
.await
|
|
.map_err(|err| Value::String(err.to_string()))?;
|
|
|
|
let decision = self.create_decision(content);
|
|
decision.evaluate_serialized(context, options).await
|
|
}
|
|
|
|
/// Creates a decision from DecisionContent, exists for easier binding creation
|
|
pub fn create_decision(&self, content: Arc<DecisionContent>) -> Decision {
|
|
Decision::from(content)
|
|
.with_loader(self.loader.clone())
|
|
.with_adapter(self.adapter.clone())
|
|
.with_http_handler(self.http_handler.clone())
|
|
}
|
|
|
|
/// Retrieves a decision based on the loader
|
|
pub async fn get_decision(&self, key: &str) -> LoaderResult<Decision> {
|
|
let content = self.loader.load(key).await?;
|
|
Ok(self.create_decision(content))
|
|
}
|
|
pub fn loader(&self) -> DynamicLoader {
|
|
self.loader.clone()
|
|
}
|
|
}
|