wfrs_validator/
lib.rs

1use wfrs_model::{
2    jsep::{BinaryExpression, JsepNode, MemberExpression},
3    json::JsonValue,
4    ConditionExpression,
5};
6use wfrs_model::{ExclusiveGatewayDef, WorkflowDefinition};
7
8pub struct Member<'a>(&'a MemberExpression);
9
10impl<'a> Member<'a> {
11    fn resolve<'v>(&self, mut variables: &'v JsonValue) -> &'v JsonValue {
12        if let JsepNode::Identifier(property) = self.0.property.as_ref() {
13            if let JsepNode::MemberExpression(member) = self.0.object.as_ref() {
14                variables = Member(member).resolve(variables);
15                if let Some(variables) = variables.as_object() {
16                    if let Some(variables) = variables.get(property.name.as_ref()) {
17                        return variables;
18                    }
19                }
20            }
21            if let JsepNode::Identifier(object) = self.0.object.as_ref() {
22                if object.name.as_ref() == "$steps" {
23                    if let Some(variables) = variables.as_object() {
24                        if let Some(variables) = variables.get(property.name.as_ref()) {
25                            return variables;
26                        }
27                    }
28                }
29            }
30        }
31        &JsonValue::Null
32    }
33}
34
35pub struct Value<'a>(&'a JsepNode);
36
37impl<'a> Value<'a> {
38    fn resolve<'v>(&self, variables: &'v JsonValue) -> &'v JsonValue
39    where
40        'a: 'v,
41    {
42        match self.0 {
43            JsepNode::MemberExpression(member) => Member(member).resolve(variables),
44            JsepNode::Literal(lit) => &lit.value,
45            _ => &JsonValue::Null,
46        }
47    }
48}
49
50pub struct Binary<'a>(&'a BinaryExpression);
51
52impl<'a> Binary<'a> {
53    pub fn validate(&self, variables: &JsonValue) -> bool {
54        let left = Value(&self.0.left).resolve(variables);
55        let right = Value(&self.0.right).resolve(variables);
56        match self.0.operator {
57            wfrs_model::jsep::Operator::Equal => left == right,
58            wfrs_model::jsep::Operator::NotEqual => left != right,
59            wfrs_model::jsep::Operator::Greater => {
60                if let Some((l, r)) = left.as_number().zip(right.as_number()) {
61                    return l > r;
62                } else if let Some((l, r)) = left.as_str().zip(right.as_str()) {
63                    return l > r;
64                } else if let Some((l, r)) = left.as_bool().zip(right.as_bool()) {
65                    return l & !r;
66                }
67                false
68            }
69            wfrs_model::jsep::Operator::GreaterOrEqual => {
70                if let Some((l, r)) = left.as_number().zip(right.as_number()) {
71                    return l >= r;
72                } else if let Some((l, r)) = left.as_str().zip(right.as_str()) {
73                    return l >= r;
74                } else if let Some((l, r)) = left.as_bool().zip(right.as_bool()) {
75                    return l >= r;
76                }
77                false
78            }
79            wfrs_model::jsep::Operator::Lower => {
80                if let Some((l, r)) = left.as_number().zip(right.as_number()) {
81                    return l < r;
82                } else if let Some((l, r)) = left.as_str().zip(right.as_str()) {
83                    return l < r;
84                } else if let Some((l, r)) = left.as_bool().zip(right.as_bool()) {
85                    return !l & r;
86                }
87                false
88            }
89            wfrs_model::jsep::Operator::LowerOrEqual => {
90                if let Some((l, r)) = left.as_number().zip(right.as_number()) {
91                    return l <= r;
92                } else if let Some((l, r)) = left.as_str().zip(right.as_str()) {
93                    return l <= r;
94                } else if let Some((l, r)) = left.as_bool().zip(right.as_bool()) {
95                    return l <= r;
96                }
97                false
98            }
99            wfrs_model::jsep::Operator::And => {
100                if let Some((l, r)) = left.as_bool().zip(right.as_bool()) {
101                    if l && r {
102                        return true;
103                    }
104                }
105                false
106            }
107            wfrs_model::jsep::Operator::Or => {
108                if let Some(l) = left.as_bool() {
109                    if l {
110                        return true;
111                    }
112                }
113                if let Some(r) = right.as_bool() {
114                    if r {
115                        return true;
116                    }
117                }
118                false
119            }
120        }
121    }
122}
123
124pub struct Condition<'a>(pub &'a ConditionExpression);
125
126impl<'a> Condition<'a> {
127    pub fn validate(&self, variables: &JsonValue) -> bool {
128        match self.0 {
129            ConditionExpression::Jsep(node) => match node {
130                wfrs_model::jsep::JsepNode::BinaryExpression(binary_expr) => {
131                    Binary(binary_expr).validate(variables)
132                }
133                _ => false,
134            },
135        }
136    }
137}
138
139pub struct ExclusiveGateway<'a>(pub &'a ExclusiveGatewayDef);
140
141impl<'a> ExclusiveGateway<'a> {
142    pub fn evaluate(&self, definition: &WorkflowDefinition, variables: &JsonValue) -> [i32; 1] {
143        let mut out = [self.0.default];
144        for outgoing in self.0.outgoing.iter() {
145            if let Some(expr) = definition
146                .flows
147                .get(*outgoing as usize)
148                .and_then(|f| f.condition_expression.as_ref())
149            {
150                if Condition(expr).validate(variables) {
151                    out = [*outgoing];
152                    break;
153                }
154            }
155        }
156        out
157    }
158}