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), // -- Query Commands -- // ClearError, CurrentSong, Idle(Option>), 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), Play(SongPosition), PlayId(SongId), Previous, Seek(SongPosition, TimeWithFractions), SeekId(SongId, TimeWithFractions), SeekCur(SeekMode, TimeWithFractions), Stop, // -- Queue Commands -- // // TODO: relative mode Add(String, Option), // TODO: relative mode AddId(String, Option), Clear, Delete(OneOrRange), DeleteId(SongId), // TODO: account for relative moves Move(OneOrRange, AbsouluteRelativeSongPosition), // TODO: account for relative moves MoveId(SongId, AbsouluteRelativeSongPosition), Playlist, PlaylistFind(Filter, Option, Option), PlaylistId(SongId), PlaylistInfo(Option), PlaylistSearch(Filter, Option, Option), // TODO: which type of range? PlChanges(Version, Option), // TODO: which type of range? PlChangesPosId(Version, Option), // TODO: which type of range? Prio(Priority, WindowRange), PrioId(Priority, Vec), RangeId(SongId, TimeInterval), Shuffle(Option), Swap(SongPosition, SongPosition), SwapId(SongId, SongId), AddTagId(SongId, TagName, TagValue), ClearTagId(SongId, TagName), // -- Stored Playlist Commands -- // ListPlaylist(PlaylistName, Option), ListPlaylistInfo(PlaylistName, Option), SearchPlaylist(PlaylistName, Filter, Option), ListPlaylists, Load(PlaylistName, Option, Option), PlaylistAdd(PlaylistName, Uri, Option), PlaylistClear(PlaylistName), PlaylistDelete(PlaylistName, OneOrRange), PlaylistLength(PlaylistName), // TODO: which type of range? PlaylistMove(PlaylistName, Option, SongPosition), Rename(PlaylistName, PlaylistName), Rm(PlaylistName), Save(PlaylistName, Option), // -- Music Database Commands -- // AlbumArt(Uri, Offset), Count(Filter, Option), GetFingerprint(Uri), Find(Filter, Option, Option), FindAdd( Filter, Option, Option, Option, ), List(TagName, Filter, Option), #[deprecated] ListAll(Option), #[deprecated] ListAllInfo(Option), ListFiles(Option), LsInfo(Option), ReadComments(Uri), ReadPicture(Uri, Offset), Search(Filter, Option, Option), SearchAdd( Filter, Option, Option, Option, ), SearchAddPl( PlaylistName, Filter, Option, Option, Option, ), SearchCount(Filter, Option), Update(Option), Rescan(Option), // -- 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, Option), StickerFindValue( StickerType, Uri, String, String, Option, Option, ), StickerNames, StickerTypes, StickerNamesTypes(Option), // -- Connection Commands -- // Close, Kill, Password(String), Ping, BinaryLimit(u64), TagTypes, TagTypesDisable(Vec), TagTypesEnable(Vec), TagTypesClear, TagTypesAll, TagTypesAvailable, TagTypesReset(Vec), Protocol, ProtocolDisable(Vec), ProtocolEnable(Vec), 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), } // 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 .split_once('\n') .ok_or(RequestParserError::MissingCommandListEnd(i))?; match line.trim() { "command_list_begin" => { 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!(), } } }