bloop_server_framework/evaluator/
trigger.rs

1use crate::achievement::AchievementContext;
2use crate::evaluator::{EvalResult, Evaluator};
3use std::fmt::Debug;
4
5/// Evaluates whether a specific trigger is active for the current player.
6///
7/// This evaluator checks if the given `Trigger` is present in the context's
8/// [`TriggerRegistry`]. It can be used to award achievements to players via
9/// special trigger NFC tags.
10#[derive(Debug)]
11pub struct TriggerEvaluator<Trigger: PartialEq + Copy> {
12    trigger: Trigger,
13}
14
15impl<Trigger: PartialEq + Copy> TriggerEvaluator<Trigger> {
16    /// Creates a new `TriggerEvaluator` for the specified trigger.
17    ///
18    /// # Examples
19    ///
20    /// ```
21    /// use bloop_server_framework::evaluator::trigger::TriggerEvaluator;
22    ///
23    /// #[derive(Copy, Clone, PartialEq)]
24    /// enum MyTrigger {
25    ///     CompletedTutorial,
26    /// }
27    ///
28    /// let evaluator = TriggerEvaluator::new(MyTrigger::CompletedTutorial);
29    /// ```
30    pub fn new(trigger: Trigger) -> Self {
31        Self { trigger }
32    }
33}
34
35impl<Player, Metadata, Trigger> Evaluator<Player, Metadata, Trigger> for TriggerEvaluator<Trigger>
36where
37    Trigger: PartialEq + Copy + Send + Sync,
38{
39    fn evaluate(
40        &self,
41        ctx: &AchievementContext<Player, Metadata, Trigger>,
42    ) -> impl Into<EvalResult> {
43        ctx.has_trigger(self.trigger)
44    }
45}
46
47#[cfg(test)]
48mod tests {
49    use super::*;
50    use crate::bloop::Bloop;
51    use crate::evaluator::{EvalResult, Evaluator};
52    use crate::nfc_uid::NfcUid;
53    use crate::test_utils::{MockPlayer, TestCtxBuilder};
54    use crate::trigger::{TriggerOccurrence, TriggerRegistry, TriggerSpec};
55    use chrono::Utc;
56    use std::collections::HashMap;
57
58    #[derive(Debug, Copy, Clone, PartialEq, Eq)]
59    enum Trigger {
60        Foo,
61        Bar,
62    }
63
64    #[test]
65    fn returns_true_when_trigger_is_present() {
66        let (player, _) = MockPlayer::builder().build();
67        let bloop = Bloop::new(player.clone(), "client1", Utc::now());
68        let mut triggers = HashMap::new();
69        triggers.insert(
70            NfcUid::default(),
71            TriggerSpec {
72                trigger: Trigger::Foo,
73                global: false,
74                occurrence: TriggerOccurrence::Once,
75            },
76        );
77        let mut trigger_registry = TriggerRegistry::new(triggers);
78        trigger_registry.try_activate_trigger(NfcUid::default(), "client1");
79
80        let mut builder = TestCtxBuilder::new(bloop).trigger_registry(trigger_registry);
81
82        let evaluator = TriggerEvaluator::new(Trigger::Foo);
83        assert_eq!(
84            evaluator.evaluate(&builder.build()).into(),
85            EvalResult::AwardSelf
86        );
87    }
88
89    #[test]
90    fn returns_false_when_trigger_is_missing() {
91        let (player, _) = MockPlayer::builder().build();
92        let bloop = Bloop::new(player.clone(), "client1", Utc::now());
93
94        let mut builder =
95            TestCtxBuilder::new(bloop).trigger_registry(TriggerRegistry::new(HashMap::new()));
96
97        let evaluator = TriggerEvaluator::new(Trigger::Bar);
98        assert_eq!(
99            evaluator.evaluate(&builder.build()).into(),
100            EvalResult::NoAward
101        );
102    }
103}