phlow_engine/
condition.rs1use std::{fmt::Display, sync::Arc};
2
3use rhai::Engine;
4use serde::Serialize;
5use valu3::{prelude::StringBehavior, traits::ToValueBehavior, value::Value};
6
7use crate::{context::Context, script::Script};
8
9#[derive(Debug)]
10pub enum ConditionError {
11 InvalidOperator(String),
12 RightInvalid(String),
13 LeftInvalid(String),
14 AssertInvalid(String),
15 ScriptError(phs::ScriptError),
16}
17
18impl Display for ConditionError {
19 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
20 match self {
21 ConditionError::InvalidOperator(err) => write!(f, "Invalid operator: {}", err),
22 ConditionError::RightInvalid(err) => write!(f, "Right invalid: {}", err),
23 ConditionError::LeftInvalid(err) => write!(f, "Left invalid: {}", err),
24 ConditionError::AssertInvalid(err) => write!(f, "Assert invalid: {}", err),
25 ConditionError::ScriptError(err) => write!(f, "Script error: {}", err),
26 }
27 }
28}
29
30impl std::error::Error for ConditionError {
31 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
32 match self {
33 ConditionError::InvalidOperator(_) => None,
34 ConditionError::RightInvalid(_) => None,
35 ConditionError::LeftInvalid(_) => None,
36 ConditionError::AssertInvalid(_) => None,
37 ConditionError::ScriptError(_) => None, }
39 }
40}
41
42#[derive(Debug, Clone, PartialEq, Serialize)]
43pub enum Operator {
44 Or,
45 And,
46 Equal,
47 NotEqual,
48 GreaterThan,
49 LessThan,
50 GreaterThanOrEqual,
51 LessThanOrEqual,
52 Contains,
53 NotContains,
54}
55
56impl ToValueBehavior for Operator {
57 fn to_value(&self) -> Value {
58 match self {
59 Operator::Or => "or".to_value(),
60 Operator::And => "and".to_value(),
61 Operator::Equal => "equal".to_value(),
62 Operator::NotEqual => "not_equal".to_value(),
63 Operator::GreaterThan => "greater_than".to_value(),
64 Operator::LessThan => "less_than".to_value(),
65 Operator::GreaterThanOrEqual => "greater_than_or_equal".to_value(),
66 Operator::LessThanOrEqual => "less_than_or_equal".to_value(),
67 Operator::Contains => "contains".to_value(),
68 Operator::NotContains => "not_contains".to_value(),
69 }
70 }
71}
72
73impl From<&Value> for Operator {
74 fn from(value: &Value) -> Self {
75 match value.as_str() {
76 "or" => Operator::Or,
77 "and" => Operator::And,
78 "equal" => Operator::Equal,
79 "not_equal" => Operator::NotEqual,
80 "greater_than" => Operator::GreaterThan,
81 "less_than" => Operator::LessThan,
82 "greater_than_or_equal" => Operator::GreaterThanOrEqual,
83 "less_than_or_equal" => Operator::LessThanOrEqual,
84 "contains" => Operator::Contains,
85 "not_contains" => Operator::NotContains,
86 _ => panic!("Invalid operator"),
87 }
88 }
89}
90
91#[derive(Debug, Clone)]
92pub struct Condition {
93 pub(crate) expression: Script,
94 pub(crate) raw: Value,
95}
96
97impl Condition {
98 pub fn try_from_value(engine: Arc<Engine>, value: &Value) -> Result<Self, ConditionError> {
99 if let Some(assert) = value.get("assert") {
100 return Ok(Self::try_build_with_assert(engine, assert.to_string())?);
101 }
102
103 return Err(ConditionError::AssertInvalid("does not exist".to_string()));
104 }
105
106 pub fn try_build_with_assert(
107 engine: Arc<Engine>,
108 assert: String,
109 ) -> Result<Self, ConditionError> {
110 let expression =
111 Script::try_build(engine, &assert.to_value()).map_err(ConditionError::ScriptError)?;
112
113 Ok(Self {
114 expression,
115 raw: assert.to_value(),
116 })
117 }
118
119 pub fn evaluate(&self, context: &Context) -> Result<bool, ConditionError> {
120 let result = self
121 .expression
122 .evaluate(context)
123 .map_err(ConditionError::ScriptError)?;
124
125 match result {
126 Value::Boolean(result) => Ok(result),
127 _ => Err(ConditionError::ScriptError(phs::ScriptError::InvalidType(
128 result,
129 ))),
130 }
131 }
132}
133
134#[cfg(test)]
135mod test {
136 use std::collections::HashMap;
137
138 use super::*;
139 use phs::build_engine;
140
141 #[test]
142 fn test_condition_execute_assert() {
143 let engine = build_engine(None);
144 let assert_map = HashMap::from([("assert".to_string(), "{{ 5 > 3 }}".to_value())]);
145 let condition = Condition::try_from_value(engine, &assert_map.to_value()).unwrap();
146
147 let context = Context::new();
148
149 let result = condition.evaluate(&context).unwrap();
150 assert_eq!(result, true);
151 }
152}