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 fn presence_conditions(message_type: &str) -> &'static [u32] {
210 match message_type {
211 "UTILMD_Strom" => &[166, 2003],
212 "UTILMD_Gas" => &[166],
213 "INVOIC" => &[22],
214 "ORDERS" => &[12],
215 _ => &[],
216 }
217}
218
219pub struct AbsentTarget<'a, E: ConditionEvaluator + ?Sized>(pub &'a E);
226
227impl<E: ConditionEvaluator + ?Sized> ConditionEvaluator for AbsentTarget<'_, E> {
228 fn evaluate(&self, condition: u32, ctx: &EvaluationContext) -> ConditionResult {
229 if presence_conditions(self.0.message_type()).contains(&condition) {
230 return ConditionResult::False;
231 }
232 self.0.evaluate(condition, ctx)
233 }
234
235 fn is_external(&self, condition: u32) -> bool {
236 self.0.is_external(condition)
237 }
238
239 fn is_known(&self, condition: u32) -> bool {
240 self.0.is_known(condition)
241 }
242
243 fn message_type(&self) -> &str {
244 self.0.message_type()
245 }
246
247 fn format_version(&self) -> &str {
248 self.0.format_version()
249 }
250}
251
252#[cfg(test)]
253mod tests {
254 use super::*;
255
256 struct AlwaysTrue(&'static str);
257
258 impl ConditionEvaluator for AlwaysTrue {
259 fn evaluate(&self, _: u32, _: &EvaluationContext) -> ConditionResult {
260 ConditionResult::True
261 }
262 fn is_external(&self, _: u32) -> bool {
263 false
264 }
265 fn message_type(&self) -> &str {
266 self.0
267 }
268 fn format_version(&self) -> &str {
269 "FV2604"
270 }
271 }
272
273 #[test]
274 fn absent_target_answers_only_the_presence_condition_with_false() {
275 let external = NoOpExternalProvider;
276 let ctx = EvaluationContext::new("55043", &external, &[]);
277 let inner = AlwaysTrue("UTILMD_Strom");
278 let absent = AbsentTarget(&inner);
279 assert_eq!(absent.evaluate(166, &ctx), ConditionResult::False);
280 assert_eq!(absent.evaluate(674, &ctx), ConditionResult::True);
281 assert_eq!(absent.evaluate(2003, &ctx), ConditionResult::False);
283
284 let invoic = AlwaysTrue("INVOIC");
286 assert_eq!(
287 AbsentTarget(&invoic).evaluate(166, &ctx),
288 ConditionResult::True
289 );
290 assert_eq!(
291 AbsentTarget(&invoic).evaluate(22, &ctx),
292 ConditionResult::False
293 );
294 }
295
296 #[test]
297 fn test_condition_result_is_methods() {
298 assert!(ConditionResult::True.is_true());
299 assert!(!ConditionResult::True.is_false());
300 assert!(!ConditionResult::True.is_unknown());
301
302 assert!(!ConditionResult::False.is_true());
303 assert!(ConditionResult::False.is_false());
304
305 assert!(ConditionResult::Unknown.is_unknown());
306 }
307
308 #[test]
309 fn three_valued_and_or_not() {
310 use ConditionResult::{False as F, True as T, Unknown as U};
311 assert_eq!(T.and(T), T);
312 assert_eq!(T.and(U), U);
313 assert_eq!(U.and(F), F);
314 assert_eq!(F.or(F), F);
315 assert_eq!(F.or(U), U);
316 assert_eq!(U.or(T), T);
317 assert_eq!(U.negate(), U);
318 assert_eq!(T.negate(), F);
319 }
320
321 #[test]
322 fn test_condition_result_to_option() {
323 assert_eq!(ConditionResult::True.to_option(), Some(true));
324 assert_eq!(ConditionResult::False.to_option(), Some(false));
325 assert_eq!(ConditionResult::Unknown.to_option(), None);
326 }
327
328 #[test]
329 fn test_condition_result_from_bool() {
330 assert_eq!(ConditionResult::from(true), ConditionResult::True);
331 assert_eq!(ConditionResult::from(false), ConditionResult::False);
332 }
333
334 #[test]
335 fn test_condition_result_display() {
336 assert_eq!(format!("{}", ConditionResult::True), "True");
337 assert_eq!(format!("{}", ConditionResult::False), "False");
338 assert_eq!(format!("{}", ConditionResult::Unknown), "Unknown");
339 }
340
341 #[test]
342 fn test_noop_external_provider() {
343 let provider = NoOpExternalProvider;
344 assert_eq!(
345 provider.evaluate("MessageSplitting"),
346 ConditionResult::Unknown
347 );
348 assert_eq!(provider.evaluate("anything"), ConditionResult::Unknown);
349 }
350}