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::{Error, ErrorCode, MpvDataType, PlaylistEntry};
8

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

            
14
impl TypeHandler for String {
15
4
    fn get_value(value: Value) -> Result<String, Error> {
16
4
        value
17
4
            .as_str()
18
4
            .ok_or(Error(ErrorCode::ValueDoesNotContainString))
19
4
            .map(|s| s.to_string())
20
4
    }
21

            
22
    fn as_string(&self) -> String {
23
        self.to_string()
24
    }
25
}
26

            
27
impl TypeHandler for bool {
28
404
    fn get_value(value: Value) -> Result<bool, Error> {
29
404
        value
30
404
            .as_bool()
31
404
            .ok_or(Error(ErrorCode::ValueDoesNotContainBool))
32
404
    }
33

            
34
    fn as_string(&self) -> String {
35
        if *self {
36
            "true".to_string()
37
        } else {
38
            "false".to_string()
39
        }
40
    }
41
}
42

            
43
impl TypeHandler for f64 {
44
12988
    fn get_value(value: Value) -> Result<f64, Error> {
45
12988
        value
46
12988
            .as_f64()
47
12988
            .ok_or(Error(ErrorCode::ValueDoesNotContainF64))
48
12988
    }
49

            
50
    fn as_string(&self) -> String {
51
        self.to_string()
52
    }
53
}
54

            
55
impl TypeHandler for usize {
56
    fn get_value(value: Value) -> Result<usize, Error> {
57
        value
58
            .as_u64()
59
            .map(|u| u as usize)
60
            .ok_or(Error(ErrorCode::ValueDoesNotContainUsize))
61
    }
62

            
63
    fn as_string(&self) -> String {
64
        self.to_string()
65
    }
66
}
67

            
68
impl TypeHandler for HashMap<String, MpvDataType> {
69
    fn get_value(value: Value) -> Result<HashMap<String, MpvDataType>, Error> {
70
        value
71
            .as_object()
72
            .ok_or(Error(ErrorCode::ValueDoesNotContainHashMap))
73
            .map(json_map_to_hashmap)
74
    }
75

            
76
    fn as_string(&self) -> String {
77
        format!("{:?}", self)
78
    }
79
}
80

            
81
impl TypeHandler for Vec<PlaylistEntry> {
82
16
    fn get_value(value: Value) -> Result<Vec<PlaylistEntry>, Error> {
83
16
        value
84
16
            .as_array()
85
16
            .ok_or(Error(ErrorCode::ValueDoesNotContainPlaylist))
86
16
            .map(|array| json_array_to_playlist(array))
87
16
    }
88

            
89
    fn as_string(&self) -> String {
90
        format!("{:?}", self)
91
    }
92
}
93

            
94
44
pub(crate) fn json_to_value(value: &Value) -> Result<MpvDataType, Error> {
95
44
    match value {
96
3
        Value::Array(array) => Ok(MpvDataType::Array(json_array_to_vec(array))),
97
7
        Value::Bool(b) => Ok(MpvDataType::Bool(*b)),
98
22
        Value::Number(n) => {
99
22
            if n.is_i64() && n.as_i64().unwrap() == -1 {
100
3
                Ok(MpvDataType::MinusOne)
101
19
            } else if n.is_u64() {
102
12
                Ok(MpvDataType::Usize(n.as_u64().unwrap() as usize))
103
7
            } else if n.is_f64() {
104
7
                Ok(MpvDataType::Double(n.as_f64().unwrap()))
105
            } else {
106
                // TODO: proper error handling
107
                panic!("Unexpected number type");
108
            }
109
        }
110
3
        Value::Object(map) => Ok(MpvDataType::HashMap(json_map_to_hashmap(map))),
111
6
        Value::String(s) => Ok(MpvDataType::String(s.to_string())),
112
3
        Value::Null => Ok(MpvDataType::Null),
113
    }
114
44
}
115

            
116
4
pub(crate) fn json_map_to_hashmap(
117
4
    map: &serde_json::map::Map<String, Value>,
118
4
) -> HashMap<String, MpvDataType> {
119
4
    let mut output_map: HashMap<String, MpvDataType> = HashMap::new();
120
11
    for (ref key, value) in map.iter() {
121
        // TODO: proper error handling
122
11
        if let Ok(value) = json_to_value(value) {
123
11
            output_map.insert(key.to_string(), value);
124
11
        }
125
    }
126
4
    output_map
127
4
}
128

            
129
5
pub(crate) fn json_array_to_vec(array: &[Value]) -> Vec<MpvDataType> {
130
5
    array
131
5
        .iter()
132
5
        // TODO: proper error handling
133
25
        .filter_map(|entry| json_to_value(entry).ok())
134
5
        .collect()
135
5
}
136

            
137
17
pub(crate) fn json_array_to_playlist(array: &[Value]) -> Vec<PlaylistEntry> {
138
17
    let mut output: Vec<PlaylistEntry> = Vec::new();
139
26
    for (id, entry) in array.iter().enumerate() {
140
26
        let mut filename: String = String::new();
141
26
        let mut title: String = String::new();
142
26
        let mut current: bool = false;
143
26
        if let Value::String(ref f) = entry["filename"] {
144
26
            filename = f.to_string();
145
26
        }
146
26
        if let Value::String(ref t) = entry["title"] {
147
26
            title = t.to_string();
148
26
        }
149
26
        if let Value::Bool(ref b) = entry["current"] {
150
26
            current = *b;
151
26
        }
152
26
        output.push(PlaylistEntry {
153
26
            id,
154
26
            filename,
155
26
            title,
156
26
            current,
157
26
        });
158
    }
159
17
    output
160
17
}
161

            
162
#[cfg(test)]
163
mod test {
164
    use super::*;
165
    use crate::MpvDataType;
166
    use serde_json::json;
167
    use std::collections::HashMap;
168

            
169
    #[test]
170
    fn test_json_map_to_hashmap() {
171
        let json = json!({
172
            "array": [1, 2, 3],
173
            "bool": true,
174
            "double": 1.0,
175
            "usize": 1,
176
            "minus_one": -1,
177
            "null": null,
178
            "string": "string",
179
            "object": {
180
                "key": "value"
181
            }
182
        });
183

            
184
        let mut expected = HashMap::new();
185
        expected.insert(
186
            "array".to_string(),
187
            MpvDataType::Array(vec![
188
                MpvDataType::Usize(1),
189
                MpvDataType::Usize(2),
190
                MpvDataType::Usize(3),
191
            ]),
192
        );
193
        expected.insert("bool".to_string(), MpvDataType::Bool(true));
194
        expected.insert("double".to_string(), MpvDataType::Double(1.0));
195
        expected.insert("usize".to_string(), MpvDataType::Usize(1));
196
        expected.insert("minus_one".to_string(), MpvDataType::MinusOne);
197
        expected.insert("null".to_string(), MpvDataType::Null);
198
        expected.insert(
199
            "string".to_string(),
200
            MpvDataType::String("string".to_string()),
201
        );
202
        expected.insert(
203
            "object".to_string(),
204
            MpvDataType::HashMap(HashMap::from([(
205
                "key".to_string(),
206
                MpvDataType::String("value".to_string()),
207
            )])),
208
        );
209

            
210
        assert_eq!(json_map_to_hashmap(json.as_object().unwrap()), expected);
211
    }
212

            
213
    #[test]
214
    fn test_json_array_to_vec() {
215
        let json = json!([
216
            [1, 2, 3],
217
            true,
218
            1.0,
219
            1,
220
            -1,
221
            null,
222
            "string",
223
            {
224
                "key": "value"
225
            }
226
        ]);
227

            
228
        println!("{:?}", json.as_array().unwrap());
229
        println!("{:?}", json_array_to_vec(json.as_array().unwrap()));
230

            
231
        let expected = vec![
232
            MpvDataType::Array(vec![
233
                MpvDataType::Usize(1),
234
                MpvDataType::Usize(2),
235
                MpvDataType::Usize(3),
236
            ]),
237
            MpvDataType::Bool(true),
238
            MpvDataType::Double(1.0),
239
            MpvDataType::Usize(1),
240
            MpvDataType::MinusOne,
241
            MpvDataType::Null,
242
            MpvDataType::String("string".to_string()),
243
            MpvDataType::HashMap(HashMap::from([(
244
                "key".to_string(),
245
                MpvDataType::String("value".to_string()),
246
            )])),
247
        ];
248

            
249
        assert_eq!(json_array_to_vec(json.as_array().unwrap()), expected);
250
    }
251

            
252
    #[test]
253
    fn test_json_array_to_playlist() {
254
        let json = json!([
255
            {
256
                "filename": "file1",
257
                "title": "title1",
258
                "current": true
259
            },
260
            {
261
                "filename": "file2",
262
                "title": "title2",
263
                "current": false
264
            }
265
        ]);
266

            
267
        let expected = vec![
268
            PlaylistEntry {
269
                id: 0,
270
                filename: "file1".to_string(),
271
                title: "title1".to_string(),
272
                current: true,
273
            },
274
            PlaylistEntry {
275
                id: 1,
276
                filename: "file2".to_string(),
277
                title: "title2".to_string(),
278
                current: false,
279
            },
280
        ];
281

            
282
        assert_eq!(json_array_to_playlist(json.as_array().unwrap()), expected);
283
    }
284
}