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
use crate::{request_tokenizer::RequestTokenizer, response_tokenizer::ResponseAttributes};
7

            
8
use serde::{Deserialize, Serialize};
9
use thiserror::Error;
10

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

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

            
39
/// A trait modelling a single MPD command request.
40
pub trait CommandRequest
41
where
42
    Self: Sized,
43
{
44
    /// The response type produced by the server when this request is executed.
45
    type Response: CommandResponse;
46

            
47
    /// The command name used within the protocol
48
    const COMMAND: &'static str;
49

            
50
    // TODO: add these for ease of throwing parsing errors
51
    /// The minimum number of arguments this command takes
52
    const MIN_ARGS: u32;
53

            
54
    /// The maximum number of arguments this command takes.
55
    ///
56
    /// Note that in the case of keyworded arguments, such as
57
    /// `group <groupname>`, `sort <sorting>`, etc., these are
58
    /// counted as a single argument despite being two tokens.
59
    const MAX_ARGS: Option<u32>;
60

            
61
    /// Helper function to create a [`RequestParserError::TooManyArguments`] error
62
    fn too_many_arguments_error(found: u32) -> RequestParserError {
63
        RequestParserError::TooManyArguments {
64
            expected_min: Self::MIN_ARGS,
65
            expected_max: Self::MAX_ARGS,
66
            found,
67
        }
68
    }
69

            
70
    /// Helper function to throw a [`RequestParserError::TooManyArguments`] error
71
    fn throw_if_too_many_arguments(parts: RequestTokenizer<'_>) -> Result<(), RequestParserError> {
72
        let remaining_args = parts.count().try_into().unwrap_or(u32::MAX);
73
        if remaining_args != 0 {
74
            return Err(Self::too_many_arguments_error(
75
                remaining_args.saturating_add(Self::MAX_ARGS.unwrap()),
76
            ));
77
        }
78
        Ok(())
79
    }
80

            
81
    /// Helper function to create a [`RequestParserError::MissingArguments`] error
82
    fn missing_arguments_error(found: u32) -> RequestParserError {
83
        RequestParserError::MissingArguments {
84
            expected_min: Self::MIN_ARGS,
85
            expected_max: Self::MAX_ARGS,
86
            found,
87
        }
88
    }
89

            
90
    /// Serializes the request into a String.
91
    fn serialize(&self) -> String;
92

            
93
    /// Parses the request from its tokenized parts.
94
    /// See also [`parse_raw`].
95
    fn parse(parts: RequestTokenizer<'_>) -> Result<Self, RequestParserError>;
96

            
97
    /// Parses the request from its raw string representation.
98
    ///
99
    /// This assumes the raw string starts with the command name, e.g.
100
    /// `command_name arg1 "arg2 arg3"`
101
    fn parse_raw(raw: &str) -> Result<Self, RequestParserError> {
102
        let (line, _rest) = raw
103
            .split_once('\n')
104
            .ok_or(RequestParserError::MissingNewline)?;
105

            
106
        if line.is_empty() {
107
            return Err(RequestParserError::EmptyLine);
108
        }
109

            
110
        let mut tokenized = RequestTokenizer::new(line);
111

            
112
        let command_name_token_length = Self::COMMAND.split_ascii_whitespace().count();
113
        let mut command_name = Vec::with_capacity(command_name_token_length);
114
        for _ in 0..command_name_token_length {
115
            let token = tokenized
116
                .next()
117
                .ok_or(RequestParserError::SyntaxError(0, line.to_string()))?;
118
            command_name.push(token);
119
        }
120
        let command_name = command_name.join(" ");
121

            
122
        if command_name != Self::COMMAND {
123
            return Err(RequestParserError::SyntaxError(0, line.to_string()));
124
        }
125

            
126
        Self::parse(tokenized)
127
    }
128
}
129

            
130
/// A trait modelling a single MPD command response.
131
pub trait CommandResponse
132
where
133
    Self: Sized,
134
{
135
    /// The request type that provokes this response.
136
    type Request: CommandRequest<Response = Self>;
137

            
138
    /// Parses the response from its tokenized parts.
139
    /// See also [`parse_raw`].
140
    fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError>;
141

            
142
    /// Parses the response from its raw byte representation.
143
10
    fn parse_raw(raw: &[u8]) -> Result<Self, ResponseParserError> {
144
10
        Self::parse(ResponseAttributes::new_from_bytes(raw))
145
10
    }
146

            
147
    /// Serializes the response to its raw byte representaion.
148
    ///
149
    /// Note that this does not include the trailing `OK\n` terminator.
150
    fn serialize(&self) -> Vec<u8>;
151
}
152

            
153
// Request/response implementation helpers
154

            
155
macro_rules! empty_command_request {
156
    ($name:ident, $command_name:expr) => {
157
        paste::paste! {
158
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
159
            pub struct [<$name Request>];
160
        }
161

            
162
        impl std::default::Default for paste::paste! { [<$name Request>] } {
163
            fn default() -> Self {
164
                paste::paste! { [<$name Request>] }
165
            }
166
        }
167

            
168
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
169
            type Response = paste::paste! { [<$name Response>] };
170

            
171
            const COMMAND: &'static str = $command_name;
172
            const MIN_ARGS: u32 = 0;
173
            const MAX_ARGS: Option<u32> = Some(0);
174

            
175
            fn serialize(&self) -> String {
176
                Self::COMMAND.to_string() + "\n"
177
            }
178

            
179
            fn parse(
180
                parts: crate::commands::RequestTokenizer<'_>,
181
            ) -> Result<Self, crate::commands::RequestParserError> {
182
                Self::throw_if_too_many_arguments(parts)?;
183

            
184
                Ok(paste::paste! { [<$name Request>] })
185
            }
186
        }
187
    };
188
}
189

            
190
macro_rules! empty_command_response {
191
    ($name:ident) => {
192
        paste::paste! {
193
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
194
            pub struct [<$name Response>];
195
        }
196

            
197
        impl std::default::Default for paste::paste! { [<$name Response>] } {
198
            fn default() -> Self {
199
                paste::paste! { [<$name Response>] }
200
            }
201
        }
202

            
203
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
204
            type Request = paste::paste! { [<$name Request>] };
205

            
206
            fn parse(
207
                _parts: crate::commands::ResponseAttributes<'_>,
208
            ) -> Result<Self, crate::commands::ResponseParserError> {
209
                debug_assert!(_parts.is_empty());
210
                Ok(paste::paste! { [<$name Response>] })
211
            }
212

            
213
            fn serialize(&self) -> Vec<u8> {
214
                Vec::with_capacity(0)
215
            }
216
        }
217
    };
218
}
219

            
220
macro_rules! single_item_command_request {
221
    ($name:ident, $command_name:expr, $item_type:ty) => {
222
        paste::paste! {
223
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
224
            pub struct [<$name Request>] (pub $item_type);
225
        }
226

            
227
        impl paste::paste! { [<$name Request>] } {
228
            pub fn new(item: $item_type) -> Self {
229
                paste::paste! {
230
                    crate::commands::[<$name Request>](item)
231
                }
232
            }
233
        }
234

            
235
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
236
            type Response = paste::paste! { [<$name Response>] };
237

            
238
            const COMMAND: &'static str = $command_name;
239
            const MIN_ARGS: u32 = 1;
240
            const MAX_ARGS: Option<u32> = Some(1);
241

            
242
            fn serialize(&self) -> String {
243
                format!("{} {}\n", Self::COMMAND, self.0)
244
            }
245

            
246
            fn parse(
247
                mut parts: crate::commands::RequestTokenizer<'_>,
248
            ) -> Result<Self, crate::commands::RequestParserError> {
249
                let item_token = parts.next().ok_or(Self::missing_arguments_error(0))?;
250

            
251
                let item = item_token.parse::<$item_type>().map_err(|_| {
252
                    crate::commands::RequestParserError::SubtypeParserError {
253
                        argument_index: 1,
254
                        expected_type: stringify!($item_type),
255
                        raw_input: item_token.to_owned(),
256
                    }
257
                })?;
258

            
259
                Self::throw_if_too_many_arguments(parts)?;
260

            
261
                Ok(paste::paste! { [<$name Request>] ( item ) })
262
            }
263
        }
264
    };
265
}
266

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

            
274
        impl paste::paste! { [<$name Request>] } {
275
            pub fn new(item: Option<$item_type>) -> Self {
276
                paste::paste! {
277
                    crate::commands::[<$name Request>](item)
278
                }
279
            }
280
        }
281

            
282
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
283
            type Response = paste::paste! { [<$name Response>] };
284

            
285
            const COMMAND: &'static str = $command_name;
286
            const MIN_ARGS: u32 = 0;
287
            const MAX_ARGS: Option<u32> = Some(1);
288

            
289
            fn serialize(&self) -> String {
290
                match &self.0 {
291
                    Some(item) => format!("{} {}\n", Self::COMMAND, item),
292
                    None => Self::COMMAND.to_string() + "\n",
293
                }
294
            }
295

            
296
            fn parse(
297
                mut parts: crate::commands::RequestTokenizer<'_>,
298
            ) -> Result<Self, crate::commands::RequestParserError> {
299
                let item = parts
300
                    .next()
301
                    .map(|s| {
302
                        s.parse().map_err(|_| {
303
                            crate::commands::RequestParserError::SubtypeParserError {
304
                                argument_index: 1,
305
                                expected_type: stringify!($item_type),
306
                                raw_input: s.to_owned(),
307
                            }
308
                        })
309
                    })
310
                    .transpose()?;
311

            
312
                Self::throw_if_too_many_arguments(parts)?;
313

            
314
                Ok(paste::paste! { [<$name Request>] ( item ) })
315
            }
316
        }
317
    };
318
}
319

            
320
macro_rules! single_item_command_response {
321
    ($name:ident, $item_name:expr, $item_type:ty) => {
322
        paste::paste! {
323
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
324
            pub struct [<$name Response>] (pub $item_type);
325
        }
326

            
327
        impl paste::paste! { [<$name Response>] } {
328
            pub fn new(item: $item_type) -> Self {
329
                paste::paste! {
330
                    crate::commands::[<$name Response>](item)
331
                }
332
            }
333
        }
334

            
335
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
336
            type Request = paste::paste! { [<$name Request>] };
337

            
338
            fn parse(
339
                parts: crate::commands::ResponseAttributes<'_>,
340
            ) -> Result<Self, crate::commands::ResponseParserError> {
341
                let map = parts.into_map()?;
342

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

            
345
                let item_token = map.get($item_name).ok_or(
346
                    crate::commands::ResponseParserError::MissingProperty($item_name.to_string()),
347
                )?;
348
                let item_ = crate::response_tokenizer::expect_property_type!(
349
                    Some(item_token),
350
                    $item_name,
351
                    Text
352
                );
353
                let item = item_.parse::<$item_type>().map_err(|_| {
354
                    crate::commands::ResponseParserError::InvalidProperty(
355
                        $item_name.to_string(),
356
                        item_.to_string(),
357
                    )
358
                })?;
359

            
360
                Ok(paste::paste! { [<$name Response>] ( item ) })
361
            }
362

            
363
            fn serialize(&self) -> Vec<u8> {
364
                paste::paste! {
365
                    unimplemented!(concat!(
366
                        "response serialization is not yet implemented for ",
367
                        stringify!([<$name Response>])
368
                    ))
369
                }
370
            }
371
        }
372
    };
373
}
374

            
375
macro_rules! multi_item_command_response {
376
    ($name:ident, $item_name:expr, $item_type:ty) => {
377
        paste::paste! {
378
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
379
            pub struct [<$name Response>] (pub Vec<$item_type>);
380
        }
381

            
382
        impl paste::paste! { [<$name Response>] } {
383
            pub fn new(items: Vec<$item_type>) -> Self {
384
                paste::paste! {
385
                    crate::commands::[<$name Response>](items)
386
                }
387
            }
388
        }
389

            
390
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
391
            type Request = paste::paste! { [<$name Request>] };
392

            
393
1
            fn parse(
394
1
                parts: crate::commands::ResponseAttributes<'_>,
395
1
            ) -> Result<Self, crate::commands::ResponseParserError> {
396
                // TODO: use lazy vec
397
1
                let parts_: Vec<_> = parts.into_vec()?;
398

            
399
2
                if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != $item_name) {
400
                    return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
401
1
                }
402

            
403
1
                let mut items = Vec::with_capacity(parts_.len());
404

            
405
1
                let mut iter = parts_.into_iter();
406
3
                while let Some(value) = iter.next() {
407
2
                    let unwrapped_value = expect_property_type!(Some(value.1), $item_name, Text);
408
2
                    let parsed_value = unwrapped_value.parse::<$item_type>().map_err(|_| {
409
                        crate::commands::ResponseParserError::InvalidProperty(
410
                            $item_name.to_string(),
411
                            unwrapped_value.to_string(),
412
                        )
413
                    })?;
414

            
415
2
                    items.push(parsed_value);
416
                }
417

            
418
1
                Ok(paste::paste! { [<$name Response>] ( items ) })
419
1
            }
420

            
421
            fn serialize(&self) -> Vec<u8> {
422
                paste::paste! {
423
                    unimplemented!(concat!(
424
                        "response serialization is not yet implemented for ",
425
                        stringify!([<$name Response>])
426
                    ))
427
                }
428
            }
429
        }
430
    };
431
}
432

            
433
pub(crate) use empty_command_request;
434
pub(crate) use empty_command_response;
435
pub(crate) use multi_item_command_response;
436
pub(crate) use single_item_command_request;
437
pub(crate) use single_item_command_response;
438
pub(crate) use single_optional_item_command_request;
439

            
440
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
441
pub enum RequestParserError {
442
    #[error("Found empty line while parsing request")]
443
    EmptyLine,
444

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

            
449
    // TODO: can we store the parser error as well?
450
    #[error(
451
        "Could not parse argument {argument_index} of the request (expected type: {expected_type}, raw input: '{raw_input}')"
452
    )]
453
    SubtypeParserError {
454
        /// The index of the argument that failed to parse
455
        ///
456
        /// Note that in the case of keyworded arguments, such as
457
        /// `group <groupname>`, `sort <sorting>`, etc., these are
458
        /// counted as a single argument despite being two tokens.
459
        argument_index: u32,
460

            
461
        /// The expected type of the argument
462
        expected_type: &'static str,
463

            
464
        /// The raw input that failed to parse
465
        raw_input: String,
466
    },
467

            
468
    #[error(
469
        "Too many arguments were provided in the request (expected between {expected_min} and {expected_max:?}, found {found})"
470
    )]
471
    TooManyArguments {
472
        /// The minimum number of arguments that were expected
473
        expected_min: u32,
474

            
475
        /// The maximum number of arguments that were expected
476
        ///
477
        /// This is `None` if the amount of arguments is unbounded.
478
        expected_max: Option<u32>,
479

            
480
        /// The number of arguments that were found
481
        ///
482
        /// Note that in the case of keyworded arguments, such as
483
        /// `group <groupname>`, `sort <sorting>`, etc., these are
484
        /// counted as a single argument despite being two tokens.
485
        found: u32,
486
    },
487

            
488
    #[error(
489
        "Not enough arguments were provided in the request (expected between {expected_min} and {expected_max:?}, found {found})"
490
    )]
491
    MissingArguments {
492
        /// The minimum number of arguments that were expected
493
        expected_min: u32,
494

            
495
        /// The maximum number of arguments that were expected.
496
        ///
497
        /// This is `None` if the amount of arguments is unbounded.
498
        expected_max: Option<u32>,
499

            
500
        /// The number of arguments that were found
501
        ///
502
        /// Note that in the case of keyworded arguments, such as
503
        /// `group <groupname>`, `sort <sorting>`, etc., these are
504
        /// counted as a single argument despite being two tokens.
505
        found: u32,
506
    },
507

            
508
    #[error("Keyword argument {keyword} at position {argument_index} is missing its value")]
509
    MissingKeywordValue {
510
        /// The unexpected keyword that was found
511
        keyword: &'static str,
512

            
513
        /// The index of the argument that was missing it's value
514
        ///
515
        /// Note that in the case of keyworded arguments, such as
516
        /// `group <groupname>`, `sort <sorting>`, etc., these are
517
        /// counted as a single argument despite being two tokens.
518
        argument_index: u32,
519
    },
520

            
521
    #[error("A command list was expected to be closed, but the end was not found")]
522
    MissingCommandListEnd,
523

            
524
    #[error("A command list was found inside another command list at line {line}")]
525
    NestedCommandList {
526
        /// The line where the nested command list was found
527
        line: u32,
528
    },
529

            
530
    #[error("An unexpected command list end was found")]
531
    UnexpectedCommandListEnd,
532

            
533
    // TODO: remove this, replaced by EmptyLine + MissingArguments
534
    #[error("Request ended early, while more arguments were expected")]
535
    UnexpectedEOF,
536

            
537
    #[error("Request is missing terminating newline")]
538
    MissingNewline,
539
}
540

            
541
// TODO: should these be renamed to fit the mpd docs?
542
//       "Attribute" instead of "Property"?
543
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
544
pub enum ResponseParserError {
545
    #[error("A property was expected to be present in the response, but was not found: {0}")]
546
    MissingProperty(String),
547

            
548
    // TODO: change name to UnexpectedPropertyEncoding
549
    #[error(
550
        "An expected property was found in the response, but its encoding was not as expected: {0}: {1}"
551
    )]
552
    UnexpectedPropertyType(String, String),
553

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

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

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

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

            
566
    #[error("Response ended early, while more properties were expected")]
567
    UnexpectedEOF,
568
    // #[error("Response is missing terminating newline")]
569
    // MissingNewline,
570
}
571

            
572
/*******************/
573

            
574
pub const COMMAND_NAMES: &[&str] = &[
575
    // Audio output devices
576
    DisableOutputRequest::COMMAND,
577
    EnableOutputRequest::COMMAND,
578
    OutputsRequest::COMMAND,
579
    OutputSetRequest::COMMAND,
580
    ToggleOutputRequest::COMMAND,
581
    // Client to client
582
    ChannelsRequest::COMMAND,
583
    ReadMessagesRequest::COMMAND,
584
    SendMessageRequest::COMMAND,
585
    SubscribeRequest::COMMAND,
586
    UnsubscribeRequest::COMMAND,
587
    // Connection settings
588
    BinaryLimitRequest::COMMAND,
589
    CloseRequest::COMMAND,
590
    KillRequest::COMMAND,
591
    PasswordRequest::COMMAND,
592
    PingRequest::COMMAND,
593
    ProtocolRequest::COMMAND,
594
    ProtocolAllRequest::COMMAND,
595
    ProtocolAvailableRequest::COMMAND,
596
    ProtocolClearRequest::COMMAND,
597
    ProtocolDisableRequest::COMMAND,
598
    ProtocolEnableRequest::COMMAND,
599
    TagTypesRequest::COMMAND,
600
    TagTypesAllRequest::COMMAND,
601
    TagTypesAvailableRequest::COMMAND,
602
    TagTypesClearRequest::COMMAND,
603
    TagTypesDisableRequest::COMMAND,
604
    TagTypesEnableRequest::COMMAND,
605
    TagTypesResetRequest::COMMAND,
606
    // Controlling playback
607
    NextRequest::COMMAND,
608
    PauseRequest::COMMAND,
609
    PlayRequest::COMMAND,
610
    PlayIdRequest::COMMAND,
611
    PreviousRequest::COMMAND,
612
    SeekRequest::COMMAND,
613
    SeekCurRequest::COMMAND,
614
    SeekIdRequest::COMMAND,
615
    StopRequest::COMMAND,
616
    // Mounts and neighbors
617
    ListMountsRequest::COMMAND,
618
    ListNeighborsRequest::COMMAND,
619
    MountRequest::COMMAND,
620
    UnmountRequest::COMMAND,
621
    // Music database
622
    AlbumArtRequest::COMMAND,
623
    CountRequest::COMMAND,
624
    FindRequest::COMMAND,
625
    FindAddRequest::COMMAND,
626
    GetFingerprintRequest::COMMAND,
627
    ListRequest::COMMAND,
628
    ListAllRequest::COMMAND,
629
    ListAllInfoRequest::COMMAND,
630
    ListFilesRequest::COMMAND,
631
    LsInfoRequest::COMMAND,
632
    ReadCommentsRequest::COMMAND,
633
    ReadPictureRequest::COMMAND,
634
    RescanRequest::COMMAND,
635
    SearchRequest::COMMAND,
636
    SearchAddRequest::COMMAND,
637
    SearchAddPlRequest::COMMAND,
638
    SearchCountRequest::COMMAND,
639
    UpdateRequest::COMMAND,
640
    // Partition commands
641
    DelPartitionRequest::COMMAND,
642
    ListPartitionsRequest::COMMAND,
643
    MoveOutputRequest::COMMAND,
644
    NewPartitionRequest::COMMAND,
645
    PartitionRequest::COMMAND,
646
    // Playback options
647
    ConsumeRequest::COMMAND,
648
    CrossfadeRequest::COMMAND,
649
    GetVolRequest::COMMAND,
650
    MixRampDbRequest::COMMAND,
651
    MixRampDelayRequest::COMMAND,
652
    RandomRequest::COMMAND,
653
    RepeatRequest::COMMAND,
654
    ReplayGainModeRequest::COMMAND,
655
    ReplayGainStatusRequest::COMMAND,
656
    SetVolRequest::COMMAND,
657
    SingleRequest::COMMAND,
658
    VolumeRequest::COMMAND,
659
    // Querying mpd status
660
    ClearErrorRequest::COMMAND,
661
    CurrentSongRequest::COMMAND,
662
    IdleRequest::COMMAND,
663
    StatsRequest::COMMAND,
664
    StatusRequest::COMMAND,
665
    // Queue
666
    AddRequest::COMMAND,
667
    AddIdRequest::COMMAND,
668
    AddTagIdRequest::COMMAND,
669
    ClearRequest::COMMAND,
670
    ClearTagIdRequest::COMMAND,
671
    DeleteRequest::COMMAND,
672
    DeleteIdRequest::COMMAND,
673
    MoveRequest::COMMAND,
674
    MoveIdRequest::COMMAND,
675
    PlaylistRequest::COMMAND,
676
    PlaylistFindRequest::COMMAND,
677
    PlaylistIdRequest::COMMAND,
678
    PlaylistInfoRequest::COMMAND,
679
    PlaylistSearchRequest::COMMAND,
680
    PlChangesRequest::COMMAND,
681
    PlChangesPosIdRequest::COMMAND,
682
    PrioRequest::COMMAND,
683
    PrioIdRequest::COMMAND,
684
    RangeIdRequest::COMMAND,
685
    ShuffleRequest::COMMAND,
686
    SwapRequest::COMMAND,
687
    SwapIdRequest::COMMAND,
688
    // Reflection
689
    CommandsRequest::COMMAND,
690
    ConfigRequest::COMMAND,
691
    DecodersRequest::COMMAND,
692
    NotCommandsRequest::COMMAND,
693
    UrlHandlersRequest::COMMAND,
694
    // Stickers
695
    StickerDecRequest::COMMAND,
696
    StickerDeleteRequest::COMMAND,
697
    StickerFindRequest::COMMAND,
698
    StickerGetRequest::COMMAND,
699
    StickerIncRequest::COMMAND,
700
    StickerListRequest::COMMAND,
701
    StickerSetRequest::COMMAND,
702
    StickerNamesRequest::COMMAND,
703
    StickerNamesTypesRequest::COMMAND,
704
    StickerTypesRequest::COMMAND,
705
    // Stored playlists
706
    ListPlaylistRequest::COMMAND,
707
    ListPlaylistInfoRequest::COMMAND,
708
    ListPlaylistsRequest::COMMAND,
709
    LoadRequest::COMMAND,
710
    PlaylistAddRequest::COMMAND,
711
    PlaylistClearRequest::COMMAND,
712
    PlaylistDeleteRequest::COMMAND,
713
    PlaylistLengthRequest::COMMAND,
714
    PlaylistMoveRequest::COMMAND,
715
    RenameRequest::COMMAND,
716
    RmRequest::COMMAND,
717
    SaveRequest::COMMAND,
718
    SearchPlaylistRequest::COMMAND,
719
];