Lines
0 %
Functions
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::common::*;
use crate::commands::*;
use crate::filter::Filter;
// TODO: SingleLineString
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum Request {
CommandList(Vec<Request>),
// -- Query Commands -- //
ClearError,
CurrentSong,
Idle(Option<Vec<SubSystem>>),
Status,
Stats,
// -- Playback Commands -- //
Consume(BoolOrOneshot),
Crossfade(Seconds),
MixRampDb(f32),
MixRampDelay(Seconds),
Random(bool),
Repeat(bool),
SetVol(VolumeValue),
GetVol,
Single(BoolOrOneshot),
ReplayGainMode(ReplayGainModeMode),
ReplayGainStatus,
Volume(VolumeValue),
// -- Playback Control Commands -- //
Next,
Pause(Option<bool>),
Play(SongPosition),
PlayId(SongId),
Previous,
Seek(SongPosition, TimeWithFractions),
SeekId(SongId, TimeWithFractions),
SeekCur(SeekMode, TimeWithFractions),
Stop,
// -- Queue Commands -- //
// TODO: relative mode
Add(String, Option<SongPosition>),
AddId(String, Option<SongPosition>),
Clear,
Delete(OneOrRange),
DeleteId(SongId),
// TODO: account for relative moves
Move(OneOrRange, AbsouluteRelativeSongPosition),
MoveId(SongId, AbsouluteRelativeSongPosition),
Playlist,
PlaylistFind(Filter, Option<Sort>, Option<WindowRange>),
PlaylistId(SongId),
PlaylistInfo(Option<OneOrRange>),
PlaylistSearch(Filter, Option<Sort>, Option<WindowRange>),
// TODO: which type of range?
PlChanges(Version, Option<WindowRange>),
PlChangesPosId(Version, Option<WindowRange>),
Prio(Priority, WindowRange),
PrioId(Priority, Vec<SongId>),
RangeId(SongId, TimeInterval),
Shuffle(Option<OneOrRange>),
Swap(SongPosition, SongPosition),
SwapId(SongId, SongId),
AddTagId(SongId, TagName, TagValue),
ClearTagId(SongId, TagName),
// -- Stored Playlist Commands -- //
ListPlaylist(PlaylistName, Option<WindowRange>),
ListPlaylistInfo(PlaylistName, Option<WindowRange>),
SearchPlaylist(PlaylistName, Filter, Option<WindowRange>),
ListPlaylists,
Load(PlaylistName, Option<WindowRange>, Option<SongPosition>),
PlaylistAdd(PlaylistName, Uri, Option<SongPosition>),
PlaylistClear(PlaylistName),
PlaylistDelete(PlaylistName, OneOrRange),
PlaylistLength(PlaylistName),
PlaylistMove(PlaylistName, Option<OneOrRange>, SongPosition),
Rename(PlaylistName, PlaylistName),
Rm(PlaylistName),
Save(PlaylistName, Option<SaveMode>),
// -- Music Database Commands -- //
AlbumArt(Uri, Offset),
Count(Filter, Option<GroupType>),
GetFingerprint(Uri),
Find(Filter, Option<Sort>, Option<WindowRange>),
FindAdd(
Filter,
Option<Sort>,
Option<WindowRange>,
Option<SongPosition>,
),
List(TagName, Filter, Option<GroupType>),
#[deprecated]
ListAll(Option<Uri>),
ListAllInfo(Option<Uri>),
ListFiles(Option<Uri>),
LsInfo(Option<Uri>),
ReadComments(Uri),
ReadPicture(Uri, Offset),
Search(Filter, Option<Sort>, Option<WindowRange>),
SearchAdd(
SearchAddPl(
PlaylistName,
SearchCount(Filter, Option<GroupType>),
Update(Option<Uri>),
Rescan(Option<Uri>),
// -- Mount and Neighbor Commands -- //
Mount(Path, Uri),
Unmount(Path),
ListMounts,
ListNeighbors,
// -- Sticker Commands -- //
StickerGet(StickerType, Uri, String),
StickerSet(StickerType, Uri, String, String),
StickerInc(StickerType, Uri, String, usize),
StickerDec(StickerType, Uri, String, usize),
StickerDelete(StickerType, Uri, String),
StickerList(StickerType, Uri),
StickerFind(StickerType, Uri, String, Option<Sort>, Option<WindowRange>),
StickerFindValue(
StickerType,
Uri,
String,
StickerNames,
StickerTypes,
StickerNamesTypes(Option<StickerType>),
// -- Connection Commands -- //
Close,
Kill,
Password(String),
Ping,
BinaryLimit(u64),
TagTypes,
TagTypesDisable(Vec<TagName>),
TagTypesEnable(Vec<TagName>),
TagTypesClear,
TagTypesAll,
TagTypesAvailable,
TagTypesReset(Vec<TagName>),
Protocol,
ProtocolDisable(Vec<Feature>),
ProtocolEnable(Vec<Feature>),
ProtocolClear,
ProtocolAll,
ProtocolAvailable,
// -- Partition Commands -- //
Partition(PartitionName),
ListPartitions,
NewPartition(PartitionName),
DelPartition(PartitionName),
MoveOutput(String),
// -- Audio Output Commands -- //
DisableOutput(AudioOutputId),
EnableOutput(AudioOutputId),
ToggleOutput(AudioOutputId),
Outputs,
OutputSet(AudioOutputId, String, String),
// -- Reflection Commands -- //
Config,
Commands,
NotCommands,
UrlHandlers,
Decoders,
// -- Client to Client Commands -- //
Subscribe(ChannelName),
Unsubscribe(ChannelName),
Channels,
ReadMessages,
SendMessage(ChannelName, String),
}
// impl From<Request> for Vec<u8> {
// fn from(val: Request) -> Self {
// todo!()
// }
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum SaveMode {
Create,
Append,
Replace,
impl FromStr for SaveMode {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"create" => Ok(Self::Create),
"append" => Ok(Self::Append),
"replace" => Ok(Self::Replace),
_ => Err(()),
pub enum SeekMode {
Relative,
RelativeReverse,
Absolute,
pub enum ReplayGainModeMode {
Off,
Track,
Album,
Auto,
impl FromStr for ReplayGainModeMode {
"off" => Ok(Self::Off),
"track" => Ok(Self::Track),
"album" => Ok(Self::Album),
"auto" => Ok(Self::Auto),
pub struct VolumeValue(u32);
impl VolumeValue {
pub fn new(volume: u32) -> Result<Self, ()> {
match volume {
0..=100 => Ok(Self(volume)),
impl From<VolumeValue> for u32 {
fn from(val: VolumeValue) -> Self {
val.0
impl FromStr for VolumeValue {
let volume = s.parse().map_err(|_| ())?;
VolumeValue::new(volume)
// TODO: fill out
pub enum GroupType {
Artist,
AlbumArtist,
Date,
Genre,
Composer,
Performer,
Conductor,
Comment,
Disc,
Filename,
Any,
impl FromStr for GroupType {
"artist" => Ok(Self::Artist),
"albumartist" => Ok(Self::AlbumArtist),
"date" => Ok(Self::Date),
"genre" => Ok(Self::Genre),
"composer" => Ok(Self::Composer),
"performer" => Ok(Self::Performer),
"conductor" => Ok(Self::Conductor),
"comment" => Ok(Self::Comment),
"disc" => Ok(Self::Disc),
"filename" => Ok(Self::Filename),
"any" => Ok(Self::Any),
// trait RequestCommand {
// // The command name used within the protocol
// const COMMAND: &'static str;
// // A function to parse the remaining parts of the command, split by whitespace
// fn parse(parts: SplitWhitespace<'_>) -> RequestParserResponse<'_>;
// type RequestParserResponse<'a> = Result<(Request, &'a str), RequestParserError>;
// pub enum RequestParserError {
// SyntaxError(u64, String),
// MissingCommandListEnd(u64),
// NestedCommandList(u64),
// UnexpectedCommandListEnd(u64),
// UnexpectedEOF,
// MissingNewline,
// TODO: upon encountering an error, there should be a function that lets you skip to the next OK,
// and continue execution. Maybe "parse_next_or_skip(&str) -> RequestParserResponse", which
// could skip stuff internally? Or do we want to report back the error with the entire command
// and let the library user decide what to do?
impl Request {
pub fn parse_next(raw: &str) -> RequestParserResult<'_> {
let (line, rest) = raw
.split_once('\n')
.ok_or(RequestParserError::UnexpectedEOF)?;
let mut parts = line.split_whitespace();
match parts
.next()
.ok_or(RequestParserError::SyntaxError(0, line.to_string()))?
.trim()
{
"command_list_begin" => {
let mut commands = Vec::new();
let mut i = 1;
loop {
i += 1;
let (line, rest) = rest
.ok_or(RequestParserError::MissingCommandListEnd(i))?;
match line.trim() {
return Err(RequestParserError::NestedCommandList(i))
"command_list_end" => {
return Ok((Request::CommandList(commands), rest));
input => {
let (command, _) = Request::parse_next(input)?;
commands.push(command);
"command_list_end" => Err(RequestParserError::UnexpectedCommandListEnd(0)),
/* querying mpd status */
ClearError::COMMAND => ClearError::parse_request(parts),
CurrentSong::COMMAND => CurrentSong::parse_request(parts),
Idle::COMMAND => Idle::parse_request(parts),
Status::COMMAND => Status::parse_request(parts),
Stats::COMMAND => Stats::parse_request(parts),
/* playback options */
Consume::COMMAND => Consume::parse_request(parts),
Crossfade::COMMAND => Crossfade::parse_request(parts),
MixRampDb::COMMAND => MixRampDb::parse_request(parts),
MixRampDelay::COMMAND => MixRampDelay::parse_request(parts),
Random::COMMAND => Random::parse_request(parts),
Repeat::COMMAND => Repeat::parse_request(parts),
SetVol::COMMAND => SetVol::parse_request(parts),
GetVol::COMMAND => GetVol::parse_request(parts),
Single::COMMAND => Single::parse_request(parts),
ReplayGainMode::COMMAND => ReplayGainMode::parse_request(parts),
ReplayGainStatus::COMMAND => ReplayGainStatus::parse_request(parts),
Volume::COMMAND => Volume::parse_request(parts),
/* playback control */
Next::COMMAND => Next::parse_request(parts),
Pause::COMMAND => Pause::parse_request(parts),
Play::COMMAND => Play::parse_request(parts),
PlayId::COMMAND => PlayId::parse_request(parts),
Previous::COMMAND => Previous::parse_request(parts),
Seek::COMMAND => Seek::parse_request(parts),
SeekId::COMMAND => SeekId::parse_request(parts),
SeekCur::COMMAND => SeekCur::parse_request(parts),
Stop::COMMAND => Stop::parse_request(parts),
/* queue */
Add::COMMAND => Add::parse_request(parts),
AddId::COMMAND => AddId::parse_request(parts),
Clear::COMMAND => Clear::parse_request(parts),
Delete::COMMAND => Delete::parse_request(parts),
DeleteId::COMMAND => DeleteId::parse_request(parts),
Move::COMMAND => Move::parse_request(parts),
MoveId::COMMAND => MoveId::parse_request(parts),
Playlist::COMMAND => Playlist::parse_request(parts),
PlaylistFind::COMMAND => PlaylistFind::parse_request(parts),
PlaylistId::COMMAND => PlaylistId::parse_request(parts),
PlaylistInfo::COMMAND => PlaylistInfo::parse_request(parts),
PlaylistSearch::COMMAND => PlaylistSearch::parse_request(parts),
PlChanges::COMMAND => PlChanges::parse_request(parts),
PlChangesPosId::COMMAND => PlChangesPosId::parse_request(parts),
Prio::COMMAND => Prio::parse_request(parts),
PrioId::COMMAND => PrioId::parse_request(parts),
RangeId::COMMAND => RangeId::parse_request(parts),
Shuffle::COMMAND => Shuffle::parse_request(parts),
Swap::COMMAND => Swap::parse_request(parts),
SwapId::COMMAND => SwapId::parse_request(parts),
AddTagId::COMMAND => AddTagId::parse_request(parts),
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 */
AlbumArt::COMMAND => AlbumArt::parse_request(parts),
Count::COMMAND => Count::parse_request(parts),
GetFingerprint::COMMAND => GetFingerprint::parse_request(parts),
Find::COMMAND => Find::parse_request(parts),
FindAdd::COMMAND => FindAdd::parse_request(parts),
List::COMMAND => List::parse_request(parts),
ListAll::COMMAND => ListAll::parse_request(parts),
ListAllInfo::COMMAND => ListAllInfo::parse_request(parts),
ListFiles::COMMAND => ListFiles::parse_request(parts),
LsInfo::COMMAND => LsInfo::parse_request(parts),
ReadComments::COMMAND => ReadComments::parse_request(parts),
ReadPicture::COMMAND => ReadPicture::parse_request(parts),
Search::COMMAND => Search::parse_request(parts),
SearchAdd::COMMAND => SearchAdd::parse_request(parts),
SearchAddPl::COMMAND => SearchAddPl::parse_request(parts),
SearchCount::COMMAND => SearchCount::parse_request(parts),
Update::COMMAND => Update::parse_request(parts),
Rescan::COMMAND => Rescan::parse_request(parts),
/* 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 */
StickerGet::COMMAND => StickerGet::parse_request(parts),
StickerSet::COMMAND => StickerSet::parse_request(parts),
StickerInc::COMMAND => StickerInc::parse_request(parts),
StickerDec::COMMAND => StickerDec::parse_request(parts),
StickerDelete::COMMAND => StickerDelete::parse_request(parts),
StickerList::COMMAND => StickerList::parse_request(parts),
StickerFind::COMMAND => StickerFind::parse_request(parts),
StickerNames::COMMAND => StickerNames::parse_request(parts),
StickerTypes::COMMAND => StickerTypes::parse_request(parts),
StickerNamesTypes::COMMAND => StickerNamesTypes::parse_request(parts),
/* 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),
EnableOutput::COMMAND => EnableOutput::parse_request(parts),
ToggleOutput::COMMAND => ToggleOutput::parse_request(parts),
Outputs::COMMAND => Outputs::parse_request(parts),
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),
Unsubscribe::COMMAND => Unsubscribe::parse_request(parts),
Channels::COMMAND => Channels::parse_request(parts),
ReadMessages::COMMAND => ReadMessages::parse_request(parts),
SendMessage::COMMAND => SendMessage::parse_request(parts),
_ => unimplemented!(),