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
use serde::{Deserialize, Serialize};
13
use thiserror::Error;
14

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

            
29
pub use audio_output_devices::*;
30
pub use client_to_client::*;
31
pub use connection_settings::*;
32
pub use controlling_playback::*;
33
pub use mounts_and_neighbors::*;
34
pub use music_database::*;
35
pub use partition_commands::*;
36
pub use playback_options::*;
37
pub use querying_mpd_status::*;
38
pub use queue::*;
39
pub use reflection::*;
40
pub use stickers::*;
41
pub use stored_playlists::*;
42

            
43
/// A trait modelling a single MPD command request.
44
pub trait CommandRequest
45
where
46
    Self: Sized,
47
{
48
    /// The command name used within the protocol
49
    const COMMAND: &'static str;
50

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

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

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

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

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

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

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

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

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

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

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

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

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

            
131
/// A trait modelling a single MPD command response.
132
pub trait CommandResponse
133
where
134
    Self: Sized,
135
{
136
    // /// Serializes the response into a Vec<u8>.
137
    // fn serialize(&self) -> Vec<u8>;
138

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

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

            
149
/// A trait modelling the request/response pair of a single MPD command.
150
pub trait Command {
151
    /// The request sent from the client to the server
152
    type Request: CommandRequest;
153
    /// The response sent from the server to the client
154
    type Response: CommandResponse;
155

            
156
    /// The command name used within the protocol
157
    const COMMAND: &'static str = Self::Request::COMMAND;
158

            
159
    /// Parse the request from its tokenized parts. See also [`parse_raw_request`].
160
    fn parse_request(parts: RequestTokenizer) -> Result<Self::Request, RequestParserError> {
161
        Self::Request::parse(parts)
162
    }
163

            
164
    /// Parse the raw request string into a request.
165
    /// This assumes the raw string starts with the command name, e.g. `command_name arg1 "arg2 arg3"`
166
    fn parse_raw_request(raw: &str) -> Result<Self::Request, RequestParserError> {
167
        Self::Request::parse_raw(raw)
168
    }
169

            
170
    // /// Serialize the response into a string.
171
    // fn serialize_response(&self, response: Self::Response) -> String {
172

            
173
    /// Parse the response from its tokenized parts. See also [`parse_raw_response`].
174
    fn parse_response(parts: ResponseAttributes) -> Result<Self::Response, ResponseParserError> {
175
        Self::Response::parse(parts)
176
    }
177
    /// Parse the raw response string into a response.
178
10
    fn parse_raw_response(raw: &[u8]) -> Result<Self::Response, ResponseParserError> {
179
10
        Self::Response::parse_raw(raw)
180
10
    }
181
}
182

            
183
// Request/response implementation helpers
184

            
185
macro_rules! empty_command_request {
186
    ($name:ident, $command_name:expr) => {
187
        paste::paste! {
188
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
189
            pub struct [<$name Request>];
190
        }
191

            
192
        impl std::default::Default for paste::paste! { [<$name Request>] } {
193
            fn default() -> Self {
194
                paste::paste! { [<$name Request>] }
195
            }
196
        }
197

            
198
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
199
            const COMMAND: &'static str = $command_name;
200
            const MIN_ARGS: u32 = 0;
201
            const MAX_ARGS: Option<u32> = Some(0);
202

            
203
            fn serialize(&self) -> String {
204
                Self::COMMAND.to_string() + "\n"
205
            }
206

            
207
            fn parse(
208
                parts: crate::commands::RequestTokenizer<'_>,
209
            ) -> Result<Self, crate::commands::RequestParserError> {
210
                Self::throw_if_too_many_arguments(parts)?;
211

            
212
                Ok(paste::paste! { [<$name Request>] })
213
            }
214
        }
215
    };
216
}
217

            
218
macro_rules! empty_command_response {
219
    ($name:ident) => {
220
        paste::paste! {
221
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
222
            pub struct [<$name Response>];
223
        }
224

            
225
        impl std::default::Default for paste::paste! { [<$name Response>] } {
226
            fn default() -> Self {
227
                paste::paste! { [<$name Response>] }
228
            }
229
        }
230

            
231
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
232
            fn parse(
233
                _parts: crate::commands::ResponseAttributes<'_>,
234
            ) -> Result<Self, crate::commands::ResponseParserError> {
235
                debug_assert!(_parts.is_empty());
236
                Ok(paste::paste! { [<$name Response>] })
237
            }
238
        }
239
    };
240
}
241

            
242
macro_rules! single_item_command_request {
243
    ($name:ident, $command_name:expr, $item_type:ty) => {
244
        paste::paste! {
245
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
246
            pub struct [<$name Request>] (pub $item_type);
247
        }
248

            
249
        impl paste::paste! { [<$name Request>] } {
250
            pub fn new(item: $item_type) -> Self {
251
                paste::paste! {
252
                    crate::commands::[<$name Request>](item)
253
                }
254
            }
255
        }
256

            
257
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
258
            const COMMAND: &'static str = $command_name;
259
            const MIN_ARGS: u32 = 1;
260
            const MAX_ARGS: Option<u32> = Some(1);
261

            
262
            fn serialize(&self) -> String {
263
                format!("{} {}\n", Self::COMMAND, self.0)
264
            }
265

            
266
            fn parse(
267
                mut parts: crate::commands::RequestTokenizer<'_>,
268
            ) -> Result<Self, crate::commands::RequestParserError> {
269
                let item_token = parts.next().ok_or(Self::missing_arguments_error(0))?;
270

            
271
                let item = item_token.parse::<$item_type>().map_err(|_| {
272
                    crate::commands::RequestParserError::SubtypeParserError {
273
                        argument_index: 1,
274
                        expected_type: stringify!($item_type),
275
                        raw_input: item_token.to_owned(),
276
                    }
277
                })?;
278

            
279
                Self::throw_if_too_many_arguments(parts)?;
280

            
281
                Ok(paste::paste! { [<$name Request>] ( item ) })
282
            }
283
        }
284
    };
285
}
286

            
287
macro_rules! single_optional_item_command_request {
288
    ($name:ident, $command_name:expr, $item_type:ty) => {
289
        paste::paste! {
290
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
291
            pub struct [<$name Request>] (pub Option<$item_type>);
292
        }
293

            
294
        impl paste::paste! { [<$name Request>] } {
295
            pub fn new(item: Option<$item_type>) -> Self {
296
                paste::paste! {
297
                    crate::commands::[<$name Request>](item)
298
                }
299
            }
300
        }
301

            
302
        impl crate::commands::CommandRequest for paste::paste! { [<$name Request>] } {
303
            const COMMAND: &'static str = $command_name;
304
            const MIN_ARGS: u32 = 0;
305
            const MAX_ARGS: Option<u32> = Some(1);
306

            
307
            fn serialize(&self) -> String {
308
                match &self.0 {
309
                    Some(item) => format!("{} {}\n", Self::COMMAND, item),
310
                    None => Self::COMMAND.to_string() + "\n",
311
                }
312
            }
313

            
314
            fn parse(
315
                mut parts: crate::commands::RequestTokenizer<'_>,
316
            ) -> Result<Self, crate::commands::RequestParserError> {
317
                let item = parts
318
                    .next()
319
                    .map(|s| {
320
                        s.parse().map_err(|_| {
321
                            crate::commands::RequestParserError::SubtypeParserError {
322
                                argument_index: 1,
323
                                expected_type: stringify!($item_type),
324
                                raw_input: s.to_owned(),
325
                            }
326
                        })
327
                    })
328
                    .transpose()?;
329

            
330
                Self::throw_if_too_many_arguments(parts)?;
331

            
332
                Ok(paste::paste! { [<$name Request>] ( item ) })
333
            }
334
        }
335
    };
336
}
337

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

            
345
        impl paste::paste! { [<$name Response>] } {
346
            pub fn new(item: $item_type) -> Self {
347
                paste::paste! {
348
                    crate::commands::[<$name Response>](item)
349
                }
350
            }
351
        }
352

            
353
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
354
            fn parse(
355
                parts: crate::commands::ResponseAttributes<'_>,
356
            ) -> Result<Self, crate::commands::ResponseParserError> {
357
                let map = parts.into_map()?;
358

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

            
361
                let item_token = map.get($item_name).ok_or(
362
                    crate::commands::ResponseParserError::MissingProperty($item_name.to_string()),
363
                )?;
364
                let item_ = crate::response_tokenizer::expect_property_type!(
365
                    Some(item_token),
366
                    $item_name,
367
                    Text
368
                );
369
                let item = item_.parse::<$item_type>().map_err(|_| {
370
                    crate::commands::ResponseParserError::InvalidProperty(
371
                        $item_name.to_string(),
372
                        item_.to_string(),
373
                    )
374
                })?;
375

            
376
                Ok(paste::paste! { [<$name Response>] ( item ) })
377
            }
378
        }
379
    };
380
}
381

            
382
macro_rules! multi_item_command_response {
383
    ($name:ident, $item_name:expr, $item_type:ty) => {
384
        paste::paste! {
385
            #[derive(Debug, Clone, PartialEq, serde::Serialize, serde::Deserialize)]
386
            pub struct [<$name Response>] (pub Vec<$item_type>);
387
        }
388

            
389
        impl paste::paste! { [<$name Response>] } {
390
            pub fn new(items: Vec<$item_type>) -> Self {
391
                paste::paste! {
392
                    crate::commands::[<$name Response>](items)
393
                }
394
            }
395
        }
396

            
397
        impl crate::commands::CommandResponse for paste::paste! { [<$name Response>] } {
398
1
            fn parse(
399
1
                parts: crate::commands::ResponseAttributes<'_>,
400
1
            ) -> Result<Self, crate::commands::ResponseParserError> {
401
                // TODO: use lazy vec
402
1
                let parts_: Vec<_> = parts.into_vec()?;
403

            
404
2
                if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != $item_name) {
405
                    return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
406
1
                }
407

            
408
1
                let mut items = Vec::with_capacity(parts_.len());
409

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

            
420
2
                    items.push(parsed_value);
421
                }
422

            
423
1
                Ok(paste::paste! { [<$name Response>] ( items ) })
424
1
            }
425
        }
426
    };
427
}
428

            
429
pub(crate) use empty_command_request;
430
pub(crate) use empty_command_response;
431
pub(crate) use multi_item_command_response;
432
pub(crate) use single_item_command_request;
433
pub(crate) use single_item_command_response;
434
pub(crate) use single_optional_item_command_request;
435

            
436
#[derive(Error, Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
437
pub enum RequestParserError {
438
    #[error("Found empty line while parsing request")]
439
    EmptyLine,
440

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

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

            
457
        /// The expected type of the argument
458
        expected_type: &'static str,
459

            
460
        /// The raw input that failed to parse
461
        raw_input: String,
462
    },
463

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

            
471
        /// The maximum number of arguments that were expected
472
        ///
473
        /// This is `None` if the amount of arguments is unbounded.
474
        expected_max: Option<u32>,
475

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

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

            
491
        /// The maximum number of arguments that were expected.
492
        ///
493
        /// This is `None` if the amount of arguments is unbounded.
494
        expected_max: Option<u32>,
495

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

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

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

            
517
    #[error("A command list was expected to be closed, but the end was not found")]
518
    MissingCommandListEnd,
519

            
520
    #[error("A command list was found inside another command list at line {line}")]
521
    NestedCommandList {
522
        /// The line where the nested command list was found
523
        line: u32,
524
    },
525

            
526
    #[error("An unexpected command list end was found")]
527
    UnexpectedCommandListEnd,
528

            
529
    // TODO: remove this, replaced by EmptyLine + MissingArguments
530
    #[error("Request ended early, while more arguments were expected")]
531
    UnexpectedEOF,
532

            
533
    #[error("Request is missing terminating newline")]
534
    MissingNewline,
535
}
536

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

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

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

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

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

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

            
562
    #[error("Response ended early, while more properties were expected")]
563
    UnexpectedEOF,
564
    // #[error("Response is missing terminating newline")]
565
    // MissingNewline,
566
}
567

            
568
/*******************/
569

            
570
pub const COMMAND_NAMES: &[&str] = &[
571
    // Audio output devices
572
    DisableOutput::COMMAND,
573
    EnableOutput::COMMAND,
574
    Outputs::COMMAND,
575
    OutputSet::COMMAND,
576
    ToggleOutput::COMMAND,
577
    // Client to client
578
    Channels::COMMAND,
579
    ReadMessages::COMMAND,
580
    SendMessage::COMMAND,
581
    Subscribe::COMMAND,
582
    Unsubscribe::COMMAND,
583
    // Connection settings
584
    BinaryLimit::COMMAND,
585
    Close::COMMAND,
586
    Kill::COMMAND,
587
    Password::COMMAND,
588
    Ping::COMMAND,
589
    Protocol::COMMAND,
590
    ProtocolAll::COMMAND,
591
    ProtocolAvailable::COMMAND,
592
    ProtocolClear::COMMAND,
593
    ProtocolDisable::COMMAND,
594
    ProtocolEnable::COMMAND,
595
    TagTypes::COMMAND,
596
    TagTypesAll::COMMAND,
597
    TagTypesAvailable::COMMAND,
598
    TagTypesClear::COMMAND,
599
    TagTypesDisable::COMMAND,
600
    TagTypesEnable::COMMAND,
601
    TagTypesReset::COMMAND,
602
    // Controlling playback
603
    Next::COMMAND,
604
    Pause::COMMAND,
605
    Play::COMMAND,
606
    PlayId::COMMAND,
607
    Previous::COMMAND,
608
    Seek::COMMAND,
609
    SeekCur::COMMAND,
610
    SeekId::COMMAND,
611
    Stop::COMMAND,
612
    // Mounts and neighbors
613
    ListMounts::COMMAND,
614
    ListNeighbors::COMMAND,
615
    Mount::COMMAND,
616
    Unmount::COMMAND,
617
    // Music database
618
    AlbumArt::COMMAND,
619
    Count::COMMAND,
620
    Find::COMMAND,
621
    FindAdd::COMMAND,
622
    GetFingerprint::COMMAND,
623
    List::COMMAND,
624
    ListAll::COMMAND,
625
    ListAllInfo::COMMAND,
626
    ListFiles::COMMAND,
627
    LsInfo::COMMAND,
628
    ReadComments::COMMAND,
629
    ReadPicture::COMMAND,
630
    Rescan::COMMAND,
631
    Search::COMMAND,
632
    SearchAdd::COMMAND,
633
    SearchAddPl::COMMAND,
634
    SearchCount::COMMAND,
635
    Update::COMMAND,
636
    // Partition commands
637
    DelPartition::COMMAND,
638
    ListPartitions::COMMAND,
639
    MoveOutput::COMMAND,
640
    NewPartition::COMMAND,
641
    Partition::COMMAND,
642
    // Playback options
643
    Consume::COMMAND,
644
    Crossfade::COMMAND,
645
    GetVol::COMMAND,
646
    MixRampDb::COMMAND,
647
    MixRampDelay::COMMAND,
648
    Random::COMMAND,
649
    Repeat::COMMAND,
650
    ReplayGainMode::COMMAND,
651
    ReplayGainStatus::COMMAND,
652
    SetVol::COMMAND,
653
    Single::COMMAND,
654
    Volume::COMMAND,
655
    // Querying mpd status
656
    ClearError::COMMAND,
657
    CurrentSong::COMMAND,
658
    Idle::COMMAND,
659
    Stats::COMMAND,
660
    Status::COMMAND,
661
    // Queue
662
    Add::COMMAND,
663
    AddId::COMMAND,
664
    AddTagId::COMMAND,
665
    Clear::COMMAND,
666
    ClearTagId::COMMAND,
667
    Delete::COMMAND,
668
    DeleteId::COMMAND,
669
    Move::COMMAND,
670
    MoveId::COMMAND,
671
    Playlist::COMMAND,
672
    PlaylistFind::COMMAND,
673
    PlaylistId::COMMAND,
674
    PlaylistInfo::COMMAND,
675
    PlaylistSearch::COMMAND,
676
    PlChanges::COMMAND,
677
    PlChangesPosId::COMMAND,
678
    Prio::COMMAND,
679
    PrioId::COMMAND,
680
    RangeId::COMMAND,
681
    Shuffle::COMMAND,
682
    Swap::COMMAND,
683
    SwapId::COMMAND,
684
    // Reflection
685
    Commands::COMMAND,
686
    Config::COMMAND,
687
    Decoders::COMMAND,
688
    NotCommands::COMMAND,
689
    UrlHandlers::COMMAND,
690
    // Stickers
691
    StickerDec::COMMAND,
692
    StickerDelete::COMMAND,
693
    StickerFind::COMMAND,
694
    StickerGet::COMMAND,
695
    StickerInc::COMMAND,
696
    StickerList::COMMAND,
697
    StickerSet::COMMAND,
698
    StickerNames::COMMAND,
699
    StickerNamesTypes::COMMAND,
700
    StickerTypes::COMMAND,
701
    // Stored playlists
702
    ListPlaylist::COMMAND,
703
    ListPlaylistInfo::COMMAND,
704
    ListPlaylists::COMMAND,
705
    Load::COMMAND,
706
    PlaylistAdd::COMMAND,
707
    PlaylistClear::COMMAND,
708
    PlaylistDelete::COMMAND,
709
    PlaylistLength::COMMAND,
710
    PlaylistMove::COMMAND,
711
    Rename::COMMAND,
712
    Rm::COMMAND,
713
    Save::COMMAND,
714
    SearchPlaylist::COMMAND,
715
];