Skip to main content

bloop_server_framework/evaluator/
mod.rs

1use crate::achievement::AchievementContext;
2use uuid::Uuid;
3
4pub(crate) mod boxed;
5pub mod distinct_values;
6pub mod min_bloops;
7pub mod registration_number;
8pub mod spelling_bee;
9pub mod streak;
10pub mod time;
11pub mod trigger;
12
13/// Result of evaluating whether an achievement should be awarded.
14#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum EvalResult {
16    /// Award the achievement to the evaluating player only.
17    AwardSelf,
18    /// Award the achievement to multiple players, identified by UUIDs.
19    AwardMultiple(Vec<Uuid>),
20    /// Do not award the achievement.
21    NoAward,
22}
23
24impl From<bool> for EvalResult {
25    fn from(value: bool) -> Self {
26        if value {
27            EvalResult::AwardSelf
28        } else {
29            EvalResult::NoAward
30        }
31    }
32}
33
34impl From<Option<Uuid>> for EvalResult {
35    fn from(value: Option<Uuid>) -> Self {
36        value.map_or(EvalResult::NoAward, |value| {
37            EvalResult::AwardMultiple(vec![value])
38        })
39    }
40}
41
42impl From<Vec<Uuid>> for EvalResult {
43    fn from(value: Vec<Uuid>) -> Self {
44        Self::AwardMultiple(value)
45    }
46}
47
48impl From<Option<Vec<Uuid>>> for EvalResult {
49    fn from(value: Option<Vec<Uuid>>) -> Self {
50        value.map_or(EvalResult::NoAward, |value| {
51            EvalResult::AwardMultiple(value)
52        })
53    }
54}
55
56/// Which players receive the award when the evaluation passes.
57#[derive(Debug, Default)]
58pub enum AwardMode {
59    /// Award only the current player
60    #[default]
61    Current,
62    /// Award all players whose bloops were involved in completing the collection.
63    All,
64}
65
66/// A derived-context value that may either borrow from the `AchievementContext`
67/// for the duration of an evaluation or own its data.
68///
69/// This type lets `derive_ctx` return either a borrowed reference tied to the
70/// `ctx` lifetime (zero-copy) or an owned `C` (cloned/constructed on demand).
71pub enum DerivedCtx<'a, C> {
72    Borrowed(&'a C),
73    Owned(C),
74}
75
76impl<'a, T: Sized> AsRef<T> for DerivedCtx<'a, T> {
77    fn as_ref(&self) -> &T {
78        match self {
79            DerivedCtx::Borrowed(r) => r,
80            DerivedCtx::Owned(v) => v,
81        }
82    }
83}
84
85/// Trait for statically typed achievement evaluators.
86///
87/// This is the primary abstraction for writing custom logic to evaluate whether
88/// an achievement should be awarded.
89pub trait Evaluator<Player, State, Trigger> {
90    /// Evaluate the achievement for the given context.
91    fn evaluate(&self, ctx: &AchievementContext<Player, State, Trigger>) -> impl Into<EvalResult>;
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97    use uuid::Uuid;
98
99    #[test]
100    fn converts_bool_to_eval_result() {
101        assert_eq!(EvalResult::from(true), EvalResult::AwardSelf);
102        assert_eq!(EvalResult::from(false), EvalResult::NoAward);
103    }
104
105    #[test]
106    fn converts_option_uuid_to_eval_result() {
107        let uuid = Uuid::new_v4();
108        assert_eq!(
109            EvalResult::from(Some(uuid)),
110            EvalResult::AwardMultiple(vec![uuid])
111        );
112
113        assert_eq!(EvalResult::from(None::<Uuid>), EvalResult::NoAward);
114    }
115
116    #[test]
117    fn converts_vec_uuid_to_eval_result() {
118        let uuid1 = Uuid::new_v4();
119        let uuid2 = Uuid::new_v4();
120        let uuids = vec![uuid1, uuid2];
121
122        assert_eq!(
123            EvalResult::from(uuids.clone()),
124            EvalResult::AwardMultiple(uuids)
125        );
126    }
127
128    #[test]
129    fn converts_option_vec_uuid_to_eval_result() {
130        let uuid = Uuid::new_v4();
131
132        assert_eq!(
133            EvalResult::from(Some(vec![uuid])),
134            EvalResult::AwardMultiple(vec![uuid])
135        );
136
137        assert_eq!(
138            EvalResult::from(Some(vec![])),
139            EvalResult::AwardMultiple(vec![])
140        );
141
142        assert_eq!(EvalResult::from(None::<Vec<Uuid>>), EvalResult::NoAward);
143    }
144}