1
//! JSON parsing logic for command responses from [`MpvIpc`](crate::ipc::MpvIpc).
2

            
3
use std::collections::HashMap;
4

            
5
use serde_json::Value;
6

            
7
use crate::{MpvDataType, MpvError, PlaylistEntry};
8

            
9
pub trait TypeHandler: Sized {
10
    fn get_value(value: Value) -> Result<Self, MpvError>;
11
    fn as_string(&self) -> String;
12
}
13

            
14
impl TypeHandler for String {
15
2
    fn get_value(value: Value) -> Result<String, MpvError> {
16
2
        value
17
2
            .as_str()
18
2
            .ok_or(MpvError::ValueContainsUnexpectedType {
19
2
                expected_type: "String".to_string(),
20
2
                received: value.clone(),
21
2
            })
22
2
            .map(|s| s.to_string())
23
2
    }
24

            
25
    fn as_string(&self) -> String {
26
        self.to_string()
27
    }
28
}
29

            
30
impl TypeHandler for bool {
31
204
    fn get_value(value: Value) -> Result<bool, MpvError> {
32
204
        value
33
204
            .as_bool()
34
204
            .ok_or(MpvError::ValueContainsUnexpectedType {
35
204
                expected_type: "bool".to_string(),
36
204
                received: value.clone(),
37
204
            })
38
204
    }
39

            
40
    fn as_string(&self) -> String {
41
        if *self {
42
            "true".to_string()
43
        } else {
44
            "false".to_string()
45
        }
46
    }
47
}
48

            
49
impl TypeHandler for f64 {
50
2280
    fn get_value(value: Value) -> Result<f64, MpvError> {
51
2280
        value.as_f64().ok_or(MpvError::ValueContainsUnexpectedType {
52
2280
            expected_type: "f64".to_string(),
53
2280
            received: value.clone(),
54
2280
        })
55
2280
    }
56

            
57
    fn as_string(&self) -> String {
58
        self.to_string()
59
    }
60
}
61

            
62
impl 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

            
78
impl TypeHandler for MpvDataType {
79
4
    fn get_value(value: Value) -> Result<MpvDataType, MpvError> {
80
4
        json_to_value(&value)
81
4
    }
82

            
83
    fn as_string(&self) -> String {
84
        format!("{:?}", self)
85
    }
86
}
87

            
88
impl 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

            
104
impl 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

            
120
82
pub(crate) fn json_to_value(value: &Value) -> Result<MpvDataType, MpvError> {
121
82
    match value {
122
7
        Value::Array(array) => Ok(MpvDataType::Array(json_array_to_vec(array)?)),
123
19
        Value::Bool(b) => Ok(MpvDataType::Bool(*b)),
124
26
        Value::Number(n) => {
125
26
            if n.is_i64() && n.as_i64().unwrap() == -1 {
126
3
                Ok(MpvDataType::MinusOne)
127
23
            } else if n.is_u64() {
128
12
                Ok(MpvDataType::Usize(n.as_u64().unwrap() as usize))
129
11
            } else if n.is_f64() {
130
11
                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
9
        Value::Object(map) => Ok(MpvDataType::HashMap(json_map_to_hashmap(map)?)),
139
18
        Value::String(s) => Ok(MpvDataType::String(s.to_string())),
140
3
        Value::Null => Ok(MpvDataType::Null),
141
    }
142
82
}
143

            
144
10
pub(crate) fn json_map_to_hashmap(
145
10
    map: &serde_json::map::Map<String, Value>,
146
10
) -> Result<HashMap<String, MpvDataType>, MpvError> {
147
10
    let mut output_map: HashMap<String, MpvDataType> = HashMap::new();
148
29
    for (ref key, value) in map.iter() {
149
29
        output_map.insert(key.to_string(), json_to_value(value)?);
150
    }
151
10
    Ok(output_map)
152
10
}
153

            
154
9
pub(crate) fn json_array_to_vec(array: &[Value]) -> Result<Vec<MpvDataType>, MpvError> {
155
9
    array.iter().map(json_to_value).collect()
156
9
}
157

            
158
3
fn json_map_to_playlist_entry(
159
3
    map: &serde_json::map::Map<String, Value>,
160
3
) -> Result<PlaylistEntry, MpvError> {
161
3
    let filename = match map.get("filename") {
162
3
        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
3
    let title = match map.get("title") {
172
2
        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
1
        None => None,
180
    };
181
3
    let current = match map.get("current") {
182
3
        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
3
    Ok(PlaylistEntry {
192
3
        id: 0,
193
3
        filename,
194
3
        title,
195
3
        current,
196
3
    })
197
3
}
198

            
199
1
pub(crate) fn json_array_to_playlist(array: &[Value]) -> Result<Vec<PlaylistEntry>, MpvError> {
200
1
    array
201
1
        .iter()
202
3
        .map(|entry| match entry {
203
3
            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
3
        })
209
1
        .enumerate()
210
3
        .map(|(id, entry)| {
211
3
            entry.map(|mut entry| {
212
3
                entry.id = id;
213
3
                entry
214
3
            })
215
3
        })
216
1
        .collect()
217
1
}
218

            
219
#[cfg(test)]
220
mod 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
}