1
//! JSON parsing logic for properties returned in [`Event::PropertyChange`]
2
//!
3
//! This module is used to parse the json data from the `data` field of the
4
//! [`Event::PropertyChange`] variant. Mpv has about 1000 different properties
5
//! as of `v0.38.0`, so this module will only implement the most common ones.
6

            
7
use std::collections::HashMap;
8

            
9
use serde::{Deserialize, Serialize};
10

            
11
use crate::{Error, ErrorCode, Event, MpvDataType, PlaylistEntry};
12

            
13
/// All possible properties that can be observed through the event system.
14
///
15
/// Not all properties are guaranteed to be implemented.
16
/// If something is missing, please open an issue.
17
///
18
/// Otherwise, the property will be returned as a `Property::Unknown` variant.
19
///
20
/// See <https://mpv.io/manual/master/#properties> for
21
/// the upstream list of properties.
22
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23
#[serde(rename_all = "kebab-case")]
24
pub enum Property {
25
    Path(Option<String>),
26
    Pause(bool),
27
    PlaybackTime(Option<f64>),
28
    Duration(Option<f64>),
29
    Metadata(Option<HashMap<String, MpvDataType>>),
30
    Playlist(Vec<PlaylistEntry>),
31
    PlaylistPos(Option<usize>),
32
    LoopFile(LoopProperty),
33
    LoopPlaylist(LoopProperty),
34
    Speed(f64),
35
    Volume(f64),
36
    Mute(bool),
37
    Unknown { name: String, data: MpvDataType },
38
}
39

            
40
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
41
#[serde(rename_all = "kebab-case")]
42
pub enum LoopProperty {
43
    N(usize),
44
    Inf,
45
    No,
46
}
47

            
48
/// Parse a highlevel [`Property`] object from json, used for [`Event::PropertyChange`].
49
8
pub fn parse_event_property(event: Event) -> Result<(usize, Property), Error> {
50
8
    let (id, name, data) = match event {
51
8
        Event::PropertyChange { id, name, data } => (id, name, data),
52
        // TODO: return proper error
53
        _ => {
54
            panic!("Event is not a PropertyChange event")
55
        }
56
    };
57

            
58
8
    match name.as_str() {
59
8
        "path" => {
60
            let path = match data {
61
                MpvDataType::String(s) => Some(s),
62
                MpvDataType::Null => None,
63
                _ => return Err(Error(ErrorCode::ValueDoesNotContainString)),
64
            };
65
            Ok((id, Property::Path(path)))
66
        }
67
8
        "pause" => {
68
4
            let pause = match data {
69
4
                MpvDataType::Bool(b) => b,
70
                _ => return Err(Error(ErrorCode::ValueDoesNotContainBool)),
71
            };
72
4
            Ok((id, Property::Pause(pause)))
73
        }
74
4
        "playback-time" => {
75
            let playback_time = match data {
76
                MpvDataType::Double(d) => Some(d),
77
                MpvDataType::Null => None,
78
                _ => return Err(Error(ErrorCode::ValueDoesNotContainF64)),
79
            };
80
            Ok((id, Property::PlaybackTime(playback_time)))
81
        }
82
4
        "duration" => {
83
            let duration = match data {
84
                MpvDataType::Double(d) => Some(d),
85
                MpvDataType::Null => None,
86
                _ => return Err(Error(ErrorCode::ValueDoesNotContainF64)),
87
            };
88
            Ok((id, Property::Duration(duration)))
89
        }
90
4
        "metadata" => {
91
            let metadata = match data {
92
                MpvDataType::HashMap(m) => Some(m),
93
                MpvDataType::Null => None,
94
                _ => return Err(Error(ErrorCode::ValueDoesNotContainHashMap)),
95
            };
96
            Ok((id, Property::Metadata(metadata)))
97
        }
98
        // "playlist" => {
99
        //     let playlist = match data {
100
        //         MpvDataType::Array(a) => json_array_to_playlist(&a),
101
        //         MpvDataType::Null => Vec::new(),
102
        //         _ => return Err(Error(ErrorCode::ValueDoesNotContainPlaylist)),
103
        //     };
104
        //     Ok((id, Property::Playlist(playlist)))
105
        // }
106
4
        "playlist-pos" => {
107
            let playlist_pos = match data {
108
                MpvDataType::Usize(u) => Some(u),
109
                MpvDataType::MinusOne => None,
110
                MpvDataType::Null => None,
111
                _ => return Err(Error(ErrorCode::ValueDoesNotContainUsize)),
112
            };
113
            Ok((id, Property::PlaylistPos(playlist_pos)))
114
        }
115
4
        "loop-file" => {
116
            let loop_file = match data {
117
                MpvDataType::Usize(n) => LoopProperty::N(n),
118
                MpvDataType::Bool(b) => match b {
119
                    true => LoopProperty::Inf,
120
                    false => LoopProperty::No,
121
                },
122
                MpvDataType::String(s) => match s.as_str() {
123
                    "inf" => LoopProperty::Inf,
124
                    "no" => LoopProperty::No,
125
                    _ => return Err(Error(ErrorCode::ValueDoesNotContainString)),
126
                },
127
                _ => return Err(Error(ErrorCode::ValueDoesNotContainString)),
128
            };
129
            Ok((id, Property::LoopFile(loop_file)))
130
        }
131
4
        "loop-playlist" => {
132
            let loop_playlist = match data {
133
                MpvDataType::Usize(n) => LoopProperty::N(n),
134
                MpvDataType::Bool(b) => match b {
135
                    true => LoopProperty::Inf,
136
                    false => LoopProperty::No,
137
                },
138
                MpvDataType::String(s) => match s.as_str() {
139
                    "inf" => LoopProperty::Inf,
140
                    "no" => LoopProperty::No,
141
                    _ => return Err(Error(ErrorCode::ValueDoesNotContainString)),
142
                },
143
                _ => return Err(Error(ErrorCode::ValueDoesNotContainString)),
144
            };
145
            Ok((id, Property::LoopPlaylist(loop_playlist)))
146
        }
147
4
        "speed" => {
148
            let speed = match data {
149
                MpvDataType::Double(d) => d,
150
                _ => return Err(Error(ErrorCode::ValueDoesNotContainF64)),
151
            };
152
            Ok((id, Property::Speed(speed)))
153
        }
154
4
        "volume" => {
155
4
            let volume = match data {
156
4
                MpvDataType::Double(d) => d,
157
                _ => return Err(Error(ErrorCode::ValueDoesNotContainF64)),
158
            };
159
4
            Ok((id, Property::Volume(volume)))
160
        }
161
        "mute" => {
162
            let mute = match data {
163
                MpvDataType::Bool(b) => b,
164
                _ => return Err(Error(ErrorCode::ValueDoesNotContainBool)),
165
            };
166
            Ok((id, Property::Mute(mute)))
167
        }
168
        // TODO: add missing cases
169
        _ => Ok((id, Property::Unknown { name, data })),
170
    }
171
8
}