konnektoren_core/commands/
parse.rs1use super::{ChallengeCommand, Command, CommandError, GameCommand};
2use crate::challenges::{ChallengeResult, CustomChallengeResult};
3use serde_json::Value;
4use wasm_bindgen::prelude::*;
5
6impl From<CommandError> for JsValue {
7 fn from(error: CommandError) -> Self {
8 JsValue::from_str(&error.to_string())
9 }
10}
11
12impl TryFrom<JsValue> for Command {
13 type Error = CommandError;
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| CommandError::ParseError(e.to_string()))?;
18 Command::try_from(obj)
19 }
20}
21
22impl TryFrom<Value> for Command {
23 type Error = CommandError;
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(Command::Game(GameCommand::try_from(obj)?)),
28 Some("Challenge") => Ok(Command::Challenge(ChallengeCommand::try_from(obj)?)),
29 Some(unknown_type) => Err(CommandError::UnknownCommandType(unknown_type.to_string())),
30 None => Err(CommandError::MissingData),
31 }
32 }
33}
34
35impl TryFrom<Value> for GameCommand {
36 type Error = CommandError;
37
38 fn try_from(value: Value) -> Result<Self, Self::Error> {
39 match value.get("action").and_then(|v| v.as_str()) {
40 Some("NextChallenge") => Ok(GameCommand::NextChallenge),
41 Some("PreviousChallenge") => Ok(GameCommand::PreviousChallenge),
42 Some(unknown_action) => {
43 Err(CommandError::UnknownCommandType(unknown_action.to_string()))
44 }
45 None => Err(CommandError::MissingData),
46 }
47 }
48}
49
50impl TryFrom<Value> for ChallengeCommand {
51 type Error = CommandError;
52
53 fn try_from(value: Value) -> Result<Self, Self::Error> {
54 match value.get("action").and_then(|v| v.as_str()) {
55 Some("NextTask") => Ok(ChallengeCommand::NextTask),
56 Some("PreviousTask") => Ok(ChallengeCommand::PreviousTask),
57 Some("SolveOption") => {
58 let option_index = value
59 .get("optionIndex")
60 .ok_or(CommandError::MissingData)?
61 .as_u64()
62 .ok_or_else(|| {
63 CommandError::InvalidData("optionIndex must be a number".to_string())
64 })?;
65 Ok(ChallengeCommand::SolveOption(option_index as usize))
66 }
67 Some("Finish") => {
68 let result = value.get("result").ok_or(CommandError::MissingData)?;
69 let result: CustomChallengeResult = serde_json::from_value(result.clone())
70 .map_err(|e| CommandError::ParseError(e.to_string()))?;
71 Ok(ChallengeCommand::Finish(Some(ChallengeResult::Custom(
72 result.clone(),
73 ))))
74 }
75 Some(unknown_action) => {
76 Err(CommandError::UnknownCommandType(unknown_action.to_string()))
77 }
78 None => Err(CommandError::MissingData),
79 }
80 }
81}
82
83#[cfg(test)]
84mod tests {
85 use super::*;
86 use crate::challenges::ChallengeResult;
87
88 #[test]
89 fn test_parse_game_command() {
90 let json = r#"{"type":"Game","action":"NextChallenge"}"#;
91 let value: serde_json::Value = serde_json::from_str(json).unwrap();
92 let command = Command::try_from(value).unwrap();
93 assert_eq!(command, Command::Game(GameCommand::NextChallenge));
94 }
95
96 #[test]
97 fn test_parse_challenge_command() {
98 let json = r#"{"type":"Challenge","action":"NextTask"}"#;
99 let value: serde_json::Value = serde_json::from_str(json).unwrap();
100 let command = Command::try_from(value).unwrap();
101 assert_eq!(command, Command::Challenge(ChallengeCommand::NextTask));
102 }
103
104 #[test]
105 fn test_parse_challenge_command_with_option() {
106 let json = r#"{"type":"Challenge","action":"SolveOption","optionIndex":0}"#;
107 let value: serde_json::Value = serde_json::from_str(json).unwrap();
108 let command = Command::try_from(value).unwrap();
109 assert_eq!(
110 command,
111 Command::Challenge(ChallengeCommand::SolveOption(0))
112 );
113 }
114
115 #[test]
116 fn test_parse_challenge_command_with_result() {
117 let json = r#"{"type":"Challenge","action":"Finish","result":{"id":"123","performance":0.0,"data":{}}}"#;
118 let value: serde_json::Value = serde_json::from_str(json).unwrap();
119 let command = Command::try_from(value).unwrap();
120 assert_eq!(
121 command,
122 Command::Challenge(ChallengeCommand::Finish(Some(ChallengeResult::Custom(
123 CustomChallengeResult {
124 id: "123".to_string(),
125 performance: 0.0,
126 data: serde_json::json!({}),
127 }
128 ))))
129 );
130 }
131
132 #[test]
133 fn test_parse_unknown_command() {
134 let json = r#"{"type":"Unknown","action":"NextTask"}"#;
135 let value: serde_json::Value = serde_json::from_str(json).unwrap();
136 let result = Command::try_from(value);
137 assert!(result.is_err());
138 assert_eq!(
139 result.unwrap_err(),
140 CommandError::UnknownCommandType("Unknown".to_string())
141 );
142 }
143
144 #[test]
145 fn test_parse_missing_data() {
146 let json = r#"{"type":"Challenge"}"#;
147 let value: serde_json::Value = serde_json::from_str(json).unwrap();
148 let result = Command::try_from(value);
149 assert!(result.is_err());
150 assert_eq!(result.unwrap_err(), CommandError::MissingData);
151 }
152
153 #[test]
154 fn test_parse_missing_option_index() {
155 let json = r#"{"type":"Challenge","action":"SolveOption"}"#;
156 let value: serde_json::Value = serde_json::from_str(json).unwrap();
157 let result = Command::try_from(value);
158 assert!(result.is_err());
159 assert_eq!(result.unwrap_err(), CommandError::MissingData);
160 }
161
162 #[test]
163 fn test_parse_invalid_json() {
164 let json = r#"{"type":"Challenge","action":"SolveOption","optionIndex":"invalid"}"#;
165 let value: serde_json::Value = serde_json::from_str(json).unwrap();
166 let result = Command::try_from(value);
167 assert!(result.is_err());
168 assert!(matches!(result.unwrap_err(), CommandError::InvalidData(_)));
169 }
170
171 #[cfg(target_arch = "wasm32")]
172 mod wasm_tests {
173 use super::*;
174 use wasm_bindgen_test::*;
175
176 #[wasm_bindgen_test]
177 fn test_parse_game_event_js() {
178 let json = r#"{"type":"Game", "action":"NextChallenge"}"#;
179 let value: JsValue = js_sys::JSON::parse(json).unwrap();
180 let command = Command::try_from(value).unwrap();
181 assert_eq!(command, Command::Game(GameCommand::NextChallenge));
182 }
183
184 #[wasm_bindgen_test]
185 fn test_parse_challenge_event_js() {
186 let json = r#"{"type":"Challenge", "action":"NextTask"}"#;
187 let value: JsValue = js_sys::JSON::parse(json).unwrap();
188 let command = Command::try_from(value).unwrap();
189 assert_eq!(command, Command::Challenge(ChallengeCommand::NextTask));
190 }
191
192 #[wasm_bindgen_test]
193 fn test_parse_challenge_event_with_option_js() {
194 let json = r#"{"type":"Challenge", "action":"SolveOption", "optionIndex":0}"#;
195 let value: JsValue = js_sys::JSON::parse(json).unwrap();
196 let command = Command::try_from(value).unwrap();
197 assert_eq!(
198 command,
199 Command::Challenge(ChallengeCommand::SolveOption(0))
200 );
201 }
202
203 #[wasm_bindgen_test]
204 fn test_parse_challenge_event_with_result_js() {
205 let json = r#"{"type":"Challenge", "action":"Finish", "result":{"id":"123","performance":0.0,"data":{}}}"#;
206 let value: JsValue = js_sys::JSON::parse(json).unwrap();
207 let command = Command::try_from(value).unwrap();
208 assert_eq!(
209 command,
210 Command::Challenge(ChallengeCommand::Finish(Some(ChallengeResult::Custom(
211 CustomChallengeResult {
212 id: "123".to_string(),
213 performance: 0.0,
214 data: serde_json::json!({}),
215 }
216 ))))
217 );
218 }
219
220 #[wasm_bindgen_test]
221 fn test_parse_unknown_command_js() {
222 let json = r#"{"type":"Unknown", "action":"NextTask"}"#;
223 let value: JsValue = js_sys::JSON::parse(json).unwrap();
224 let result = Command::try_from(value);
225 assert!(result.is_err());
226 assert_eq!(
227 result.unwrap_err(),
228 CommandError::UnknownCommandType("Unknown".to_string())
229 );
230 }
231
232 #[wasm_bindgen_test]
233 fn test_parse_missing_data_js() {
234 let json = r#"{"type":"Challenge"}"#;
235 let value: JsValue = js_sys::JSON::parse(json).unwrap();
236 let result = Command::try_from(value);
237 assert!(result.is_err());
238 assert_eq!(result.unwrap_err(), CommandError::MissingData);
239 }
240
241 #[wasm_bindgen_test]
242 fn test_parse_missing_option_index_js() {
243 let json = r#"{"type":"Challenge", "action":"SolveOption"}"#;
244 let value: JsValue = js_sys::JSON::parse(json).unwrap();
245 let result = Command::try_from(value);
246 assert!(result.is_err());
247 assert_eq!(result.unwrap_err(), CommandError::MissingData);
248 }
249 }
250}