287 lines
7.9 KiB
Rust
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,
|
|
];
|