418 lines
17 KiB
Rust
418 lines
17 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::common::types::*;
|
|
|
|
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>),
|
|
// TODO: relative mode
|
|
AddId(String, Option<SongPosition>),
|
|
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<Sort>, Option<WindowRange>),
|
|
PlaylistId(SongId),
|
|
PlaylistInfo(Option<OneOrRange>),
|
|
PlaylistSearch(Filter, Option<Sort>, Option<WindowRange>),
|
|
// TODO: which type of range?
|
|
PlChanges(PlaylistVersion, Option<WindowRange>),
|
|
// TODO: which type of range?
|
|
PlChangesPosId(PlaylistVersion, Option<WindowRange>),
|
|
// TODO: which type of range?
|
|
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),
|
|
// TODO: which type of range?
|
|
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, Option<Filter>, Vec<GroupType>, Option<WindowRange>),
|
|
#[deprecated]
|
|
ListAll(Option<Uri>),
|
|
#[deprecated]
|
|
ListAllInfo(Option<Uri>),
|
|
ListFiles(Option<Uri>),
|
|
LsInfo(Option<Uri>),
|
|
ReadComments(Uri),
|
|
ReadPicture(Uri, Offset),
|
|
Search(Filter, Option<Sort>, Option<WindowRange>),
|
|
SearchAdd(
|
|
Filter,
|
|
Option<Sort>,
|
|
Option<WindowRange>,
|
|
Option<SongPosition>,
|
|
),
|
|
SearchAddPl(
|
|
PlaylistName,
|
|
Filter,
|
|
Option<Sort>,
|
|
Option<WindowRange>,
|
|
Option<SongPosition>,
|
|
),
|
|
SearchCount(Filter, Option<GroupType>),
|
|
Update(Option<Uri>),
|
|
Rescan(Option<Uri>),
|
|
|
|
// -- Mount and Neighbor Commands -- //
|
|
Mount(MountPath, Uri),
|
|
Unmount(MountPath),
|
|
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,
|
|
String,
|
|
Option<Sort>,
|
|
Option<WindowRange>,
|
|
),
|
|
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 Request {
|
|
|
|
// 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) -> Result<Self::Request, RequestParserError> {
|
|
// let (line, rest) = raw
|
|
// .split_once('\n')
|
|
// .ok_or(RequestParserError::UnexpectedEOF)?;
|
|
// let mut parts = RequestTokenizer::new(line);
|
|
|
|
// 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!(),
|
|
// }
|
|
// }
|
|
}
|