Files
empidee/src/commands.rs

287 lines
7.9 KiB
Rust

use crate::{Request, request_tokenizer::RequestTokenizer, response_tokenizer::ResponseAttributes};
mod audio_output_devices;
mod client_to_client;
mod connection_settings;
mod controlling_playback;
mod mounts_and_neighbors;
mod music_database;
mod partition_commands;
mod playback_options;
mod querying_mpd_status;
mod queue;
mod reflection;
mod stickers;
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 music_database::*;
pub use partition_commands::*;
pub use playback_options::*;
pub use querying_mpd_status::*;
pub use queue::*;
pub use reflection::*;
pub use stickers::*;
pub use stored_playlists::*;
/// A trait modelling the request/response pair of a single MPD command.
pub trait Command {
/// The request sent from the client to the server
type Request;
/// The response sent from the server to the client
type Response;
/// The command name used within the protocol
const COMMAND: &'static str;
/// Serialize the request into a string.
/// This should optimally produce an input that can be parsed by [`parse_request`]
fn serialize_request(&self, request: Self::Request) -> String;
/// Serialize the request into a bytestring.
fn serialize_request_to_bytes(&self, request: Self::Request) -> Vec<u8> {
self.serialize_request(request).into_bytes()
}
// fn serialize_response(&self) -> String;
// fn serialize_response_to_bytes(&self) -> Vec<u8> {
// self.serialize_response().into_bytes()
// }
/// Parse the request from its tokenized parts.
///
/// Note that this assumes only the parts after the command name are passed in, e.g.
///
/// ```ignore
/// arg1 "arg2 arg3"
/// ```
fn parse_request(parts: RequestTokenizer<'_>) -> Result<Self::Request, RequestParserError>;
/// Parse the raw request string into a request and the remaining unparsed string.
/// This assumes the raw string starts with the command name, e.g.
///
/// ```ignore
/// command_name arg1 "arg2 arg3"
/// ```
fn parse_raw_request(raw: &str) -> Result<(Self::Request, &str), RequestParserError> {
let (line, rest) = raw
.split_once('\n')
.ok_or(RequestParserError::UnexpectedEOF)?;
let mut tokenized = RequestTokenizer::new(line);
let command_name = tokenized
.next()
.ok_or(RequestParserError::SyntaxError(0, line.to_string()))?
.trim();
debug_assert!(command_name == Self::COMMAND);
Self::parse_request(tokenized).map(|req| (req, rest))
}
/// Parse the response from its tokenized parts.
/// See also [`parse_raw_response`].
fn parse_response(parts: ResponseAttributes<'_>) -> ResponseParserResult<'_, Self::Response>;
/// Parse the raw response string into a response.
fn parse_raw_response(raw: &str) -> ResponseParserResult<'_, Self::Response> {
Self::parse_response(ResponseAttributes::new(raw))
}
}
// pub type RequestParserResult<'a> = ;
#[derive(Debug, Clone, PartialEq)]
pub enum RequestParserError {
SyntaxError(u64, String),
MissingCommandListEnd(u64),
NestedCommandList(u64),
UnexpectedCommandListEnd(u64),
UnexpectedEOF,
MissingNewline,
}
pub type ResponseParserResult<'a, T> = Result<T, ResponseParserError<'a>>;
// TODO: should these be renamed to fit the mpd docs?
// "Attribute" instead of "Property"?
#[derive(Debug, Clone, PartialEq)]
pub enum ResponseParserError<'a> {
/// A property was expected to be present in the response, but was not found.
MissingProperty(&'a str),
// TODO: change name to UnexpectedPropertyEncoding
/// An expected property was found in the response, but its encoding was not as expected. (e.g. binary instead of text)
UnexpectedPropertyType(&'a str, &'a str),
/// A property was found in the response that was not expected.
UnexpectedProperty(&'a str),
/// A property was found multiple times in the response, but was only expected once.
DuplicateProperty(&'a str),
/// The property value is parsable, but the value is invalid or nonsensical.
InvalidProperty(&'a str, &'a str),
/// Could not parse the response due to a syntax error.
SyntaxError(u64, &'a str),
/// Response ended early, while more properties were expected.
UnexpectedEOF,
// MissingNewline,
}
/*******************/
pub const COMMAND_NAMES: &[&str] = &[
// Audio output devices
DisableOutput::COMMAND,
EnableOutput::COMMAND,
Outputs::COMMAND,
OutputSet::COMMAND,
ToggleOutput::COMMAND,
// Client to client
Channels::COMMAND,
ReadMessages::COMMAND,
SendMessage::COMMAND,
Subscribe::COMMAND,
Unsubscribe::COMMAND,
// Connection settings
BinaryLimit::COMMAND,
Close::COMMAND,
Kill::COMMAND,
Password::COMMAND,
Ping::COMMAND,
Protocol::COMMAND,
ProtocolAll::COMMAND,
ProtocolAvailable::COMMAND,
ProtocolClear::COMMAND,
ProtocolDisable::COMMAND,
ProtocolEnable::COMMAND,
TagTypes::COMMAND,
TagTypesAll::COMMAND,
TagTypesAvailable::COMMAND,
TagTypesClear::COMMAND,
TagTypesDisable::COMMAND,
TagTypesEnable::COMMAND,
TagTypesReset::COMMAND,
// Controlling playback
Next::COMMAND,
Pause::COMMAND,
Play::COMMAND,
PlayId::COMMAND,
Previous::COMMAND,
Seek::COMMAND,
SeekCur::COMMAND,
SeekId::COMMAND,
Stop::COMMAND,
// Mounts and neighbors
ListMounts::COMMAND,
ListNeighbors::COMMAND,
Mount::COMMAND,
Unmount::COMMAND,
// Music database
AlbumArt::COMMAND,
Count::COMMAND,
Find::COMMAND,
FindAdd::COMMAND,
GetFingerprint::COMMAND,
List::COMMAND,
ListAll::COMMAND,
ListAllInfo::COMMAND,
ListFiles::COMMAND,
LsInfo::COMMAND,
ReadComments::COMMAND,
ReadPicture::COMMAND,
Rescan::COMMAND,
Search::COMMAND,
SearchAdd::COMMAND,
SearchAddPl::COMMAND,
SearchCount::COMMAND,
Update::COMMAND,
// Partition commands
DelPartition::COMMAND,
ListPartitions::COMMAND,
MoveOutput::COMMAND,
NewPartition::COMMAND,
Partition::COMMAND,
// Playback options
Consume::COMMAND,
Crossfade::COMMAND,
GetVol::COMMAND,
MixRampDb::COMMAND,
MixRampDelay::COMMAND,
Random::COMMAND,
Repeat::COMMAND,
ReplayGainMode::COMMAND,
ReplayGainStatus::COMMAND,
SetVol::COMMAND,
Single::COMMAND,
Volume::COMMAND,
// Querying mpd status
ClearError::COMMAND,
CurrentSong::COMMAND,
Idle::COMMAND,
Stats::COMMAND,
Status::COMMAND,
// Queue
Add::COMMAND,
AddId::COMMAND,
AddTagId::COMMAND,
Clear::COMMAND,
ClearTagId::COMMAND,
Delete::COMMAND,
DeleteId::COMMAND,
Move::COMMAND,
MoveId::COMMAND,
Playlist::COMMAND,
PlaylistFind::COMMAND,
PlaylistId::COMMAND,
PlaylistInfo::COMMAND,
PlaylistSearch::COMMAND,
PlChanges::COMMAND,
PlChangesPosId::COMMAND,
Prio::COMMAND,
PrioId::COMMAND,
RangeId::COMMAND,
Shuffle::COMMAND,
Swap::COMMAND,
SwapId::COMMAND,
// Reflection
Commands::COMMAND,
Config::COMMAND,
Decoders::COMMAND,
NotCommands::COMMAND,
UrlHandlers::COMMAND,
// Stickers
StickerDec::COMMAND,
StickerDelete::COMMAND,
StickerFind::COMMAND,
StickerGet::COMMAND,
StickerInc::COMMAND,
StickerList::COMMAND,
StickerSet::COMMAND,
StickerNames::COMMAND,
StickerNamesTypes::COMMAND,
StickerTypes::COMMAND,
// Stored playlists
ListPlaylist::COMMAND,
ListPlaylistInfo::COMMAND,
ListPlaylists::COMMAND,
Load::COMMAND,
PlaylistAdd::COMMAND,
PlaylistClear::COMMAND,
PlaylistDelete::COMMAND,
PlaylistLength::COMMAND,
PlaylistMove::COMMAND,
Rename::COMMAND,
Rm::COMMAND,
Save::COMMAND,
SearchPlaylist::COMMAND,
];