1use std::collections::HashMap;
4
5use serde_json::Value;
6
7use crate::{MpvDataType, MpvError, PlaylistEntry};
8
9pub trait TypeHandler: Sized {
10 fn get_value(value: Value) -> Result<Self, MpvError>;
11 fn as_string(&self) -> String;
12}
13
14impl TypeHandler for String {
15 fn get_value(value: Value) -> Result<String, MpvError> {
16 value
17 .as_str()
18 .ok_or(MpvError::ValueContainsUnexpectedType {
19 expected_type: "String".to_string(),
20 received: value.clone(),
21 })
22 .map(|s| s.to_string())
23 }
24
25 fn as_string(&self) -> String {
26 self.to_string()
27 }
28}
29
30impl TypeHandler for bool {
31 fn get_value(value: Value) -> Result<bool, MpvError> {
32 value
33 .as_bool()
34 .ok_or(MpvError::ValueContainsUnexpectedType {
35 expected_type: "bool".to_string(),
36 received: value.clone(),
37 })
38 }
39
40 fn as_string(&self) -> String {
41 if *self {
42 "true".to_string()
43 } else {
44 "false".to_string()
45 }
46 }
47}
48
49impl TypeHandler for f64 {
50 fn get_value(value: Value) -> Result<f64, MpvError> {
51 value.as_f64().ok_or(MpvError::ValueContainsUnexpectedType {
52 expected_type: "f64".to_string(),
53 received: value.clone(),
54 })
55 }
56
57 fn as_string(&self) -> String {
58 self.to_string()
59 }
60}
61
62impl TypeHandler for usize {
63 fn get_value(value: Value) -> Result<usize, MpvError> {
64 value
65 .as_u64()
66 .map(|u| u as usize)
67 .ok_or(MpvError::ValueContainsUnexpectedType {
68 expected_type: "usize".to_string(),
69 received: value.clone(),
70 })
71 }
72
73 fn as_string(&self) -> String {
74 self.to_string()
75 }
76}
77
78impl TypeHandler for MpvDataType {
79 fn get_value(value: Value) -> Result<MpvDataType, MpvError> {
80 json_to_value(&value)
81 }
82
83 fn as_string(&self) -> String {
84 format!("{:?}", self)
85 }
86}
87
88impl TypeHandler for HashMap<String, MpvDataType> {
89 fn get_value(value: Value) -> Result<HashMap<String, MpvDataType>, MpvError> {
90 value
91 .as_object()
92 .ok_or(MpvError::ValueContainsUnexpectedType {
93 expected_type: "Map<String, Value>".to_string(),
94 received: value.clone(),
95 })
96 .and_then(json_map_to_hashmap)
97 }
98
99 fn as_string(&self) -> String {
100 format!("{:?}", self)
101 }
102}
103
104impl TypeHandler for Vec<PlaylistEntry> {
105 fn get_value(value: Value) -> Result<Vec<PlaylistEntry>, MpvError> {
106 value
107 .as_array()
108 .ok_or(MpvError::ValueContainsUnexpectedType {
109 expected_type: "Array<Value>".to_string(),
110 received: value.clone(),
111 })
112 .and_then(|array| json_array_to_playlist(array))
113 }
114
115 fn as_string(&self) -> String {
116 format!("{:?}", self)
117 }
118}
119
120pub(crate) fn json_to_value(value: &Value) -> Result<MpvDataType, MpvError> {
121 match value {
122 Value::Array(array) => Ok(MpvDataType::Array(json_array_to_vec(array)?)),
123 Value::Bool(b) => Ok(MpvDataType::Bool(*b)),
124 Value::Number(n) => {
125 if n.is_i64() && n.as_i64().unwrap() == -1 {
126 Ok(MpvDataType::MinusOne)
127 } else if n.is_u64() {
128 Ok(MpvDataType::Usize(n.as_u64().unwrap() as usize))
129 } else if n.is_f64() {
130 Ok(MpvDataType::Double(n.as_f64().unwrap()))
131 } else {
132 Err(MpvError::ValueContainsUnexpectedType {
133 expected_type: "i64, u64, or f64".to_string(),
134 received: value.clone(),
135 })
136 }
137 }
138 Value::Object(map) => Ok(MpvDataType::HashMap(json_map_to_hashmap(map)?)),
139 Value::String(s) => Ok(MpvDataType::String(s.to_string())),
140 Value::Null => Ok(MpvDataType::Null),
141 }
142}
143
144pub(crate) fn json_map_to_hashmap(
145 map: &serde_json::map::Map<String, Value>,
146) -> Result<HashMap<String, MpvDataType>, MpvError> {
147 let mut output_map: HashMap<String, MpvDataType> = HashMap::new();
148 for (ref key, value) in map.iter() {
149 output_map.insert(key.to_string(), json_to_value(value)?);
150 }
151 Ok(output_map)
152}
153
154pub(crate) fn json_array_to_vec(array: &[Value]) -> Result<Vec<MpvDataType>, MpvError> {
155 array.iter().map(json_to_value).collect()
156}
157
158fn json_map_to_playlist_entry(
159 map: &serde_json::map::Map<String, Value>,
160) -> Result<PlaylistEntry, MpvError> {
161 let filename = match map.get("filename") {
162 Some(Value::String(s)) => s.to_string(),
163 Some(data) => {
164 return Err(MpvError::ValueContainsUnexpectedType {
165 expected_type: "String".to_owned(),
166 received: data.clone(),
167 })
168 }
169 None => return Err(MpvError::MissingMpvData),
170 };
171 let title = match map.get("title") {
172 Some(Value::String(s)) => Some(s.to_string()),
173 Some(data) => {
174 return Err(MpvError::ValueContainsUnexpectedType {
175 expected_type: "String".to_owned(),
176 received: data.clone(),
177 })
178 }
179 None => None,
180 };
181 let current = match map.get("current") {
182 Some(Value::Bool(b)) => *b,
183 Some(data) => {
184 return Err(MpvError::ValueContainsUnexpectedType {
185 expected_type: "bool".to_owned(),
186 received: data.clone(),
187 })
188 }
189 None => false,
190 };
191 Ok(PlaylistEntry {
192 id: 0,
193 filename,
194 title,
195 current,
196 })
197}
198
199pub(crate) fn json_array_to_playlist(array: &[Value]) -> Result<Vec<PlaylistEntry>, MpvError> {
200 array
201 .iter()
202 .map(|entry| match entry {
203 Value::Object(map) => json_map_to_playlist_entry(map),
204 data => Err(MpvError::ValueContainsUnexpectedType {
205 expected_type: "Map<String, Value>".to_owned(),
206 received: data.clone(),
207 }),
208 })
209 .enumerate()
210 .map(|(id, entry)| {
211 entry.map(|mut entry| {
212 entry.id = id;
213 entry
214 })
215 })
216 .collect()
217}
218
219#[cfg(test)]
220mod test {
221 use super::*;
222 use crate::MpvDataType;
223 use serde_json::json;
224 use std::collections::HashMap;
225
226 #[test]
227 fn test_json_map_to_hashmap() {
228 let json = json!({
229 "array": [1, 2, 3],
230 "bool": true,
231 "double": 1.0,
232 "usize": 1,
233 "minus_one": -1,
234 "null": null,
235 "string": "string",
236 "object": {
237 "key": "value"
238 }
239 });
240
241 let mut expected = HashMap::new();
242 expected.insert(
243 "array".to_string(),
244 MpvDataType::Array(vec![
245 MpvDataType::Usize(1),
246 MpvDataType::Usize(2),
247 MpvDataType::Usize(3),
248 ]),
249 );
250 expected.insert("bool".to_string(), MpvDataType::Bool(true));
251 expected.insert("double".to_string(), MpvDataType::Double(1.0));
252 expected.insert("usize".to_string(), MpvDataType::Usize(1));
253 expected.insert("minus_one".to_string(), MpvDataType::MinusOne);
254 expected.insert("null".to_string(), MpvDataType::Null);
255 expected.insert(
256 "string".to_string(),
257 MpvDataType::String("string".to_string()),
258 );
259 expected.insert(
260 "object".to_string(),
261 MpvDataType::HashMap(HashMap::from([(
262 "key".to_string(),
263 MpvDataType::String("value".to_string()),
264 )])),
265 );
266
267 match json_map_to_hashmap(json.as_object().unwrap()) {
268 Ok(m) => assert_eq!(m, expected),
269 Err(e) => panic!("{:?}", e),
270 }
271 }
272
273 #[test]
274 fn test_json_array_to_vec() {
275 let json = json!([
276 [1, 2, 3],
277 true,
278 1.0,
279 1,
280 -1,
281 null,
282 "string",
283 {
284 "key": "value"
285 }
286 ]);
287
288 println!("{:?}", json.as_array().unwrap());
289 println!("{:?}", json_array_to_vec(json.as_array().unwrap()));
290
291 let expected = vec![
292 MpvDataType::Array(vec![
293 MpvDataType::Usize(1),
294 MpvDataType::Usize(2),
295 MpvDataType::Usize(3),
296 ]),
297 MpvDataType::Bool(true),
298 MpvDataType::Double(1.0),
299 MpvDataType::Usize(1),
300 MpvDataType::MinusOne,
301 MpvDataType::Null,
302 MpvDataType::String("string".to_string()),
303 MpvDataType::HashMap(HashMap::from([(
304 "key".to_string(),
305 MpvDataType::String("value".to_string()),
306 )])),
307 ];
308
309 match json_array_to_vec(json.as_array().unwrap()) {
310 Ok(v) => assert_eq!(v, expected),
311 Err(e) => panic!("{:?}", e),
312 }
313 }
314
315 #[test]
316 fn test_json_array_to_playlist() -> Result<(), MpvError> {
317 let json = json!([
318 {
319 "filename": "file1",
320 "title": "title1",
321 "current": true
322 },
323 {
324 "filename": "file2",
325 "title": "title2",
326 "current": false
327 },
328 {
329 "filename": "file3",
330 "current": false
331 }
332 ]);
333
334 let expected = vec![
335 PlaylistEntry {
336 id: 0,
337 filename: "file1".to_string(),
338 title: Some("title1".to_string()),
339 current: true,
340 },
341 PlaylistEntry {
342 id: 1,
343 filename: "file2".to_string(),
344 title: Some("title2".to_string()),
345 current: false,
346 },
347 PlaylistEntry {
348 id: 2,
349 filename: "file3".to_string(),
350 title: None,
351 current: false,
352 },
353 ];
354
355 assert_eq!(json_array_to_playlist(json.as_array().unwrap())?, expected);
356
357 Ok(())
358 }
359}