spaceapi_server/
modifiers.rs

1//! Modifiers which can be injected by the application logic to change the
2//! state dynamically per request.
3
4use crate::api::{self, sensors};
5
6/// `StatusModifier`s are used to modify the status
7pub trait StatusModifier: Send + Sync {
8    /// Called after all registered sensors are read
9    fn modify(&self, status: &mut api::Status);
10}
11
12/// This modifier updates the opening state based on the
13/// first people now present sensor (if present).
14pub struct StateFromPeopleNowPresent;
15
16impl StatusModifier for StateFromPeopleNowPresent {
17    fn modify(&self, status: &mut api::Status) {
18        // Update state depending on number of people present
19        let people_now_present: Option<u64> = status
20            .sensors
21            .as_ref()
22            .and_then(|sensors: &sensors::Sensors| sensors.people_now_present.first())
23            .map(|sensor: &sensors::PeopleNowPresentSensor| sensor.value);
24        if let Some(count) = people_now_present {
25            let mut state = status.state.clone().unwrap_or_default();
26            state.open = Some(count > 0);
27            // comparison chain is actually cleaner here IMO
28            #[allow(clippy::comparison_chain)]
29            if count == 1 {
30                state.message = Some(format!("{} person here right now", count));
31            } else if count > 1 {
32                state.message = Some(format!("{} people here right now", count));
33            }
34            status.state = Some(state);
35        }
36    }
37}
38
39#[cfg(test)]
40mod tests {
41    use super::*;
42
43    mod state_from_people_now_present {
44        use super::*;
45
46        #[test]
47        fn no_sensors() {
48            let mut status = api::Status {
49                sensors: None,
50                ..api::Status::default()
51            };
52            assert_eq!(status.state, None);
53            StateFromPeopleNowPresent.modify(&mut status);
54            assert_eq!(status.sensors, None);
55            assert_eq!(status.state, None);
56        }
57
58        #[test]
59        fn no_people_present_sensor() {
60            let mut status = api::Status {
61                sensors: Some(sensors::Sensors::default()),
62                ..api::Status::default()
63            };
64            assert_eq!(status.state, None);
65            StateFromPeopleNowPresent.modify(&mut status);
66            assert_eq!(status.state, None);
67        }
68
69        fn make_pnp_sensor(value: u64) -> sensors::PeopleNowPresentSensor {
70            sensors::PeopleNowPresentSensor {
71                metadata: Default::default(),
72                names: None,
73                value,
74            }
75        }
76
77        #[test]
78        fn zero_people_present() {
79            let mut status = api::Status {
80                sensors: Some(sensors::Sensors {
81                    people_now_present: vec![make_pnp_sensor(0)],
82                    ..sensors::Sensors::default()
83                }),
84                state: Some(api::State::default()),
85                ..api::Status::default()
86            };
87            status.state.as_mut().unwrap().message = Some("This will remain unchanged.".to_string());
88            assert_eq!(
89                status.state.as_ref().unwrap().message,
90                Some("This will remain unchanged.".to_string())
91            );
92            StateFromPeopleNowPresent.modify(&mut status);
93            assert_eq!(
94                status.state.unwrap().message,
95                Some("This will remain unchanged.".to_string())
96            );
97        }
98
99        #[test]
100        fn one_person_present() {
101            let mut status = api::Status {
102                sensors: Some(sensors::Sensors {
103                    people_now_present: vec![make_pnp_sensor(1)],
104                    ..sensors::Sensors::default()
105                }),
106                ..api::Status::default()
107            };
108            assert_eq!(status.state, None);
109            StateFromPeopleNowPresent.modify(&mut status);
110            assert_eq!(
111                status.state.unwrap().message,
112                Some("1 person here right now".to_string())
113            );
114        }
115
116        #[test]
117        fn two_people_present() {
118            let mut status = api::Status {
119                sensors: Some(sensors::Sensors {
120                    people_now_present: vec![make_pnp_sensor(2)],
121                    ..sensors::Sensors::default()
122                }),
123                ..api::Status::default()
124            };
125            assert_eq!(status.state, None);
126            StateFromPeopleNowPresent.modify(&mut status);
127            assert_eq!(
128                status.state.as_ref().unwrap().message,
129                Some("2 people here right now".to_string())
130            );
131        }
132    }
133}