automapper_validation/eval/
evaluator.rs1use super::context::EvaluationContext;
4
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
10pub enum ConditionResult {
11 True,
13 False,
15 Unknown,
17}
18
19impl ConditionResult {
20 pub fn is_true(self) -> bool {
22 matches!(self, ConditionResult::True)
23 }
24
25 pub fn is_false(self) -> bool {
27 matches!(self, ConditionResult::False)
28 }
29
30 pub fn is_unknown(self) -> bool {
32 matches!(self, ConditionResult::Unknown)
33 }
34
35 pub fn and(self, other: ConditionResult) -> ConditionResult {
37 match (self, other) {
38 (ConditionResult::False, _) | (_, ConditionResult::False) => ConditionResult::False,
39 (ConditionResult::True, ConditionResult::True) => ConditionResult::True,
40 _ => ConditionResult::Unknown,
41 }
42 }
43
44 pub fn or(self, other: ConditionResult) -> ConditionResult {
46 match (self, other) {
47 (ConditionResult::True, _) | (_, ConditionResult::True) => ConditionResult::True,
48 (ConditionResult::False, ConditionResult::False) => ConditionResult::False,
49 _ => ConditionResult::Unknown,
50 }
51 }
52
53 pub fn negate(self) -> ConditionResult {
55 match self {
56 ConditionResult::True => ConditionResult::False,
57 ConditionResult::False => ConditionResult::True,
58 ConditionResult::Unknown => ConditionResult::Unknown,
59 }
60 }
61
62 pub fn to_option(self) -> Option<bool> {
64 match self {
65 ConditionResult::True => Some(true),
66 ConditionResult::False => Some(false),
67 ConditionResult::Unknown => None,
68 }
69 }
70}
71
72impl From<bool> for ConditionResult {
73 fn from(value: bool) -> Self {
74 if value {
75 ConditionResult::True
76 } else {
77 ConditionResult::False
78 }
79 }
80}
81
82impl std::fmt::Display for ConditionResult {
83 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
84 match self {
85 ConditionResult::True => write!(f, "True"),
86 ConditionResult::False => write!(f, "False"),
87 ConditionResult::Unknown => write!(f, "Unknown"),
88 }
89 }
90}
91
92pub trait ConditionEvaluator: Send + Sync {
98 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult;
103
104 fn is_external(&self, condition: u32) -> bool;
107
108 fn is_known(&self, _condition: u32) -> bool {
116 false
117 }
118
119 fn message_type(&self) -> &str;
121
122 fn format_version(&self) -> &str;
124}
125
126impl<T: ConditionEvaluator + ?Sized> ConditionEvaluator for std::sync::Arc<T> {
127 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
128 (**self).evaluate(condition, ctx)
129 }
130
131 fn is_external(&self, condition: u32) -> bool {
132 (**self).is_external(condition)
133 }
134
135 fn is_known(&self, condition: u32) -> bool {
136 (**self).is_known(condition)
137 }
138
139 fn message_type(&self) -> &str {
140 (**self).message_type()
141 }
142
143 fn format_version(&self) -> &str {
144 (**self).format_version()
145 }
146}
147
148pub trait ExternalConditionProvider: Send + Sync {
158 fn evaluate(&self, condition_name: &str) -> ConditionResult;
164}
165
166pub struct NoOpExternalProvider;
171
172impl ExternalConditionProvider for NoOpExternalProvider {
173 fn evaluate(&self, _condition_name: &str) -> ConditionResult {
174 ConditionResult::Unknown
175 }
176}
177
178pub fn presence_condition(message_type: &str) -> Option<u32> {
191 match message_type {
192 "UTILMD_Strom" | "UTILMD_Gas" => Some(166),
193 "INVOIC" => Some(22),
194 "ORDERS" => Some(12),
195 _ => None,
196 }
197}
198
199pub struct AbsentTarget<'a, E: ConditionEvaluator + ?Sized>(pub &'a E);
206
207impl<E: ConditionEvaluator + ?Sized> ConditionEvaluator for AbsentTarget<'_, E> {
208 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
209 if presence_condition(self.0.message_type()) == Some(condition) {
210 return ConditionResult::False;
211 }
212 self.0.evaluate(condition, ctx)
213 }
214
215 fn is_external(&self, condition: u32) -> bool {
216 self.0.is_external(condition)
217 }
218
219 fn is_known(&self, condition: u32) -> bool {
220 self.0.is_known(condition)
221 }
222
223 fn message_type(&self) -> &str {
224 self.0.message_type()
225 }
226
227 fn format_version(&self) -> &str {
228 self.0.format_version()
229 }
230}
231
232#[cfg(test)]
233mod tests {
234 use super::*;
235
236 struct AlwaysTrue(&'static str);
237
238 impl ConditionEvaluator for AlwaysTrue {
239 fn evaluate(&self, _: u32, _: &EvaluationContext) -> ConditionResult {
240 ConditionResult::True
241 }
242 fn is_external(&self, _: u32) -> bool {
243 false
244 }
245 fn message_type(&self) -> &str {
246 self.0
247 }
248 fn format_version(&self) -> &str {
249 "FV2604"
250 }
251 }
252
253 #[test]
254 fn absent_target_answers_only_the_presence_condition_with_false() {
255 let external = NoOpExternalProvider;
256 let ctx = EvaluationContext::new("55043", &external, &[]);
257 let inner = AlwaysTrue("UTILMD_Strom");
258 let absent = AbsentTarget(&inner);
259 assert_eq!(absent.evaluate(166, &ctx), ConditionResult::False);
260 assert_eq!(absent.evaluate(674, &ctx), ConditionResult::True);
261
262 let invoic = AlwaysTrue("INVOIC");
264 assert_eq!(
265 AbsentTarget(&invoic).evaluate(166, &ctx),
266 ConditionResult::True
267 );
268 assert_eq!(
269 AbsentTarget(&invoic).evaluate(22, &ctx),
270 ConditionResult::False
271 );
272 }
273
274 #[test]
275 fn test_condition_result_is_methods() {
276 assert!(ConditionResult::True.is_true());
277 assert!(!ConditionResult::True.is_false());
278 assert!(!ConditionResult::True.is_unknown());
279
280 assert!(!ConditionResult::False.is_true());
281 assert!(ConditionResult::False.is_false());
282
283 assert!(ConditionResult::Unknown.is_unknown());
284 }
285
286 #[test]
287 fn three_valued_and_or_not() {
288 use ConditionResult::{False as F, True as T, Unknown as U};
289 assert_eq!(T.and(T), T);
290 assert_eq!(T.and(U), U);
291 assert_eq!(U.and(F), F);
292 assert_eq!(F.or(F), F);
293 assert_eq!(F.or(U), U);
294 assert_eq!(U.or(T), T);
295 assert_eq!(U.negate(), U);
296 assert_eq!(T.negate(), F);
297 }
298
299 #[test]
300 fn test_condition_result_to_option() {
301 assert_eq!(ConditionResult::True.to_option(), Some(true));
302 assert_eq!(ConditionResult::False.to_option(), Some(false));
303 assert_eq!(ConditionResult::Unknown.to_option(), None);
304 }
305
306 #[test]
307 fn test_condition_result_from_bool() {
308 assert_eq!(ConditionResult::from(true), ConditionResult::True);
309 assert_eq!(ConditionResult::from(false), ConditionResult::False);
310 }
311
312 #[test]
313 fn test_condition_result_display() {
314 assert_eq!(format!("{}", ConditionResult::True), "True");
315 assert_eq!(format!("{}", ConditionResult::False), "False");
316 assert_eq!(format!("{}", ConditionResult::Unknown), "Unknown");
317 }
318
319 #[test]
320 fn test_noop_external_provider() {
321 let provider = NoOpExternalProvider;
322 assert_eq!(
323 provider.evaluate("MessageSplitting"),
324 ConditionResult::Unknown
325 );
326 assert_eq!(provider.evaluate("anything"), ConditionResult::Unknown);
327 }
328}