1
//! The basic building blocks, definitions and helpers used to define an Mpd command.
2
//!
3
//! An mpd command consists of a pair of serializers and parsers for both the request
4
//! and the corresponding response, as well as the command name used to identify the command.
5
//!
6
//! Each command is modelled as a struct implementing the [`Command`] trait,
7
//! which in turn uses the [`CommandRequest`] and [`CommandResponse`] traits
8
//! to define the request and response types respectively.
9

            
10
use crate::{request_tokenizer::RequestTokenizer, response_tokenizer::ResponseAttributes};
11

            
12
mod audio_output_devices;
13
mod client_to_client;
14
mod connection_settings;
15
mod controlling_playback;
16
mod mounts_and_neighbors;
17
mod music_database;
18
mod partition_commands;
19
mod playback_options;
20
mod querying_mpd_status;
21
mod queue;
22
mod reflection;
23
mod stickers;
24
mod stored_playlists;
25

            
26
pub use audio_output_devices::*;
27
pub use client_to_client::*;
28
pub use connection_settings::*;
29
pub use controlling_playback::*;
30
pub use mounts_and_neighbors::*;
31
pub use music_database::*;
32
pub use partition_commands::*;
33
pub use playback_options::*;
34
pub use querying_mpd_status::*;
35
pub use queue::*;
36
pub use reflection::*;
37
use serde::{Deserialize, Serialize};
38
pub use stickers::*;
39
pub use stored_playlists::*;
40

            
41
#[cfg(feature = "futures")]
42
use futures_util::{
43
    AsyncBufReadExt,
44
    io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader},
45
};
46

            
47
use thiserror::Error;
48
#[cfg(feature = "tokio")]
49
use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader};
50

            
51
/// A trait modelling a single MPD command request.
52
pub(crate) trait CommandRequest
53
where
54
    Self: Sized,
55
{
56
    /// The command name used within the protocol
57
    const COMMAND: &'static str;
58

            
59
    // TODO: add these for ease of throwing parsing errors
60
    /// The minimum number of arguments this command takes
61
    const MIN_ARGS: u32;
62

            
63
    /// The maximum number of arguments this command takes.
64
    ///
65
    /// Note that in the case of keyworded arguments, such as
66
    /// `group <groupname>`, `sort <sorting>`, etc., these are
67
    /// counted as a single argument despite being two tokens.
68
    const MAX_ARGS: Option<u32>;
69

            
70
    /// Helper function to create a [`RequestParserError::TooManyArguments`] error
71
    fn too_many_arguments_error(found: u32) -> RequestParserError {
72
        RequestParserError::TooManyArguments {
73
            expected_min: Self::MIN_ARGS,
74
            expected_max: Self::MAX_ARGS,
75
            found,
76
        }
77
    }
78

            
79
    /// Helper function to throw a [`RequestParserError::TooManyArguments`] error
80
    fn throw_if_too_many_arguments(parts: RequestTokenizer<'_>) -> Result<(), RequestParserError> {
81
        let remaining_args = parts.count().try_into().unwrap_or(u32::MAX);
82
        if remaining_args != 0 {
83
            return Err(Self::too_many_arguments_error(
84
                remaining_args.saturating_add(Self::MAX_ARGS.unwrap()),
85
            ));
86
        }
87
        Ok(())
88
    }
89

            
90
    /// Helper function to create a [`RequestParserError::MissingArguments`] error
91
    fn missing_arguments_error(found: u32) -> RequestParserError {
92
        RequestParserError::MissingArguments {
93
            expected_min: Self::MIN_ARGS,
94
            expected_max: Self::MAX_ARGS,
95
            found,
96
        }
97
    }
98

            
99
    /// Converts this specific request type to it's corresponding variant in the generic Request enum.
100
    fn into_request_enum(self) -> crate::Request;
101

            
102
    /// Converts from the generic Request enum to this specific request type.
103
    ///
104
    /// If the enum variant does not match this type, returns None.
105
    fn from_request_enum(request: crate::Request) -> Option<Self>;
106

            
107
    /// Serializes the request into a String.
108
    fn serialize(&self) -> String;
109

            
110
    /// Parses the request from its tokenized parts.
111
    /// See also [`parse_raw`].
112
    fn parse(parts: RequestTokenizer<'_>) -> Result<Self, RequestParserError>;
113

            
114
    /// Parses the request from its raw string representation.
115
    ///
116
    /// This assumes the raw string starts with the command name, e.g.
117
    /// `command_name arg1 "arg2 arg3"`
118
    fn parse_raw(raw: &str) -> Result<Self, RequestParserError> {
119
        let (line, _rest) = raw
120
            .split_once('\n')
121
            .ok_or(RequestParserError::MissingNewline)?;
122

            
123
        if line.is_empty() {
124
            return Err(RequestParserError::EmptyLine);
125
        }
126

            
127
        let mut tokenized = RequestTokenizer::new(line);
128

            
129
        let command_name_token_length = Self::COMMAND.split_ascii_whitespace().count();
130
        let mut command_name = Vec::with_capacity(command_name_token_length);
131
        for _ in 0..command_name_token_length {
132
            let token = tokenized
133
                .next()
134
                .ok_or(RequestParserError::SyntaxError(0, line.to_string()))?;
135
            command_name.push(token);
136
        }
137
        let command_name = command_name.join(" ");
138

            
139
        if command_name != Self::COMMAND {
140
            return Err(RequestParserError::SyntaxError(0, line.to_string()));
141
        }
142

            
143
        Self::parse(tokenized)
144
    }
145
}
146

            
147
/// A trait modelling a single MPD command response.
148
pub(crate) trait CommandResponse
149
where
150
    Self: Sized,
151
{
152
    /// Converts this specific response type to it's corresponding variant in the generic Response enum.
153
    fn into_response_enum(self) -> crate::Response;
154

            
155
    /// Converts from the generic Response enum to this specific response type.
156
    ///
157
    /// If the enum variant does not match this type, returns None.
158
    fn from_response_enum(response: crate::Response) -> Option<Self>;
159

            
160
    // /// Serializes the response into a Vec<u8>.
161
    // fn serialize(&self) -> Vec<u8>;
162

            
163
    /// Parses the response from its tokenized parts.
164
    /// See also [`parse_raw`].
165
    fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError>;
166

            
167
    /// Parses the response from its raw byte representation.
168
10
    fn parse_raw(raw: &[u8]) -> Result<Self, ResponseParserError> {
169
10
        Self::parse(ResponseAttributes::new_from_bytes(raw))
170
10
    }
171
}
172

            
173
/// A trait modelling the request/response pair of a single MPD command.
174
pub trait Command {
175
    /// The request sent from the client to the server
176
    type Request: CommandRequest;
177
    /// The response sent from the server to the client
178
    type Response: CommandResponse;
179

            
180
    /// The command name used within the protocol
181
    const COMMAND: &'static str = Self::Request::COMMAND;
182

            
183
    /// Serialize the request into a string.
184
    /// This should optimally produce an input that can be parsed by [`parse_request`]
185
    fn serialize_request(&self, request: Self::Request) -> String {
186
        request.serialize().to_owned()
187
    }
188
    /// Serialize the request into a bytestring.
189
    fn serialize_request_to_bytes(&self, request: Self::Request) -> Vec<u8> {
190
        self.serialize_request(request).into_bytes()
191
    }
192

            
193
    /// Parse the request from its tokenized parts. See also [`parse_raw_request`].
194
    fn parse_request(parts: RequestTokenizer) -> Result<Self::Request, RequestParserError> {
195
        Self::Request::parse(parts)
196
    }
197

            
198
    /// Parse the raw request string into a request.
199
    /// This assumes the raw string starts with the command name, e.g. `command_name arg1 "arg2 arg3"`
200
    fn parse_raw_request(raw: &str) -> Result<Self::Request, RequestParserError> {
201
        Self::Request::parse_raw(raw)
202
    }
203

            
204
    // /// Serialize the response into a string.
205
    // fn serialize_response(&self, response: Self::Response) -> String {
206

            
207
    /// Parse the response from its tokenized parts. See also [`parse_raw_response`].
208
    fn parse_response(parts: ResponseAttributes) -> Result<Self::Response, ResponseParserError> {
209
        Self::Response::parse(parts)
210
    }
211
    /// Parse the raw response string into a response.
212
10
    fn parse_raw_response(raw: &[u8]) -> Result<Self::Response, ResponseParserError> {
213
10
        Self::Response::parse_raw(raw)
214
10
    }
215

            
216
    async fn execute<T>(
217
        request: Self::Request,
218
        connection: &mut T,
219
    ) -> Result<Self::Response, crate::MpdClientError>
220
    where
221
        Self: Sized,
222
        T: AsyncWrite + AsyncRead + Unpin,
223
    {
224
        let payload = request.serialize();
225

            
226
        connection
227
            .write_all(payload.as_bytes())
228
            .await
229
            .map_err(crate::MpdClientError::ConnectionError)?;
230

            
231
        connection
232
            .flush()
233
            .await
234
            .map_err(crate::MpdClientError::ConnectionError)?;
235

            
236
        let mut response_bytes = Vec::new();
237
        let mut reader = BufReader::new(connection);
238

            
239
        loop {
240
            let mut line = Vec::new();
241

            
242
            let bytes_read = reader
243
                .read_until(b'\n', &mut line)
244
                .await
245
                .map_err(crate::MpdClientError::ConnectionError)?;
246

            
247
            if bytes_read == 0 {
248
                break; // EOF reached
249
            }
250

            
251
            response_bytes.extend_from_slice(&line);
252

            
253
            // TODO: handle errors properly
254
            if line == b"OK\n" || line.starts_with(b"ACK ") {
255
                break; // End of response
256
            }
257
        }
258

            
259
        let response = Self::parse_raw_response(&response_bytes)
260
            .map_err(crate::MpdClientError::ResponseParseError)?;
261

            
262
        Ok(response)
263
    }
264
}
265

            
266
// Request/response implementation helpers
267

            
268
macro_rules! empty_command_request {
269
    ($name:ident, $command_name:expr) => {
270
        paste::paste! {
271
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
272
            pub struct [<$name Request>];
273
        }
274

            
275
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
276
            const COMMAND: &'static str = $command_name;
277
            const MIN_ARGS: u32 = 0;
278
            const MAX_ARGS: Option<u32> = Some(0);
279

            
280
            fn into_request_enum(self) -> crate::Request {
281
                match Self::COMMAND {
282
                    $command_name => crate::Request::$name,
283
                    _ => unimplemented!(),
284
                }
285
            }
286

            
287
            fn from_request_enum(request: crate::Request) -> Option<Self> {
288
                match (Self::COMMAND, request) {
289
                    ($command_name, crate::Request::$name) => {
290
                        Some(paste::paste! { [<$name Request>] })
291
                    }
292
                    _ => None,
293
                }
294
            }
295

            
296
            fn serialize(&self) -> String {
297
                Self::COMMAND.to_string() + "\n"
298
            }
299

            
300
            fn parse(
301
                parts: crate::commands::RequestTokenizer<'_>,
302
            ) -> Result<Self, crate::commands::RequestParserError> {
303
                Self::throw_if_too_many_arguments(parts)?;
304

            
305
                Ok(paste::paste! { [<$name Request>] })
306
            }
307
        }
308
    };
309
}
310

            
311
macro_rules! empty_command_response {
312
    ($name:ident) => {
313
        paste::paste! {
314
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
315
            pub struct [<$name Response>];
316
        }
317

            
318
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
319
            fn into_response_enum(self) -> crate::Response {
320
                todo!()
321
            }
322

            
323
            fn from_response_enum(_response: crate::Response) -> Option<Self> {
324
                todo!()
325
            }
326

            
327
            fn parse(
328
                _parts: crate::commands::ResponseAttributes<'_>,
329
            ) -> Result<Self, crate::commands::ResponseParserError> {
330
                debug_assert!(_parts.is_empty());
331
                Ok(paste::paste! { [<$name Response>] })
332
            }
333
        }
334
    };
335
}
336

            
337
macro_rules! single_item_command_request {
338
    ($name:ident, $command_name:expr, $item_type:ty) => {
339
        paste::paste! {
340
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
341
            pub struct [<$name Request>] ($item_type);
342
        }
343

            
344
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
345
            const COMMAND: &'static str = $command_name;
346
            const MIN_ARGS: u32 = 1;
347
            const MAX_ARGS: Option<u32> = Some(1);
348

            
349
            fn into_request_enum(self) -> crate::Request {
350
                match Self::COMMAND {
351
                    $command_name => crate::Request::$name(self.0),
352
                    _ => unimplemented!(),
353
                }
354
            }
355

            
356
            fn from_request_enum(request: crate::Request) -> Option<Self> {
357
                match (Self::COMMAND, request) {
358
                    ($command_name, crate::Request::$name(item)) => {
359
                        Some(paste::paste! { [<$name Request>] ( item ) })
360
                    }
361
                    _ => None,
362
                }
363
            }
364

            
365
            fn serialize(&self) -> String {
366
                format!("{} {}\n", Self::COMMAND, self.0)
367
            }
368

            
369
            fn parse(
370
                mut parts: crate::commands::RequestTokenizer<'_>,
371
            ) -> Result<Self, crate::commands::RequestParserError> {
372
                let item_token = parts.next().ok_or(Self::missing_arguments_error(0))?;
373

            
374
                let item = item_token.parse::<$item_type>().map_err(|_| {
375
                    crate::commands::RequestParserError::SubtypeParserError {
376
                        argument_index: 1,
377
                        expected_type: stringify!($item_type),
378
                        raw_input: item_token.to_owned(),
379
                    }
380
                })?;
381

            
382
                Self::throw_if_too_many_arguments(parts)?;
383

            
384
                Ok(paste::paste! { [<$name Request>] ( item ) })
385
            }
386
        }
387
    };
388
}
389

            
390
macro_rules! single_optional_item_command_request {
391
    ($name:ident, $command_name:expr, $item_type:ty) => {
392
        paste::paste! {
393
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
394
            pub struct [<$name Request>] (Option<$item_type>);
395
        }
396

            
397
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
398
            const COMMAND: &'static str = $command_name;
399
            const MIN_ARGS: u32 = 0;
400
            const MAX_ARGS: Option<u32> = Some(1);
401

            
402
            fn into_request_enum(self) -> crate::Request {
403
                match Self::COMMAND {
404
                    $command_name => crate::Request::$name(self.0),
405
                    _ => unimplemented!(),
406
                }
407
            }
408

            
409
            fn from_request_enum(request: crate::Request) -> Option<Self> {
410
                match (Self::COMMAND, request) {
411
                    ($command_name, crate::Request::$name(item)) => {
412
                        Some(paste::paste! { [<$name Request>] ( item ) })
413
                    }
414
                    _ => None,
415
                }
416
            }
417

            
418
            fn serialize(&self) -> String {
419
                match &self.0 {
420
                    Some(item) => format!("{} {}\n", Self::COMMAND, item),
421
                    None => Self::COMMAND.to_string() + "\n",
422
                }
423
            }
424

            
425
            fn parse(
426
                mut parts: crate::commands::RequestTokenizer<'_>,
427
            ) -> Result<Self, crate::commands::RequestParserError> {
428
                let item = parts
429
                    .next()
430
                    .map(|s| {
431
                        s.parse().map_err(|_| {
432
                            crate::commands::RequestParserError::SubtypeParserError {
433
                                argument_index: 1,
434
                                expected_type: stringify!($item_type),
435
                                raw_input: s.to_owned(),
436
                            }
437
                        })
438
                    })
439
                    .transpose()?;
440

            
441
                Self::throw_if_too_many_arguments(parts)?;
442

            
443
                Ok(paste::paste! { [<$name Request>] ( item ) })
444
            }
445
        }
446
    };
447
}
448

            
449
macro_rules! single_item_command_response {
450
    ($name:ident, $item_name:expr, $item_type:ty) => {
451
        paste::paste! {
452
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
453
            pub struct [<$name Response>] ( $item_type );
454
        }
455

            
456
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
457
            fn into_response_enum(self) -> crate::Response {
458
                todo!()
459
            }
460

            
461
            fn from_response_enum(_response: crate::Response) -> Option<Self> {
462
                todo!()
463
            }
464

            
465
            fn parse(
466
                parts: crate::commands::ResponseAttributes<'_>,
467
            ) -> Result<Self, crate::commands::ResponseParserError> {
468
                let map = parts.into_map()?;
469

            
470
                debug_assert!(map.len() == 1, "Expected only one property in response");
471

            
472
                let item_token = map.get($item_name).ok_or(
473
                    crate::commands::ResponseParserError::MissingProperty($item_name.to_string()),
474
                )?;
475
                let item_ = crate::response_tokenizer::expect_property_type!(
476
                    Some(item_token),
477
                    $item_name,
478
                    Text
479
                );
480
                let item = item_.parse::<$item_type>().map_err(|_| {
481
                    crate::commands::ResponseParserError::InvalidProperty(
482
                        $item_name.to_string(),
483
                        item_.to_string(),
484
                    )
485
                })?;
486

            
487
                Ok(paste::paste! { [<$name Response>] ( item ) })
488
            }
489
        }
490
    };
491
}
492

            
493
macro_rules! multi_item_command_response {
494
    ($name:ident, $item_name:expr, $item_type:ty) => {
495
        paste::paste! {
496
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
497
            pub struct [<$name Response>] ( Vec<$item_type> );
498
        }
499

            
500
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
501
            fn into_response_enum(self) -> crate::Response {
502
                todo!()
503
            }
504

            
505
            fn from_response_enum(_response: crate::Response) -> Option<Self> {
506
                todo!()
507
            }
508

            
509
1
            fn parse(
510
1
                parts: crate::commands::ResponseAttributes<'_>,
511
1
            ) -> Result<Self, crate::commands::ResponseParserError> {
512
                // TODO: use lazy vec
513
1
                let parts_: Vec<_> = parts.into_vec()?;
514

            
515
2
                if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != $item_name) {
516
                    return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
517
1
                }
518

            
519
1
                let mut items = Vec::with_capacity(parts_.len());
520

            
521
1
                let mut iter = parts_.into_iter();
522
3
                while let Some(value) = iter.next() {
523
2
                    let unwrapped_value = expect_property_type!(Some(value.1), $item_name, Text);
524
2
                    let parsed_value = unwrapped_value.parse::<$item_type>().map_err(|_| {
525
                        crate::commands::ResponseParserError::InvalidProperty(
526
                            $item_name.to_string(),
527
                            unwrapped_value.to_string(),
528
                        )
529
                    })?;
530

            
531
2
                    items.push(parsed_value);
532
                }
533

            
534
1
                Ok(paste::paste! { [<$name Response>] ( items ) })
535
1
            }
536
        }
537
    };
538
}
539

            
540
pub(crate) use empty_command_request;
541
pub(crate) use empty_command_response;
542
pub(crate) use multi_item_command_response;
543
pub(crate) use single_item_command_request;
544
pub(crate) use single_item_command_response;
545
pub(crate) use single_optional_item_command_request;
546

            
547
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
548
pub enum RequestParserError {
549
    #[error("Found empty line while parsing request")]
550
    EmptyLine,
551

            
552
    // TODO: remove this, replaced by various other errors
553
    #[error("Could not parse the request due to a syntax error at position {0}: {1}")]
554
    SyntaxError(u64, String),
555

            
556
    // TODO: can we store the parser error as well?
557
    #[error(
558
        "Could not parse argument {argument_index} of the request (expected type: {expected_type}, raw input: '{raw_input}')"
559
    )]
560
    SubtypeParserError {
561
        /// The index of the argument that failed to parse
562
        ///
563
        /// Note that in the case of keyworded arguments, such as
564
        /// `group <groupname>`, `sort <sorting>`, etc., these are
565
        /// counted as a single argument despite being two tokens.
566
        argument_index: u32,
567

            
568
        /// The expected type of the argument
569
        expected_type: &'static str,
570

            
571
        /// The raw input that failed to parse
572
        raw_input: String,
573
    },
574

            
575
    #[error(
576
        "Too many arguments were provided in the request (expected between {expected_min} and {expected_max:?}, found {found})"
577
    )]
578
    TooManyArguments {
579
        /// The minimum number of arguments that were expected
580
        expected_min: u32,
581

            
582
        /// The maximum number of arguments that were expected
583
        ///
584
        /// This is `None` if the amount of arguments is unbounded.
585
        expected_max: Option<u32>,
586

            
587
        /// The number of arguments that were found
588
        ///
589
        /// Note that in the case of keyworded arguments, such as
590
        /// `group <groupname>`, `sort <sorting>`, etc., these are
591
        /// counted as a single argument despite being two tokens.
592
        found: u32,
593
    },
594

            
595
    #[error(
596
        "Not enough arguments were provided in the request (expected between {expected_min} and {expected_max:?}, found {found})"
597
    )]
598
    MissingArguments {
599
        /// The minimum number of arguments that were expected
600
        expected_min: u32,
601

            
602
        /// The maximum number of arguments that were expected.
603
        ///
604
        /// This is `None` if the amount of arguments is unbounded.
605
        expected_max: Option<u32>,
606

            
607
        /// The number of arguments that were found
608
        ///
609
        /// Note that in the case of keyworded arguments, such as
610
        /// `group <groupname>`, `sort <sorting>`, etc., these are
611
        /// counted as a single argument despite being two tokens.
612
        found: u32,
613
    },
614

            
615
    #[error("Keyword argument {keyword} at position {argument_index} is missing its value")]
616
    MissingKeywordValue {
617
        /// The unexpected keyword that was found
618
        keyword: &'static str,
619

            
620
        /// The index of the argument that was missing it's value
621
        ///
622
        /// Note that in the case of keyworded arguments, such as
623
        /// `group <groupname>`, `sort <sorting>`, etc., these are
624
        /// counted as a single argument despite being two tokens.
625
        argument_index: u32,
626
    },
627

            
628
    #[error("A command list was expected to be closed, but the end was not found")]
629
    MissingCommandListEnd,
630

            
631
    #[error("A command list was found inside another command list at line {line}")]
632
    NestedCommandList {
633
        /// The line where the nested command list was found
634
        line: u32,
635
    },
636

            
637
    #[error("An unexpected command list end was found")]
638
    UnexpectedCommandListEnd,
639

            
640
    // TODO: remove this, replaced by EmptyLine + MissingArguments
641
    #[error("Request ended early, while more arguments were expected")]
642
    UnexpectedEOF,
643

            
644
    #[error("Request is missing terminating newline")]
645
    MissingNewline,
646
}
647

            
648
// TODO: should these be renamed to fit the mpd docs?
649
//       "Attribute" instead of "Property"?
650
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
651
pub enum ResponseParserError {
652
    #[error("A property was expected to be present in the response, but was not found: {0}")]
653
    MissingProperty(String),
654

            
655
    // TODO: change name to UnexpectedPropertyEncoding
656
    #[error(
657
        "An expected property was found in the response, but its encoding was not as expected: {0}: {1}"
658
    )]
659
    UnexpectedPropertyType(String, String),
660

            
661
    #[error("A property was found in the response that was not expected: {0}")]
662
    UnexpectedProperty(String),
663

            
664
    #[error("A property was found multiple times in the response, but was only expected once: {0}")]
665
    DuplicateProperty(String),
666

            
667
    #[error("The property value is parsable, but the value is invalid or nonsensical: {0}: {1}")]
668
    InvalidProperty(String, String),
669

            
670
    #[error("Could not parse the response due to a syntax error at position {0}: {1}")]
671
    SyntaxError(u64, String),
672

            
673
    #[error("Response ended early, while more properties were expected")]
674
    UnexpectedEOF,
675
    // #[error("Response is missing terminating newline")]
676
    // MissingNewline,
677
}
678

            
679
/*******************/
680

            
681
pub const COMMAND_NAMES: &[&str] = &[
682
    // Audio output devices
683
    DisableOutput::COMMAND,
684
    EnableOutput::COMMAND,
685
    Outputs::COMMAND,
686
    OutputSet::COMMAND,
687
    ToggleOutput::COMMAND,
688
    // Client to client
689
    Channels::COMMAND,
690
    ReadMessages::COMMAND,
691
    SendMessage::COMMAND,
692
    Subscribe::COMMAND,
693
    Unsubscribe::COMMAND,
694
    // Connection settings
695
    BinaryLimit::COMMAND,
696
    Close::COMMAND,
697
    Kill::COMMAND,
698
    Password::COMMAND,
699
    Ping::COMMAND,
700
    Protocol::COMMAND,
701
    ProtocolAll::COMMAND,
702
    ProtocolAvailable::COMMAND,
703
    ProtocolClear::COMMAND,
704
    ProtocolDisable::COMMAND,
705
    ProtocolEnable::COMMAND,
706
    TagTypes::COMMAND,
707
    TagTypesAll::COMMAND,
708
    TagTypesAvailable::COMMAND,
709
    TagTypesClear::COMMAND,
710
    TagTypesDisable::COMMAND,
711
    TagTypesEnable::COMMAND,
712
    TagTypesReset::COMMAND,
713
    // Controlling playback
714
    Next::COMMAND,
715
    Pause::COMMAND,
716
    Play::COMMAND,
717
    PlayId::COMMAND,
718
    Previous::COMMAND,
719
    Seek::COMMAND,
720
    SeekCur::COMMAND,
721
    SeekId::COMMAND,
722
    Stop::COMMAND,
723
    // Mounts and neighbors
724
    ListMounts::COMMAND,
725
    ListNeighbors::COMMAND,
726
    Mount::COMMAND,
727
    Unmount::COMMAND,
728
    // Music database
729
    AlbumArt::COMMAND,
730
    Count::COMMAND,
731
    Find::COMMAND,
732
    FindAdd::COMMAND,
733
    GetFingerprint::COMMAND,
734
    List::COMMAND,
735
    ListAll::COMMAND,
736
    ListAllInfo::COMMAND,
737
    ListFiles::COMMAND,
738
    LsInfo::COMMAND,
739
    ReadComments::COMMAND,
740
    ReadPicture::COMMAND,
741
    Rescan::COMMAND,
742
    Search::COMMAND,
743
    SearchAdd::COMMAND,
744
    SearchAddPl::COMMAND,
745
    SearchCount::COMMAND,
746
    Update::COMMAND,
747
    // Partition commands
748
    DelPartition::COMMAND,
749
    ListPartitions::COMMAND,
750
    MoveOutput::COMMAND,
751
    NewPartition::COMMAND,
752
    Partition::COMMAND,
753
    // Playback options
754
    Consume::COMMAND,
755
    Crossfade::COMMAND,
756
    GetVol::COMMAND,
757
    MixRampDb::COMMAND,
758
    MixRampDelay::COMMAND,
759
    Random::COMMAND,
760
    Repeat::COMMAND,
761
    ReplayGainMode::COMMAND,
762
    ReplayGainStatus::COMMAND,
763
    SetVol::COMMAND,
764
    Single::COMMAND,
765
    Volume::COMMAND,
766
    // Querying mpd status
767
    ClearError::COMMAND,
768
    CurrentSong::COMMAND,
769
    Idle::COMMAND,
770
    Stats::COMMAND,
771
    Status::COMMAND,
772
    // Queue
773
    Add::COMMAND,
774
    AddId::COMMAND,
775
    AddTagId::COMMAND,
776
    Clear::COMMAND,
777
    ClearTagId::COMMAND,
778
    Delete::COMMAND,
779
    DeleteId::COMMAND,
780
    Move::COMMAND,
781
    MoveId::COMMAND,
782
    Playlist::COMMAND,
783
    PlaylistFind::COMMAND,
784
    PlaylistId::COMMAND,
785
    PlaylistInfo::COMMAND,
786
    PlaylistSearch::COMMAND,
787
    PlChanges::COMMAND,
788
    PlChangesPosId::COMMAND,
789
    Prio::COMMAND,
790
    PrioId::COMMAND,
791
    RangeId::COMMAND,
792
    Shuffle::COMMAND,
793
    Swap::COMMAND,
794
    SwapId::COMMAND,
795
    // Reflection
796
    Commands::COMMAND,
797
    Config::COMMAND,
798
    Decoders::COMMAND,
799
    NotCommands::COMMAND,
800
    UrlHandlers::COMMAND,
801
    // Stickers
802
    StickerDec::COMMAND,
803
    StickerDelete::COMMAND,
804
    StickerFind::COMMAND,
805
    StickerGet::COMMAND,
806
    StickerInc::COMMAND,
807
    StickerList::COMMAND,
808
    StickerSet::COMMAND,
809
    StickerNames::COMMAND,
810
    StickerNamesTypes::COMMAND,
811
    StickerTypes::COMMAND,
812
    // Stored playlists
813
    ListPlaylist::COMMAND,
814
    ListPlaylistInfo::COMMAND,
815
    ListPlaylists::COMMAND,
816
    Load::COMMAND,
817
    PlaylistAdd::COMMAND,
818
    PlaylistClear::COMMAND,
819
    PlaylistDelete::COMMAND,
820
    PlaylistLength::COMMAND,
821
    PlaylistMove::COMMAND,
822
    Rename::COMMAND,
823
    Rm::COMMAND,
824
    Save::COMMAND,
825
    SearchPlaylist::COMMAND,
826
];