Add more commands

This commit is contained in:
Oystein Kristoffer Tveit 2024-12-01 20:06:01 +01:00
parent 3e512092bd
commit 2ee6bbc582
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
54 changed files with 1605 additions and 12 deletions

View File

@ -4,17 +4,27 @@ use crate::Request;
mod audio_output_devices;
mod client_to_client;
mod connection_settings;
mod controlling_playback;
mod mounts_and_neighbors;
mod partition_commands;
mod playback_options;
mod querying_mpd_status;
mod queue;
mod reflection;
mod stored_playlists;
pub use audio_output_devices::*;
pub use client_to_client::*;
pub use connection_settings::*;
pub use controlling_playback::*;
pub use mounts_and_neighbors::*;
pub use partition_commands::*;
pub use playback_options::*;
pub use querying_mpd_status::*;
pub use queue::*;
pub use reflection::*;
pub use stored_playlists::*;
pub trait Command {
type Response;
@ -87,10 +97,13 @@ pub enum RequestParserError {
MissingNewline,
}
// TODO: should these be renamed to fit the mpd docs?
// "Attribute" instead of "Property"?
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseParserError<'a> {
MissingProperty(&'a str),
UnexpectedPropertyType(&'a str, &'a str),
UnexpectedProperty(&'a str),
InvalidProperty(&'a str, &'a str),
SyntaxError(u64, &'a str),
UnexpectedEOF,

View File

@ -8,4 +8,4 @@ pub use disableoutput::DisableOutput;
pub use enableoutput::EnableOutput;
pub use outputs::Outputs;
pub use outputset::OutputSet;
pub use toggleoutput::ToggleOutput;
pub use toggleoutput::ToggleOutput;

View File

@ -0,0 +1,37 @@
pub mod binary_limit;
pub mod close;
pub mod kill;
pub mod password;
pub mod ping;
pub mod protocol;
pub mod protocol_all;
pub mod protocol_available;
pub mod protocol_clear;
pub mod protocol_disable;
pub mod protocol_enable;
pub mod tag_types;
pub mod tag_types_all;
pub mod tag_types_available;
pub mod tag_types_clear;
pub mod tag_types_disable;
pub mod tag_types_enable;
pub mod tag_types_reset;
pub use binary_limit::BinaryLimit;
pub use close::Close;
pub use kill::Kill;
pub use password::Password;
pub use ping::Ping;
pub use protocol::Protocol;
pub use protocol_all::ProtocolAll;
pub use protocol_available::ProtocolAvailable;
pub use protocol_clear::ProtocolClear;
pub use protocol_disable::ProtocolDisable;
pub use protocol_enable::ProtocolEnable;
pub use tag_types::TagTypes;
pub use tag_types_all::TagTypesAll;
pub use tag_types_available::TagTypesAvailable;
pub use tag_types_clear::TagTypesClear;
pub use tag_types_disable::TagTypesDisable;
pub use tag_types_enable::TagTypesEnable;
pub use tag_types_reset::TagTypesReset;

View File

@ -0,0 +1,27 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct BinaryLimit;
impl Command for BinaryLimit {
type Response = ();
const COMMAND: &'static str = "binarylimit";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let limit = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let limit = limit
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, limit.to_string()))?;
debug_assert!(parts.next().is_none());
Ok((Request::BinaryLimit(limit), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct Close;
impl Command for Close {
type Response = ();
const COMMAND: &'static str = "close";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Close, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct Kill;
impl Command for Kill {
type Response = ();
const COMMAND: &'static str = "kill";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Kill, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,27 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Password;
impl Command for Password {
type Response = ();
const COMMAND: &'static str = "password";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let password = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Password(password), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct Ping;
impl Command for Ping {
type Response = ();
const COMMAND: &'static str = "ping";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Ping, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct Protocol;
impl Command for Protocol {
type Response = ();
const COMMAND: &'static str = "protocol";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Protocol, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,23 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct ProtocolAll;
impl Command for ProtocolAll {
type Response = ();
const COMMAND: &'static str = "protocol all";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ProtocolAll, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct ProtocolAvailable;
impl Command for ProtocolAvailable {
type Response = ();
const COMMAND: &'static str = "protocol available";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ProtocolAvailable, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,23 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct ProtocolClear;
impl Command for ProtocolClear {
type Response = ();
const COMMAND: &'static str = "protocol clear";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ProtocolClear, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct ProtocolDisable;
impl Command for ProtocolDisable {
type Response = ();
const COMMAND: &'static str = "protocol disable";
fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable();
if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF);
}
let mut protocols = Vec::new();
for protocol in parts {
protocols.push(protocol.to_string());
}
Ok((Request::ProtocolDisable(protocols), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct ProtocolEnable;
impl Command for ProtocolEnable {
type Response = ();
const COMMAND: &'static str = "protocol enable";
fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable();
if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF);
}
let mut protocols = Vec::new();
for protocol in parts {
protocols.push(protocol.to_string());
}
Ok((Request::ProtocolEnable(protocols), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,42 @@
use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct TagTypes;
pub type TagTypesResponse = Vec<String>;
impl Command for TagTypes {
type Response = TagTypesResponse;
const COMMAND: &'static str = "tagtypes";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::TagTypes, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into();
let mut tagtypes = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() {
debug_assert_eq!(key, "tagtype");
let tagtype = match value {
GenericResponseValue::Text(name) => name.to_string(),
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"tagtype", "Binary",
))
}
};
tagtypes.push(tagtype);
}
Ok(tagtypes)
}
}

View File

@ -0,0 +1,23 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct TagTypesAll;
impl Command for TagTypesAll {
type Response = ();
const COMMAND: &'static str = "tagtypes all";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::TagTypesAll, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,22 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct TagTypesAvailable;
impl Command for TagTypesAvailable {
type Response = ();
const COMMAND: &'static str = "tagtypes available";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::TagTypesAvailable, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,23 @@
use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request,
};
pub struct TagTypesClear;
impl Command for TagTypesClear {
type Response = ();
const COMMAND: &'static str = "tagtypes clear";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::TagTypesClear, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct TagTypesDisable;
impl Command for TagTypesDisable {
type Response = ();
const COMMAND: &'static str = "tagtypes disable";
fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable();
if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF);
}
let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesDisable(tag_types), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct TagTypesEnable;
impl Command for TagTypesEnable {
type Response = ();
const COMMAND: &'static str = "tagtypes enable";
fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable();
if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF);
}
let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesEnable(tag_types), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct TagTypesReset;
impl Command for TagTypesReset {
type Response = ();
const COMMAND: &'static str = "tagtypes reset";
fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable();
if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF);
}
let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesReset(tag_types), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,9 @@
pub mod listmounts;
pub mod listneighbors;
pub mod mount;
pub mod unmount;
pub use listmounts::ListMounts;
pub use listneighbors::ListNeighbors;
pub use mount::Mount;
pub use unmount::Unmount;

View File

@ -0,0 +1,49 @@
use crate::{
commands::{
Command, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
// pub struct Unmount;
// impl Command for Unmount {
// type Response = ();
// const COMMAND: &'static str = "unmount";
// fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
// let path = parts
// .next()
// .ok_or(RequestParserError::UnexpectedEOF)?
// .to_string();
// debug_assert!(parts.next().is_none());
// Ok((Request::Unmount(path), ""))
// }
// fn parse_response(
// parts: ResponseAttributes<'_>,
// ) -> Result<Self::Response, ResponseParserError> {
// debug_assert!(parts.is_empty());
// Ok(())
// }
// }
pub struct ListMounts;
impl Command for ListMounts {
type Response = Vec<(String, String)>;
const COMMAND: &'static str = "listmounts";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ListMounts, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,24 @@
use crate::{
commands::{
Command, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct ListNeighbors;
impl Command for ListNeighbors {
type Response = Vec<(String, String)>;
const COMMAND: &'static str = "listneighbors";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ListNeighbors, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,36 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct Mount;
impl Command for Mount {
type Response = ();
const COMMAND: &'static str = "mount";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let path = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let uri = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Mount(path, uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,31 @@
use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request,
};
pub struct Unmount;
impl Command for Unmount {
type Response = ();
const COMMAND: &'static str = "unmount";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let path = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Unmount(path), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,11 @@
pub mod delpartition;
pub mod listpartitions;
pub mod moveoutput;
pub mod newpartition;
pub mod partition;
pub use delpartition::DelPartition;
pub use listpartitions::ListPartitions;
pub use moveoutput::MoveOutput;
pub use newpartition::NewPartition;
pub use partition::Partition;

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct DelPartition;
impl Command for DelPartition {
type Response = ();
const COMMAND: &'static str = "delpartition";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let partition = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::DelPartition(partition), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,43 @@
use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct ListPartitions;
pub type ListPartitionsResponse = Vec<String>;
impl Command for ListPartitions {
type Response = ListPartitionsResponse;
const COMMAND: &'static str = "listpartitions";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ListPartitions, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into();
let mut partitions = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() {
debug_assert_eq!(key, "partition");
let partition = match value {
GenericResponseValue::Text(name) => name.to_string(),
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"partition",
"Binary",
))
}
};
partitions.push(partition);
}
Ok(partitions)
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct MoveOutput;
impl Command for MoveOutput {
type Response = ();
const COMMAND: &'static str = "moveoutput";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let output_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::MoveOutput(output_name), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct NewPartition;
impl Command for NewPartition {
type Response = ();
const COMMAND: &'static str = "newpartition";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let partition = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::NewPartition(partition), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Partition;
impl Command for Partition {
type Response = ();
const COMMAND: &'static str = "partition";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let partition = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Partition(partition), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -1,9 +1,12 @@
use std::str::FromStr;
use crate::{commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
}, request::VolumeValue};
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
request::VolumeValue,
};
pub struct Volume;

View File

@ -0,0 +1,11 @@
pub mod commands;
pub mod config;
pub mod decoders;
pub mod not_commands;
pub mod url_handlers;
pub use commands::Commands;
pub use config::Config;
pub use decoders::Decoders;
pub use not_commands::NotCommands;
pub use url_handlers::UrlHandlers;

View File

@ -0,0 +1,21 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct Commands;
impl Command for Commands {
type Response = ();
const COMMAND: &'static str = "commands";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Commands, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,42 @@
use std::collections::HashMap;
use crate::commands::{
get_and_parse_property, get_property, Command, Request, RequestParserResult,
ResponseAttributes, ResponseParserError,
};
pub struct Config;
pub struct ConfigResponse {
pub music_directory: String,
pub playlist_directory: String,
pub pcre: bool,
}
impl Command for Config {
type Response = ConfigResponse;
const COMMAND: &'static str = "config";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Config, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let music_directory = get_property!(parts, "music_directory", Text).to_string();
let playlist_directory = get_property!(parts, "playlist_directory", Text).to_string();
// TODO: is this parsed correctly?
let pcre = get_and_parse_property!(parts, "pcre", Text);
Ok(ConfigResponse {
music_directory,
playlist_directory,
pcre,
})
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct Decoders;
pub struct Decoder {
pub plugin: String,
pub suffixes: Vec<String>,
pub mime_types: Vec<String>,
}
pub type DecodersResponse = Vec<Decoder>;
impl Command for Decoders {
type Response = DecodersResponse;
const COMMAND: &'static str = "decoders";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::Decoders, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,21 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct NotCommands;
impl Command for NotCommands {
type Response = ();
const COMMAND: &'static str = "not_commands";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::NotCommands, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,40 @@
use crate::commands::{
Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct UrlHandlers;
pub type UrlHandlersResponse = Vec<String>;
impl Command for UrlHandlers {
type Response = UrlHandlersResponse;
const COMMAND: &'static str = "urlhandlers";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::UrlHandlers, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into();
let mut url_handlers = Vec::new();
for (key, value) in parts.into_iter() {
if key != "handler" {
return Err(ResponseParserError::UnexpectedProperty(key));
}
let value = match value {
GenericResponseValue::Text(value) => value,
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"handler", "Binary",
))
}
};
url_handlers.push(value.to_string());
}
Ok(url_handlers)
}
}

View File

@ -0,0 +1,27 @@
pub mod listplaylist;
pub mod listplaylistinfo;
pub mod listplaylists;
pub mod load;
pub mod playlistadd;
pub mod playlistclear;
pub mod playlistdelete;
pub mod playlistlength;
pub mod playlistmove;
pub mod rename;
pub mod rm;
pub mod save;
pub mod searchplaylist;
pub use listplaylist::ListPlaylist;
pub use listplaylistinfo::ListPlaylistInfo;
pub use listplaylists::ListPlaylists;
pub use load::Load;
pub use playlistadd::PlaylistAdd;
pub use playlistclear::PlaylistClear;
pub use playlistdelete::PlaylistDelete;
pub use playlistlength::PlaylistLength;
pub use playlistmove::PlaylistMove;
pub use rename::Rename;
pub use rm::Rm;
pub use save::Save;
pub use searchplaylist::SearchPlaylist;

View File

@ -0,0 +1,53 @@
use crate::{
commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
},
common::PlaylistName,
};
pub struct ListPlaylist;
pub type ListPlaylistResponse = Vec<PlaylistName>;
impl Command for ListPlaylist {
type Response = ListPlaylistResponse;
const COMMAND: &'static str = "listplaylist";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let name = name
.parse::<PlaylistName>()
.map_err(|_| RequestParserError::SyntaxError(0, name.to_owned()))?;
let range = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))
})
.transpose()?;
debug_assert!(parts.next().is_none());
Ok((Request::ListPlaylist(name.to_string(), range), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into();
parts
.into_iter()
.map(|(name, value)| {
debug_assert_eq!(name, "file");
match value {
GenericResponseValue::Text(value) => Ok(value.to_string()),
GenericResponseValue::Binary(_) => Err(
ResponseParserError::UnexpectedPropertyType("file", "Binary"),
),
}
})
.collect::<Result<Vec<_>, _>>()
}
}

View File

@ -0,0 +1,39 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::PlaylistName,
};
pub struct ListPlaylistInfo;
impl Command for ListPlaylistInfo {
type Response = ();
const COMMAND: &'static str = "listplaylistinfo";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let name = name
.parse::<PlaylistName>()
.map_err(|_| RequestParserError::SyntaxError(0, name.to_owned()))?;
let range = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))
})
.transpose()?;
debug_assert!(parts.next().is_none());
Ok((Request::ListPlaylistInfo(name, range), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,21 @@
use crate::commands::{
Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
};
pub struct ListPlaylists;
impl Command for ListPlaylists {
type Response = ();
const COMMAND: &'static str = "listplaylists";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none());
Ok((Request::ListPlaylists, ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,46 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Load;
impl Command for Load {
type Response = ();
const COMMAND: &'static str = "load";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let mut range = None;
let mut pos = None;
for _ in 0..2 {
if let Some(range_or_pos) = parts.next() {
if range_or_pos.contains(':') {
range = Some(range_or_pos.parse().map_err(|_| {
RequestParserError::SyntaxError(0, range_or_pos.to_owned())
})?);
} else {
pos = Some(range_or_pos.parse().map_err(|_| {
RequestParserError::SyntaxError(0, range_or_pos.to_owned())
})?);
}
}
}
debug_assert!(parts.next().is_none());
Ok((Request::Load(playlist_name, range, pos), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,42 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct PlaylistAdd;
impl Command for PlaylistAdd {
type Response = ();
const COMMAND: &'static str = "playlistadd";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let uri = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let position = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))
})
.transpose()?;
debug_assert!(parts.next().is_none());
Ok((Request::PlaylistAdd(playlist_name, uri, position), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct PlaylistClear;
impl Command for PlaylistClear {
type Response = ();
const COMMAND: &'static str = "playlistclear";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::PlaylistClear(playlist_name), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,35 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct PlaylistDelete;
impl Command for PlaylistDelete {
type Response = ();
const COMMAND: &'static str = "playlistdelete";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
// TODO: this can be a range, according to docs
let position = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let position = position
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, position.to_string()))?;
debug_assert!(parts.next().is_none());
Ok((Request::PlaylistDelete(playlist_name, position), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,40 @@
use std::collections::HashMap;
use crate::commands::{
get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
};
pub struct PlaylistLength;
pub struct PlaylistLengthResponse {
pub songs: u64,
pub playtime: u64,
}
impl Command for PlaylistLength {
type Response = PlaylistLengthResponse;
const COMMAND: &'static str = "playlistlength";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::PlaylistLength(playlist_name), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let songs = get_and_parse_property!(parts, "songs", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text);
Ok(PlaylistLengthResponse { songs, playtime })
}
}

View File

@ -0,0 +1,46 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct PlaylistMove;
impl Command for PlaylistMove {
type Response = ();
const COMMAND: &'static str = "playlistmove";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let mut from = None;
let from_or_range_or_to = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let to = parts.next();
debug_assert!(parts.next().is_none());
let to = if let Some(to) = to {
from = Some(from_or_range_or_to.parse().map_err(|_| {
RequestParserError::SyntaxError(0, from_or_range_or_to.to_string())
})?);
to.parse()
.map_err(|_| RequestParserError::SyntaxError(0, to.to_string()))?
} else {
from_or_range_or_to
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, from_or_range_or_to.to_string()))?
};
Ok((Request::PlaylistMove(playlist_name, from, to), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,34 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Rename;
impl Command for Rename {
type Response = ();
const COMMAND: &'static str = "rename";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let old_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let new_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Rename(old_name, new_name), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,29 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Rm;
impl Command for Rm {
type Response = ();
const COMMAND: &'static str = "rm";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
debug_assert!(parts.next().is_none());
Ok((Request::Rm(playlist_name), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,37 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Save;
impl Command for Save {
type Response = ();
const COMMAND: &'static str = "save";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let mode = parts
.next()
.map(|m| {
m.parse()
.map_err(|_| RequestParserError::SyntaxError(0, m.to_string()))
})
.transpose()?;
debug_assert!(parts.next().is_none());
Ok((Request::Save(playlist_name, mode), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty());
Ok(())
}
}

View File

@ -0,0 +1,44 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::{Filter, PlaylistName},
};
pub struct SearchPlaylist;
impl Command for SearchPlaylist {
type Response = ();
const COMMAND: &'static str = "searchplaylist";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let name = name
.parse::<PlaylistName>()
.map_err(|_| RequestParserError::SyntaxError(0, name.to_owned()))?;
let filter = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let filter = filter
.parse::<Filter>()
.map_err(|_| RequestParserError::SyntaxError(0, filter.to_owned()))?;
let range = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))
})
.transpose()?;
debug_assert!(parts.next().is_none());
Ok((Request::SearchPlaylist(name, filter, range), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -80,13 +80,13 @@ pub enum Request {
ListPlaylistInfo(PlaylistName, Option<WindowRange>),
SearchPlaylist(PlaylistName, Filter, Option<WindowRange>),
ListPlaylists,
Load(PlaylistName, Option<WindowRange>, SongPosition),
PlaylistAdd(PlaylistName, Uri, SongPosition),
Load(PlaylistName, Option<WindowRange>, Option<SongPosition>),
PlaylistAdd(PlaylistName, Uri, Option<SongPosition>),
PlaylistClear(PlaylistName),
PlaylistDelete(PlaylistName, OneOrRange),
PlaylistLength(PlaylistName),
// TODO: which type of range?
PlaylistMove(PlaylistName, OneOrRange, SongPosition),
PlaylistMove(PlaylistName, Option<OneOrRange>, SongPosition),
Rename(PlaylistName, PlaylistName),
Rm(PlaylistName),
Save(PlaylistName, Option<SaveMode>),
@ -129,7 +129,7 @@ pub enum Request {
Rescan(Option<Uri>),
// -- Mount and Neighbor Commands -- //
Mount(Option<Path>, Option<Uri>),
Mount(Path, Uri),
Unmount(Path),
ListMounts,
ListNeighbors,
@ -159,12 +159,12 @@ pub enum Request {
Ping,
BinaryLimit(u64),
TagTypes,
TagTypesDisable(Vec<Tag>),
TagTypesEnable(Vec<Tag>),
TagTypesDisable(Vec<TagName>),
TagTypesEnable(Vec<TagName>),
TagTypesClear,
TagTypesAll,
TagTypesAvailable,
TagTypesReset(Vec<Tag>),
TagTypesReset(Vec<TagName>),
Protocol,
ProtocolDisable(Vec<Feature>),
ProtocolEnable(Vec<Feature>),
@ -410,16 +410,56 @@ impl Request {
ClearTagId::COMMAND => ClearTagId::parse_request(parts),
/* stored playlists */
ListPlaylist::COMMAND => ListPlaylist::parse_request(parts),
ListPlaylistInfo::COMMAND => ListPlaylistInfo::parse_request(parts),
SearchPlaylist::COMMAND => SearchPlaylist::parse_request(parts),
ListPlaylists::COMMAND => ListPlaylists::parse_request(parts),
Load::COMMAND => Load::parse_request(parts),
PlaylistAdd::COMMAND => PlaylistAdd::parse_request(parts),
PlaylistClear::COMMAND => PlaylistClear::parse_request(parts),
PlaylistDelete::COMMAND => PlaylistDelete::parse_request(parts),
PlaylistLength::COMMAND => PlaylistLength::parse_request(parts),
PlaylistMove::COMMAND => PlaylistMove::parse_request(parts),
Rename::COMMAND => Rename::parse_request(parts),
Rm::COMMAND => Rm::parse_request(parts),
Save::COMMAND => Save::parse_request(parts),
/* music database */
/* mounts and neighbors */
Mount::COMMAND => Mount::parse_request(parts),
Unmount::COMMAND => Unmount::parse_request(parts),
ListMounts::COMMAND => ListMounts::parse_request(parts),
ListNeighbors::COMMAND => ListNeighbors::parse_request(parts),
/* stickers */
/* connection settings */
Close::COMMAND => Close::parse_request(parts),
Kill::COMMAND => Kill::parse_request(parts),
Password::COMMAND => Password::parse_request(parts),
Ping::COMMAND => Ping::parse_request(parts),
BinaryLimit::COMMAND => BinaryLimit::parse_request(parts),
TagTypes::COMMAND => TagTypes::parse_request(parts),
TagTypesDisable::COMMAND => TagTypesDisable::parse_request(parts),
TagTypesEnable::COMMAND => TagTypesEnable::parse_request(parts),
TagTypesClear::COMMAND => TagTypesClear::parse_request(parts),
TagTypesAll::COMMAND => TagTypesAll::parse_request(parts),
TagTypesAvailable::COMMAND => TagTypesAvailable::parse_request(parts),
TagTypesReset::COMMAND => TagTypesReset::parse_request(parts),
Protocol::COMMAND => Protocol::parse_request(parts),
ProtocolDisable::COMMAND => ProtocolDisable::parse_request(parts),
ProtocolEnable::COMMAND => ProtocolEnable::parse_request(parts),
ProtocolClear::COMMAND => ProtocolClear::parse_request(parts),
ProtocolAll::COMMAND => ProtocolAll::parse_request(parts),
ProtocolAvailable::COMMAND => ProtocolAvailable::parse_request(parts),
/* partition commands */
Partition::COMMAND => Partition::parse_request(parts),
ListPartitions::COMMAND => ListPartitions::parse_request(parts),
NewPartition::COMMAND => NewPartition::parse_request(parts),
DelPartition::COMMAND => DelPartition::parse_request(parts),
MoveOutput::COMMAND => MoveOutput::parse_request(parts),
/* audio output devices */
DisableOutput::COMMAND => DisableOutput::parse_request(parts),
@ -429,6 +469,11 @@ impl Request {
OutputSet::COMMAND => OutputSet::parse_request(parts),
/* reflection */
Config::COMMAND => Config::parse_request(parts),
Commands::COMMAND => Commands::parse_request(parts),
NotCommands::COMMAND => NotCommands::parse_request(parts),
UrlHandlers::COMMAND => UrlHandlers::parse_request(parts),
Decoders::COMMAND => Decoders::parse_request(parts),
/* client to client */
Subscribe::COMMAND => Subscribe::parse_request(parts),