konnektoren_core/events/
parse.rs

1use super::EventParseError;
2use super::{ChallengeEvent, Event, GameEvent};
3use serde_json::Value;
4use wasm_bindgen::prelude::*;
5
6impl From<EventParseError> for JsValue {
7    fn from(error: EventParseError) -> Self {
8        JsValue::from_str(&error.to_string())
9    }
10}
11
12impl TryFrom<JsValue> for Event {
13    type Error = EventParseError;
14
15    fn try_from(value: JsValue) -> Result<Self, Self::Error> {
16        let obj: Value = serde_wasm_bindgen::from_value(value)
17            .map_err(|e| EventParseError::ParseError(e.to_string()))?;
18        Event::try_from(obj)
19    }
20}
21
22impl TryFrom<Value> for Event {
23    type Error = EventParseError;
24
25    fn try_from(obj: Value) -> Result<Self, Self::Error> {
26        match obj.get("type").and_then(|v| v.as_str()) {
27            Some("Game") => Ok(Event::Game(GameEvent::try_from(obj)?)),
28            Some("Challenge") => Ok(Event::Challenge(ChallengeEvent::try_from(obj)?)),
29            Some(unknown_type) => Err(EventParseError::UnknownEventType(unknown_type.to_string())),
30            None => Err(EventParseError::MissingData),
31        }
32    }
33}
34
35impl TryFrom<Value> for GameEvent {
36    type Error = EventParseError;
37
38    fn try_from(value: Value) -> Result<Self, Self::Error> {
39        match value.get("action").and_then(|v| v.as_str()) {
40            Some("Started") => Ok(GameEvent::Started),
41            Some(unknown_action) => Err(EventParseError::UnknownEventType(
42                unknown_action.to_string(),
43            )),
44            None => Err(EventParseError::MissingData),
45        }
46    }
47}
48
49impl TryFrom<Value> for ChallengeEvent {
50    type Error = EventParseError;
51
52    fn try_from(value: Value) -> Result<Self, EventParseError> {
53        match value.get("action").and_then(|v| v.as_str()) {
54            Some("SolvedCorrect") => {
55                let index = value
56                    .get("index")
57                    .ok_or(EventParseError::MissingData)?
58                    .as_u64()
59                    .ok_or_else(|| {
60                        EventParseError::InvalidData("index must be a number".to_string())
61                    })?;
62                Ok(ChallengeEvent::SolvedCorrect(index as usize))
63            }
64            Some("SolvedIncorrect") => {
65                let index = value
66                    .get("index")
67                    .ok_or(EventParseError::MissingData)?
68                    .as_u64()
69                    .ok_or_else(|| {
70                        EventParseError::InvalidData("index must be a number".to_string())
71                    })?;
72                Ok(ChallengeEvent::SolvedIncorrect(index as usize))
73            }
74            Some("Error") => {
75                let message = value
76                    .get("message")
77                    .ok_or(EventParseError::MissingData)?
78                    .as_str()
79                    .ok_or_else(|| {
80                        EventParseError::InvalidData("message must be a string".to_string())
81                    })?;
82                Ok(ChallengeEvent::Error(message.to_string()))
83            }
84            Some("Started") => Ok(ChallengeEvent::Started),
85            Some("Completed") => Ok(ChallengeEvent::Completed),
86            Some(unknown_action) => Err(EventParseError::UnknownEventType(
87                unknown_action.to_string(),
88            )),
89            None => Err(EventParseError::MissingData),
90        }
91    }
92}
93
94#[cfg(test)]
95mod tests {
96    use super::*;
97
98    #[test]
99    fn test_parse_game_event() {
100        let json = r#"{"type":"Game","action":"Started"}"#;
101        let value: serde_json::Value = serde_json::from_str(json).unwrap();
102        let event = Event::try_from(value).unwrap();
103        assert_eq!(event, Event::Game(GameEvent::Started));
104    }
105
106    #[test]
107    fn test_parse_challenge_event() {
108        let json = r#"{"type":"Challenge","action":"SolvedCorrect","index":2}"#;
109        let value: serde_json::Value = serde_json::from_str(json).unwrap();
110        let event = Event::try_from(value).unwrap();
111        assert_eq!(event, Event::Challenge(ChallengeEvent::SolvedCorrect(2)));
112    }
113
114    #[test]
115    fn test_parse_challenge_event_solved_incorrect() {
116        let json = r#"{"type":"Challenge","action":"SolvedIncorrect","index":2}"#;
117        let value: serde_json::Value = serde_json::from_str(json).unwrap();
118        let event = Event::try_from(value).unwrap();
119        assert_eq!(event, Event::Challenge(ChallengeEvent::SolvedIncorrect(2)));
120    }
121
122    #[test]
123    fn test_parse_challenge_event_started() {
124        let json = r#"{"type":"Challenge","action":"Started"}"#;
125        let value: serde_json::Value = serde_json::from_str(json).unwrap();
126        let event = Event::try_from(value).unwrap();
127        assert_eq!(event, Event::Challenge(ChallengeEvent::Started));
128    }
129
130    #[test]
131    fn test_parse_challenge_event_completed() {
132        let json = r#"{"type":"Challenge","action":"Completed"}"#;
133        let value: serde_json::Value = serde_json::from_str(json).unwrap();
134        let event = Event::try_from(value).unwrap();
135        assert_eq!(event, Event::Challenge(ChallengeEvent::Completed));
136    }
137
138    #[test]
139    fn test_parse_challenge_event_error_missing_message() {
140        let json = r#"{"type":"Challenge","action":"Error"}"#;
141        let value: serde_json::Value = serde_json::from_str(json).unwrap();
142        let result = Event::try_from(value);
143        assert_eq!(result, Err(EventParseError::MissingData));
144    }
145
146    #[test]
147    fn test_parse_challenge_event_error_invalid_message() {
148        let json = r#"{"type":"Challenge","action":"Error","message":123}"#;
149        let value: serde_json::Value = serde_json::from_str(json).unwrap();
150        let result = Event::try_from(value);
151        assert_eq!(
152            result,
153            Err(EventParseError::InvalidData(
154                "message must be a string".to_string()
155            ))
156        );
157    }
158
159    #[cfg(target_arch = "wasm32")]
160    mod wasm_tests {
161        use super::*;
162        use wasm_bindgen_test::*;
163
164        wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
165
166        #[wasm_bindgen_test]
167        fn test_parse_game_event_js() {
168            let json = r#"{"type":"Game","action":"Started"}"#;
169            let value: JsValue = js_sys::JSON::parse(json).unwrap();
170            let event = Event::try_from(value).unwrap();
171            assert_eq!(event, Event::Game(GameEvent::Started));
172        }
173
174        #[wasm_bindgen_test]
175        fn test_parse_challenge_event_js() {
176            let json = r#"{"type":"Challenge","action":"SolvedCorrect","index":2}"#;
177            let value: JsValue = js_sys::JSON::parse(json).unwrap();
178            let event = Event::try_from(value).unwrap();
179            assert_eq!(event, Event::Challenge(ChallengeEvent::SolvedCorrect(2)));
180        }
181
182        #[wasm_bindgen_test]
183        fn test_parse_challenge_event_solved_incorrect_js() {
184            let json = r#"{"type":"Challenge","action":"SolvedIncorrect","index":2}"#;
185            let value: JsValue = js_sys::JSON::parse(json).unwrap();
186            let event = Event::try_from(value).unwrap();
187            assert_eq!(event, Event::Challenge(ChallengeEvent::SolvedIncorrect(2)));
188        }
189
190        #[wasm_bindgen_test]
191        fn test_parse_challenge_event_started_js() {
192            let json = r#"{"type":"Challenge","action":"Started"}"#;
193            let value: JsValue = js_sys::JSON::parse(json).unwrap();
194            let event = Event::try_from(value).unwrap();
195            assert_eq!(event, Event::Challenge(ChallengeEvent::Started));
196        }
197
198        #[wasm_bindgen_test]
199        fn test_parse_challenge_event_completed_js() {
200            let json = r#"{"type":"Challenge","action":"Completed"}"#;
201            let value: JsValue = js_sys::JSON::parse(json).unwrap();
202            let event = Event::try_from(value).unwrap();
203            assert_eq!(event, Event::Challenge(ChallengeEvent::Completed));
204        }
205
206        #[wasm_bindgen_test]
207        fn test_parse_game_event_invalid() {
208            let json = r#"{"type":"Game","action":"Invalid"}"#;
209            let value: JsValue = js_sys::JSON::parse(json).unwrap();
210            let event = Event::try_from(value);
211            assert_eq!(
212                event,
213                Err(EventParseError::UnknownEventType("Invalid".to_string()))
214            );
215        }
216
217        #[wasm_bindgen_test]
218        fn test_parse_challenge_event_invalid() {
219            let json = r#"{"type":"Challenge","action":"Invalid"}"#;
220            let value: JsValue = js_sys::JSON::parse(json).unwrap();
221            let event = Event::try_from(value);
222            assert_eq!(
223                event,
224                Err(EventParseError::UnknownEventType("Invalid".to_string()))
225            );
226        }
227
228        #[wasm_bindgen_test]
229        fn test_parse_game_event_missing_data() {
230            let json = r#"{"type":"Game"}"#;
231            let value: JsValue = js_sys::JSON::parse(json).unwrap();
232            let event = Event::try_from(value);
233            assert_eq!(event, Err(EventParseError::MissingData));
234        }
235
236        #[wasm_bindgen_test]
237        fn test_parse_challenge_event_missing_data() {
238            let json = r#"{"type":"Challenge"}"#;
239            let value: JsValue = js_sys::JSON::parse(json).unwrap();
240            let event = Event::try_from(value);
241            assert_eq!(event, Err(EventParseError::MissingData));
242        }
243    }
244}