1
//! Tokenizer for Mpd responses named [`ResponseAttributes`].
2
//!
3
//! This module also contains some helper macros that makes it easier to
4
//! create response parsers.
5

            
6
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
7
pub enum GenericResponseValue<'a> {
8
    Text(&'a str),
9
    Binary(&'a [u8]),
10
    // Many(Vec<GenericResponseValue<'a>>),
11
}
12

            
13
#[derive(Debug, Clone)]
14
pub struct ResponseAttributes<'a> {
15
    bytestring: &'a [u8],
16
    cursor: usize,
17
}
18

            
19
impl<'a> ResponseAttributes<'a> {
20
8
    pub fn new(raw: &'a str) -> Self {
21
8
        Self::new_from_bytes(raw.as_bytes())
22
8
    }
23

            
24
19
    pub fn new_from_bytes(bytes: &'a [u8]) -> Self {
25
19
        Self {
26
19
            bytestring: bytes,
27
19
            cursor: 0,
28
19
        }
29
19
    }
30

            
31
    pub fn is_empty(&self) -> bool {
32
        self.cursor >= self.bytestring.len() || self.bytestring[self.cursor..].starts_with(b"OK")
33
    }
34

            
35
7
    pub fn into_map(
36
7
        self,
37
7
    ) -> Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError> {
38
7
        self.into()
39
7
    }
40

            
41
11
    pub fn into_vec(self) -> Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError> {
42
11
        self.into()
43
11
    }
44

            
45
1
    pub fn into_lazy_vec(
46
1
        self,
47
1
    ) -> Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>> {
48
1
        self.into()
49
1
    }
50

            
51
    pub fn verify_all_keys_equal(&self, expected_key: &str) -> Result<(), ResponseParserError> {
52
        let mut copy = self.clone();
53
        copy.cursor = 0;
54
        for item in copy {
55
            let (key, _) = item?;
56
            if key != expected_key {
57
                return Err(ResponseParserError::UnexpectedProperty(key.to_string()));
58
            }
59
        }
60
        Ok(())
61
    }
62

            
63
    // pub fn get<'a>(&self, key: &str) -> Option<&GenericResponseValue<'a>> {
64
    //     self.0.iter().find_map(|(k, v)| if k == &key { Some(v) } else { None })
65
    // }
66
}
67

            
68
impl<'a> Iterator for ResponseAttributes<'a> {
69
    type Item = Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>;
70

            
71
159
    fn next(&mut self) -> Option<Self::Item> {
72
        loop {
73
160
            if self.cursor >= self.bytestring.len() {
74
1
                return Some(Err(ResponseParserError::UnexpectedEOF));
75
159
            }
76

            
77
159
            if self.bytestring[self.cursor..].starts_with(b"OK") {
78
18
                return None;
79
141
            }
80

            
81
141
            let remaining = &self.bytestring[self.cursor..];
82
141
            let newline_pos = remaining
83
141
                .iter()
84
2898
                .position(|&b| b == b'\n')
85
141
                .unwrap_or(remaining.len());
86

            
87
141
            let line = &remaining[..newline_pos];
88

            
89
            // NOTE: it is important that this happens before any further None returns,
90
            //       so that the iterator advances despite errors.
91
141
            self.cursor += newline_pos + 1;
92

            
93
141
            if line.is_empty() {
94
1
                continue;
95
140
            }
96

            
97
140
            return Some(parse_line(line, self));
98
        }
99
159
    }
100
}
101

            
102
140
fn parse_line<'a>(
103
140
    line: &'a [u8],
104
140
    state: &mut ResponseAttributes<'a>,
105
140
) -> Result<(&'a str, GenericResponseValue<'a>), ResponseParserError> {
106
1199
    let mut parts = line.splitn(2, |&b| b == b':');
107

            
108
140
    let key = parts
109
140
        .next()
110
140
        .filter(|k| !k.is_empty())
111
140
        .and_then(|k| std::str::from_utf8(k).ok())
112
140
        .ok_or_else(|| ResponseParserError::SyntaxError(0, "Invalid key".into()))?;
113

            
114
140
    match key {
115
140
        "binary" => parse_binary(parts.next(), state).map(|v| (key, v)),
116
        _ => {
117
139
            let value = parts.next().unwrap_or(b"").trim_ascii_start();
118

            
119
139
            let text = std::str::from_utf8(value)
120
139
                .map_err(|_| ResponseParserError::SyntaxError(0, "Invalid UTF-8".into()))?;
121

            
122
139
            Ok((key, GenericResponseValue::Text(text)))
123
        }
124
    }
125
140
}
126

            
127
1
fn parse_binary<'a>(
128
1
    count_bytes: Option<&[u8]>,
129
1
    state: &mut ResponseAttributes<'a>,
130
1
) -> Result<GenericResponseValue<'a>, ResponseParserError> {
131
    // In the case of binary data, the following value will be the byte count
132
    // in decimal, and the actual binary data will follow in the next N bytes,
133
    // followed by a newline.
134
    //
135
    // We parse the number and assign the binary data to the "binary" key.
136
1
    let count = count_bytes
137
1
        .map(|b| b.trim_ascii_start())
138
1
        .and_then(|b| std::str::from_utf8(b).ok())
139
1
        .and_then(|s| s.parse::<usize>().ok())
140
1
        .ok_or_else(|| ResponseParserError::SyntaxError(0, "Invalid byte count".into()))?;
141

            
142
1
    let start = state.cursor;
143
1
    let end = start + count;
144

            
145
1
    if end > state.bytestring.len() {
146
        return Err(ResponseParserError::UnexpectedEOF);
147
1
    }
148

            
149
1
    let bytes = &state.bytestring[start..end];
150

            
151
1
    if state.bytestring.get(end).is_some_and(|&b| b != b'\n') {
152
        return Err(ResponseParserError::SyntaxError(
153
            0,
154
            "Missing newline".into(),
155
        ));
156
1
    }
157

            
158
1
    state.cursor = end + 1;
159

            
160
1
    Ok(GenericResponseValue::Binary(bytes))
161
1
}
162

            
163
impl<'a> From<ResponseAttributes<'a>>
164
    for Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError>
165
{
166
7
    fn from(val: ResponseAttributes<'a>) -> Self {
167
7
        let mut map = HashMap::new();
168
28
        for item in val {
169
28
            let (k, v) = item?;
170
28
            if map.contains_key(k) {
171
                return Err(ResponseParserError::DuplicateProperty(k.to_string()));
172
28
            }
173
28
            map.insert(k, v);
174
        }
175
7
        Ok(map)
176
7
    }
177
}
178

            
179
impl<'a> From<ResponseAttributes<'a>>
180
    for Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>>
181
{
182
1
    fn from(val: ResponseAttributes<'a>) -> Self {
183
1
        val.collect()
184
1
    }
185
}
186

            
187
impl<'a> From<ResponseAttributes<'a>>
188
    for Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError>
189
{
190
11
    fn from(val: ResponseAttributes<'a>) -> Self {
191
11
        val.collect()
192
11
    }
193
}
194

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

            
199
/*******************/
200
/* Parsing Helpers */
201
/*******************/
202

            
203
macro_rules! _expect_property_type {
204
    ($property:expr, $name:expr, $variant:ident) => {
205
        match $property {
206
            Some(crate::response_tokenizer::GenericResponseValue::$variant(value)) => Some(value),
207
            Some(value) => {
208
                let actual_type = match value {
209
                    crate::response_tokenizer::GenericResponseValue::Text(_) => "Text",
210
                    crate::response_tokenizer::GenericResponseValue::Binary(_) => "Binary",
211
                };
212
                return Err(
213
                    crate::commands::ResponseParserError::UnexpectedPropertyType(
214
                        $name.to_string(),
215
                        actual_type.to_string(),
216
                    ),
217
                );
218
            }
219
            None => None,
220
        }
221
    };
222
}
223

            
224
macro_rules! _parse_optional_property_type {
225
    ($name:expr, $property:expr) => {
226
        $property
227
38
            .map(|value| {
228
38
                value.parse().map_err(|_| {
229
                    crate::commands::ResponseParserError::InvalidProperty(
230
                        $name.to_string(),
231
                        value.to_string(),
232
                    )
233
                })
234
38
            })
235
            .transpose()?
236
    };
237
}
238

            
239
macro_rules! _unwrap_optional_property_type {
240
    ($name:expr, $property:expr) => {
241
        match $property {
242
            Some(value) => value,
243
            None => {
244
                return Err(crate::commands::ResponseParserError::MissingProperty(
245
                    $name.to_string(),
246
                ))
247
            }
248
        }
249
    };
250
}
251

            
252
macro_rules! expect_optional_property_type {
253
    ($property:expr, $name:expr, $variant:ident) => {
254
        crate::response_tokenizer::_expect_property_type!($property, $name, $variant)
255
    };
256
}
257

            
258
macro_rules! expect_property_type {
259
    ($property:expr, $name:expr, $variant:ident) => {{
260
        let prop = crate::response_tokenizer::_expect_property_type!($property, $name, $variant);
261
        crate::response_tokenizer::_unwrap_optional_property_type!($name, prop)
262
    }};
263
}
264

            
265
macro_rules! get_optional_property {
266
    ($parts:expr, $name:literal, $variant:ident) => {
267
        crate::response_tokenizer::_expect_property_type!(
268
            { $parts.get($name).map(|v| *v) },
269
            $name,
270
            $variant
271
        )
272
    };
273
}
274

            
275
macro_rules! get_property {
276
    ($parts:expr, $name:literal, $variant:ident) => {{
277
        let prop = crate::response_tokenizer::_expect_property_type!(
278
            { $parts.get($name).map(|v| *v) },
279
            $name,
280
            $variant
281
        );
282
        crate::response_tokenizer::_unwrap_optional_property_type!($name, prop)
283
    }};
284
}
285

            
286
macro_rules! get_and_parse_optional_property {
287
    ($parts:ident, $name:literal, $variant:ident) => {{
288
        let prop = crate::response_tokenizer::_expect_property_type!(
289
            { $parts.get($name).map(|v| *v) },
290
            $name,
291
            $variant
292
        );
293
        crate::response_tokenizer::_parse_optional_property_type!($name, prop)
294
    }};
295
}
296

            
297
macro_rules! get_and_parse_property {
298
    ($parts:ident, $name:literal, $variant:ident) => {{
299
        let prop = crate::response_tokenizer::_expect_property_type!(
300
            { $parts.get($name).map(|v| *v) },
301
            $name,
302
            $variant
303
        );
304
        let prop = crate::response_tokenizer::_parse_optional_property_type!($name, prop);
305
        crate::response_tokenizer::_unwrap_optional_property_type!($name, prop)
306
    }};
307
}
308

            
309
macro_rules! get_next_optional_property {
310
    ($parts:ident, $variant:ident) => {
311
        match $parts.next() {
312
            Some((name, value)) => {
313
                crate::response_tokenizer::_expect_property_type!({ Some(value) }, name, $variant)
314
                    .map(|value| (name, value))
315
            }
316
            None => None,
317
        }
318
    };
319
}
320

            
321
macro_rules! get_next_property {
322
    ($parts:ident, $variant:ident) => {
323
        match $parts.next() {
324
            Some(Ok((name, value))) => (
325
                name,
326
                crate::response_tokenizer::_expect_property_type!({ Some(value) }, name, $variant)
327
                    .unwrap(),
328
            ),
329
            Some(Err(e)) => return Err(e),
330
            None => return Err(crate::commands::ResponseParserError::UnexpectedEOF),
331
        }
332
    };
333
}
334

            
335
macro_rules! get_next_and_parse_optional_property {
336
    ($parts:ident, $variant:ident) => {
337
        match $parts.next() {
338
            Some((name, value)) => {
339
                let prop = crate::response_tokenizer::_expect_property_type!(
340
                    { Some(value) },
341
                    name,
342
                    $variant
343
                );
344
                prop.map(|value| {
345
                    (
346
                        name,
347
                        crate::response_tokenizer::_parse_optional_property_type!(name, value),
348
                    )
349
                })
350
            }
351
            None => None,
352
        }
353
    };
354
}
355

            
356
macro_rules! get_next_and_parse_property {
357
    ($parts:ident, $variant:ident) => {
358
        match $parts.next() {
359
            Some(Ok((name, value))) => {
360
                let prop = crate::response_tokenizer::_expect_property_type!(
361
                    { Some(value) },
362
                    name,
363
                    $variant
364
                );
365
                let prop = crate::response_tokenizer::_parse_optional_property_type!(name, prop);
366
                (
367
                    name,
368
                    crate::response_tokenizer::_unwrap_optional_property_type!(name, prop),
369
                )
370
            }
371
            Some(Err(e)) => return Err(e),
372
            None => return Err(crate::commands::ResponseParserError::UnexpectedEOF),
373
        }
374
    };
375
}
376

            
377
use std::collections::HashMap;
378

            
379
pub(crate) use _expect_property_type;
380
pub(crate) use _parse_optional_property_type;
381
pub(crate) use _unwrap_optional_property_type;
382
pub(crate) use expect_property_type;
383
// pub(crate) use expect_optional_property_type;
384
pub(crate) use get_and_parse_optional_property;
385
pub(crate) use get_and_parse_property;
386
// pub(crate) use get_next_and_parse_optional_property;
387
pub(crate) use get_next_and_parse_property;
388
// pub(crate) use get_next_optional_property;
389
pub(crate) use get_next_property;
390
pub(crate) use get_optional_property;
391
pub(crate) use get_property;
392

            
393
use crate::commands::ResponseParserError;
394

            
395
#[cfg(test)]
396
mod tests {
397
    use indoc::indoc;
398

            
399
    use super::*;
400

            
401
    #[test]
402
    #[cfg(debug_assertions)]
403
    fn test_valid_hashmap_uniqueness_assert() -> Result<(), ResponseParserError> {
404
        let raw_response = indoc! {
405
            "a: 1
406
            A: 2
407
            A : 3
408
            b: 4
409
            OK"
410
        };
411
        let attrs = ResponseAttributes::new(raw_response);
412
        let map: HashMap<_, _> = attrs.into_map()?;
413
        assert_eq!(map.len(), 4);
414
        Ok(())
415
    }
416

            
417
    #[test]
418
    #[cfg(debug_assertions)]
419
    #[should_panic]
420
    fn test_invalid_hashmap_uniqueness_assert() {
421
        let raw_response = indoc! {
422
            "a: 1
423
            b: 2
424
            c: 3
425
            a: 4
426
            OK"
427
        };
428
        ResponseAttributes::new(raw_response).into_map().unwrap();
429
    }
430

            
431
    #[test]
432
1
    fn test_response_attributes_single_attribute() -> Result<(), ResponseParserError> {
433
1
        let raw_response = indoc! {
434
1
            "name: Sticker1
435
1
            OK"
436
        };
437
1
        let attrs = ResponseAttributes::new(raw_response);
438
1
        let map: HashMap<_, _> = attrs.into_map()?;
439
1
        assert_eq!(map.len(), 1);
440
1
        assert_eq!(
441
1
            map.get("name"),
442
            Some(&GenericResponseValue::Text("Sticker1"))
443
        );
444
1
        Ok(())
445
1
    }
446

            
447
    #[test]
448
1
    fn test_response_attributes_multiple_attributes() -> Result<(), ResponseParserError> {
449
1
        let raw_response = indoc! {
450
1
            "name: Sticker1
451
1
            type: emoji
452
1
            size: 128
453
1
            OK"
454
        };
455
1
        let attrs = ResponseAttributes::new(raw_response);
456
1
        let map: HashMap<_, _> = attrs.into_map()?;
457
1
        assert_eq!(map.len(), 3);
458
1
        assert_eq!(
459
1
            map.get("name"),
460
            Some(&GenericResponseValue::Text("Sticker1"))
461
        );
462
1
        assert_eq!(map.get("type"), Some(&GenericResponseValue::Text("emoji")));
463
1
        assert_eq!(map.get("size"), Some(&GenericResponseValue::Text("128")));
464
1
        Ok(())
465
1
    }
466

            
467
    #[test]
468
1
    fn test_response_attributes_empty_response() -> Result<(), ResponseParserError> {
469
1
        let raw_response = indoc! {
470
1
            "OK"
471
        };
472
1
        let attrs = ResponseAttributes::new(raw_response);
473
1
        let map: HashMap<_, _> = attrs.into_map()?;
474
1
        assert_eq!(map.len(), 0);
475
1
        Ok(())
476
1
    }
477

            
478
    #[test]
479
1
    fn test_response_attributes_unexpected_eof() -> Result<(), ResponseParserError> {
480
1
        let raw_response = indoc! {
481
1
            "name: Sticker1
482
1
            type: emoji"
483
        };
484
1
        let attrs = ResponseAttributes::new(raw_response);
485
1
        let vec: Result<Vec<_>, ResponseParserError> = attrs.into_vec();
486
1
        assert!(matches!(vec, Err(ResponseParserError::UnexpectedEOF)));
487
1
        Ok(())
488
1
    }
489

            
490
    #[test]
491
1
    fn test_response_attributes_repeated_attribute() -> Result<(), ResponseParserError> {
492
1
        let raw_response = indoc! {
493
1
            "name: Sticker1
494
1
            name: Sticker2
495
1
            OK"
496
        };
497
1
        let attrs = ResponseAttributes::new(raw_response);
498
1
        let vec = attrs.into_vec()?;
499
1
        assert_eq!(vec.len(), 2);
500
1
        assert_eq!(vec[0], ("name", GenericResponseValue::Text("Sticker1")));
501
1
        assert_eq!(vec[1], ("name", GenericResponseValue::Text("Sticker2")));
502
1
        Ok(())
503
1
    }
504

            
505
    #[test]
506
1
    fn test_response_attributes_empty_line() -> Result<(), ResponseParserError> {
507
1
        let raw_response = indoc! {
508
1
            "name: Sticker1
509
1

            
510
1
            type: emoji
511
1
            OK"
512
        };
513
1
        let attrs = ResponseAttributes::new(raw_response);
514
1
        let map: HashMap<_, _> = attrs.into_map()?;
515
1
        assert_eq!(map.len(), 2);
516
1
        assert_eq!(
517
1
            map.get("name"),
518
            Some(&GenericResponseValue::Text("Sticker1"))
519
        );
520
1
        assert_eq!(map.get("type"), Some(&GenericResponseValue::Text("emoji")));
521
1
        Ok(())
522
1
    }
523

            
524
    #[test]
525
1
    fn test_response_attributes_no_value() -> Result<(), ResponseParserError> {
526
1
        let raw_response = indoc! {
527
1
            "name:
528
1
            type: emoji
529
1
            OK"
530
        };
531
1
        let attrs = ResponseAttributes::new(raw_response);
532
1
        let map: HashMap<_, _> = attrs.into_map()?;
533
1
        assert_eq!(map.len(), 2);
534
1
        assert_eq!(map.get("name"), Some(&GenericResponseValue::Text("")));
535
1
        assert_eq!(map.get("type"), Some(&GenericResponseValue::Text("emoji")));
536
1
        Ok(())
537
1
    }
538

            
539
    #[test]
540
1
    fn test_response_attributes_binary_data() -> Result<(), ResponseParserError> {
541
1
        let bytestring: &[u8] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09";
542
1
        let raw_response = {
543
1
            let mut response = format!("binary: {}\n", bytestring.len()).into_bytes();
544
1
            response.extend_from_slice(bytestring);
545
1
            response.extend_from_slice(b"\nOK");
546
1
            response
547
        };
548

            
549
1
        let attrs = ResponseAttributes::new_from_bytes(&raw_response);
550
1
        let map: HashMap<_, _> = attrs.into_map().unwrap();
551
1
        assert_eq!(map.len(), 1);
552
1
        assert_eq!(
553
1
            map.get("binary"),
554
1
            Some(&GenericResponseValue::Binary(bytestring))
555
        );
556
1
        Ok(())
557
1
    }
558
}