1
use std::{
2
    collections::{HashMap, HashSet},
3
    str::SplitWhitespace,
4
};
5

            
6
use crate::Request;
7

            
8
mod audio_output_devices;
9
mod client_to_client;
10
mod connection_settings;
11
mod controlling_playback;
12
mod mounts_and_neighbors;
13
mod music_database;
14
mod partition_commands;
15
mod playback_options;
16
mod querying_mpd_status;
17
mod queue;
18
mod reflection;
19
mod stickers;
20
mod stored_playlists;
21

            
22
pub use audio_output_devices::*;
23
pub use client_to_client::*;
24
pub use connection_settings::*;
25
pub use controlling_playback::*;
26
pub use mounts_and_neighbors::*;
27
pub use music_database::*;
28
pub use partition_commands::*;
29
pub use playback_options::*;
30
pub use querying_mpd_status::*;
31
pub use queue::*;
32
pub use reflection::*;
33
pub use stickers::*;
34
pub use stored_playlists::*;
35

            
36
pub trait Command {
37
    type Response;
38
    // The command name used within the protocol
39
    const COMMAND: &'static str;
40

            
41
    // TODO: `parse_request` should be using a more custom splitter, that can handle
42
    //       quoted strings and escape characters. This is what mpd uses to provide arguments
43
    //       with spaces and whatnot.
44

            
45
    // A function to parse the remaining parts of the command, split by whitespace
46
    fn parse_request(parts: SplitWhitespace<'_>) -> RequestParserResult<'_>;
47

            
48
    fn parse_raw_request(raw: &str) -> RequestParserResult<'_> {
49
        let (line, rest) = raw
50
            .split_once('\n')
51
            .ok_or(RequestParserError::UnexpectedEOF)?;
52
        let mut parts = line.split_whitespace();
53

            
54
        let command_name = parts
55
            .next()
56
            .ok_or(RequestParserError::SyntaxError(0, line.to_string()))?
57
            .trim();
58

            
59
        debug_assert!(command_name == Self::COMMAND);
60

            
61
        Self::parse_request(parts).map(|(req, _)| (req, rest))
62
    }
63

            
64
    fn parse_response(
65
        parts: ResponseAttributes<'_>,
66
    ) -> Result<Self::Response, ResponseParserError<'_>>;
67

            
68
3
    fn parse_raw_response(raw: &str) -> Result<Self::Response, ResponseParserError<'_>> {
69
3
        let mut parts = Vec::new();
70
3
        let mut lines = raw.lines();
71
        loop {
72
29
            let line = lines.next().ok_or(ResponseParserError::UnexpectedEOF)?;
73
29
            if line.is_empty() {
74
                println!("Warning: empty line in response");
75
                continue;
76
29
            }
77
29

            
78
29
            if line == "OK" {
79
3
                break;
80
26
            }
81
26

            
82
26
            let mut keyval = line.splitn(2, ": ");
83
26
            let key = keyval
84
26
                .next()
85
26
                .ok_or(ResponseParserError::SyntaxError(0, line))?;
86

            
87
            // TODO: handle binary data, also verify binarylimit
88
26
            let value = keyval
89
26
                .next()
90
26
                .ok_or(ResponseParserError::SyntaxError(0, line))?;
91

            
92
26
            parts.push((key, GenericResponseValue::Text(value)));
93
        }
94

            
95
3
        Self::parse_response(parts.into())
96
3
    }
97
}
98

            
99
pub type RequestParserResult<'a> = Result<(Request, &'a str), RequestParserError>;
100

            
101
#[derive(Debug, Clone, PartialEq)]
102
pub enum RequestParserError {
103
    SyntaxError(u64, String),
104
    MissingCommandListEnd(u64),
105
    NestedCommandList(u64),
106
    UnexpectedCommandListEnd(u64),
107
    UnexpectedEOF,
108
    MissingNewline,
109
}
110

            
111
// TODO: should these be renamed to fit the mpd docs?
112
//       "Attribute" instead of "Property"?
113
#[derive(Debug, Clone, PartialEq)]
114
pub enum ResponseParserError<'a> {
115
    MissingProperty(&'a str),
116
    UnexpectedPropertyType(&'a str, &'a str),
117
    UnexpectedProperty(&'a str),
118
    InvalidProperty(&'a str, &'a str),
119
    SyntaxError(u64, &'a str),
120
    UnexpectedEOF,
121
    MissingNewline,
122
}
123

            
124
pub type GenericResponseResult<'a> = Result<GenericResponse<'a>, &'a str>;
125

            
126
pub type GenericResponse<'a> = HashMap<&'a str, GenericResponseValue<'a>>;
127

            
128
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
129
pub enum GenericResponseValue<'a> {
130
    Text(&'a str),
131
    Binary(&'a [u8]),
132
    // Many(Vec<GenericResponseValue<'a>>),
133
}
134

            
135
pub struct ResponseAttributes<'a>(Vec<(&'a str, GenericResponseValue<'a>)>);
136

            
137
// impl ResponseAttributes<'_> {
138
//     pub fn
139
// pub fn get<'a>(&self, key: &str) -> Option<&GenericResponseValue<'a>> {
140
//     self.0.iter().find_map(|(k, v)| if k == &key { Some(v) } else { None })
141
// }
142
// }
143

            
144
impl ResponseAttributes<'_> {
145
    pub fn is_empty(&self) -> bool {
146
        self.0.is_empty()
147
    }
148
}
149

            
150
impl<'a> From<HashMap<&'a str, GenericResponseValue<'a>>> for ResponseAttributes<'a> {
151
    fn from(map: HashMap<&'a str, GenericResponseValue<'a>>) -> Self {
152
        Self(map.into_iter().collect())
153
    }
154
}
155

            
156
impl<'a> From<ResponseAttributes<'a>> for HashMap<&'a str, GenericResponseValue<'a>> {
157
1
    fn from(val: ResponseAttributes<'a>) -> Self {
158
1
        debug_assert!({
159
            let mut uniq = HashSet::new();
160
            val.0.iter().all(move |x| uniq.insert(*x))
161
        });
162

            
163
1
        val.0.into_iter().collect()
164
1
    }
165
}
166

            
167
impl<'a> From<ResponseAttributes<'a>> for Vec<(&'a str, GenericResponseValue<'a>)> {
168
2
    fn from(val: ResponseAttributes<'a>) -> Self {
169
2
        val.0
170
2
    }
171
}
172

            
173
impl<'a> From<Vec<(&'a str, GenericResponseValue<'a>)>> for ResponseAttributes<'a> {
174
3
    fn from(val: Vec<(&'a str, GenericResponseValue<'a>)>) -> Self {
175
3
        Self(val)
176
3
    }
177
}
178

            
179
// TODO: There should probably be a helper that lets you extract and verify one, two or maybe
180
//       three properties without having to allocate a hashmap to get a nice API. We can retrieve
181
//       the properties by name with a loop on the inner vec.
182

            
183
/*******************/
184
/* Parsing Helpers */
185
/*******************/
186

            
187
macro_rules! get_property {
188
    ($parts:expr, $name:literal, $variant:ident) => {
189
        match $parts.get($name) {
190
            Some(crate::commands::GenericResponseValue::$variant(value)) => *value,
191
            Some(value) => {
192
                let actual_type = match value {
193
                    crate::commands::GenericResponseValue::Text(_) => "Text",
194
                    crate::commands::GenericResponseValue::Binary(_) => "Binary",
195
                };
196
                return Err(
197
                    crate::commands::ResponseParserError::UnexpectedPropertyType(
198
                        $name,
199
                        actual_type,
200
                    ),
201
                );
202
            }
203
            None => return Err(crate::commands::ResponseParserError::MissingProperty($name)),
204
        }
205
    };
206
}
207

            
208
macro_rules! get_optional_property {
209
    ($parts:expr, $name:literal, $variant:ident) => {
210
        match $parts.get($name) {
211
            Some(crate::commands::GenericResponseValue::$variant(value)) => Some(*value),
212
            Some(value) => {
213
                let actual_type = match value {
214
                    crate::commands::GenericResponseValue::Text(_) => "Text",
215
                    crate::commands::GenericResponseValue::Binary(_) => "Binary",
216
                };
217
                return Err(
218
                    crate::commands::ResponseParserError::UnexpectedPropertyType(
219
                        $name,
220
                        actual_type,
221
                    ),
222
                );
223
            }
224
            None => None,
225
        }
226
    };
227
}
228

            
229
macro_rules! get_and_parse_property {
230
    ($parts:ident, $name:literal, $variant:ident) => {
231
        match $parts.get($name) {
232
            Some(crate::commands::GenericResponseValue::$variant(value)) => (*value)
233
                .parse()
234
                .map_err(|_| crate::commands::ResponseParserError::InvalidProperty($name, value))?,
235
            Some(value) => {
236
                let actual_type = match value {
237
                    crate::commands::GenericResponseValue::Text(_) => "Text",
238
                    crate::commands::GenericResponseValue::Binary(_) => "Binary",
239
                };
240
                return Err(
241
                    crate::commands::ResponseParserError::UnexpectedPropertyType(
242
                        $name,
243
                        actual_type,
244
                    ),
245
                );
246
            }
247
            None => return Err(crate::commands::ResponseParserError::MissingProperty($name)),
248
        }
249
    };
250
}
251

            
252
macro_rules! get_and_parse_optional_property {
253
    ($parts:ident, $name:literal, $variant:ident) => {
254
        match $parts.get($name) {
255
            Some(crate::commands::GenericResponseValue::$variant(value)) => {
256
                Some((*value).parse().map_err(|_| {
257
                    crate::commands::ResponseParserError::InvalidProperty($name, value)
258
                })?)
259
            }
260
            Some(value) => {
261
                let actual_type = match value {
262
                    crate::commands::GenericResponseValue::Text(_) => "Text",
263
                    crate::commands::GenericResponseValue::Binary(_) => "Binary",
264
                };
265
                return Err(
266
                    crate::commands::ResponseParserError::UnexpectedPropertyType(
267
                        $name,
268
                        actual_type,
269
                    ),
270
                );
271
            }
272
            None => None,
273
        }
274
    };
275
}
276

            
277
pub(crate) use get_and_parse_optional_property;
278
pub(crate) use get_and_parse_property;
279
pub(crate) use get_optional_property;
280
pub(crate) use get_property;
281

            
282
#[cfg(test)]
283
mod tests {
284
    use super::*;
285

            
286
    #[test]
287
    #[cfg(debug_assertions)]
288
    fn test_valid_hashmap_uniqueness_assert() {
289
        let valid_maplike_attrs: ResponseAttributes = vec![
290
            ("a", GenericResponseValue::Text("1")),
291
            ("A", GenericResponseValue::Text("2")),
292
            ("A ", GenericResponseValue::Text("3")),
293
            ("b", GenericResponseValue::Text("4")),
294
        ]
295
        .into();
296

            
297
        let map: HashMap<_, _> = valid_maplike_attrs.into();
298
        assert_eq!(map.len(), 4);
299
    }
300

            
301
    #[test]
302
    #[cfg(debug_assertions)]
303
    #[should_panic]
304
    fn test_invalid_hashmap_uniqueness_assert() {
305
        let invalid_maplike_attrs: ResponseAttributes = vec![
306
            ("a", GenericResponseValue::Text("1")),
307
            ("b", GenericResponseValue::Text("2")),
308
            ("c", GenericResponseValue::Text("3")),
309
            ("a", GenericResponseValue::Text("4")),
310
        ]
311
        .into();
312

            
313
        let map: HashMap<_, _> = invalid_maplike_attrs.into();
314
        assert_eq!(map.len(), 4);
315
    }
316
}