From abc1fcc01b91a86fb30a9f58ccfdef1092ffcefa Mon Sep 17 00:00:00 2001 From: h7x4 Date: Mon, 24 Nov 2025 14:18:30 +0900 Subject: [PATCH] Implement a proper request tokenizer --- src/commands.rs | 168 ++++++++++++-- .../audio_output_devices/disableoutput.rs | 3 +- .../audio_output_devices/enableoutput.rs | 3 +- src/commands/audio_output_devices/outputs.rs | 11 +- .../audio_output_devices/outputset.rs | 3 +- .../audio_output_devices/toggleoutput.rs | 3 +- src/commands/client_to_client/channels.rs | 3 +- src/commands/client_to_client/readmessages.rs | 3 +- src/commands/client_to_client/sendmessage.rs | 3 +- src/commands/client_to_client/subscribe.rs | 3 +- src/commands/client_to_client/unsubscribe.rs | 3 +- .../connection_settings/binary_limit.rs | 11 +- src/commands/connection_settings/close.rs | 7 +- src/commands/connection_settings/kill.rs | 7 +- src/commands/connection_settings/password.rs | 11 +- src/commands/connection_settings/ping.rs | 7 +- src/commands/connection_settings/protocol.rs | 3 +- .../connection_settings/protocol_all.rs | 3 +- .../connection_settings/protocol_available.rs | 3 +- .../connection_settings/protocol_clear.rs | 3 +- .../connection_settings/protocol_disable.rs | 3 +- .../connection_settings/protocol_enable.rs | 3 +- src/commands/connection_settings/tag_types.rs | 11 +- .../connection_settings/tag_types_all.rs | 3 +- .../tag_types_available.rs | 3 +- .../connection_settings/tag_types_clear.rs | 3 +- .../connection_settings/tag_types_disable.rs | 3 +- .../connection_settings/tag_types_enable.rs | 3 +- .../connection_settings/tag_types_reset.rs | 3 +- src/commands/controlling_playback/next.rs | 7 +- src/commands/controlling_playback/pause.rs | 11 +- src/commands/controlling_playback/play.rs | 3 +- src/commands/controlling_playback/playid.rs | 3 +- src/commands/controlling_playback/previous.rs | 7 +- src/commands/controlling_playback/seek.rs | 3 +- src/commands/controlling_playback/seekcur.rs | 3 +- src/commands/controlling_playback/seekid.rs | 3 +- src/commands/controlling_playback/stop.rs | 7 +- .../mounts_and_neighbors/listmounts.rs | 3 +- .../mounts_and_neighbors/listneighbors.rs | 3 +- src/commands/mounts_and_neighbors/mount.rs | 3 +- src/commands/mounts_and_neighbors/unmount.rs | 3 +- src/commands/music_database/albumart.rs | 3 +- src/commands/music_database/count.rs | 12 +- src/commands/music_database/find.rs | 12 +- src/commands/music_database/findadd.rs | 12 +- src/commands/music_database/getfingerprint.rs | 3 +- src/commands/music_database/list.rs | 12 +- src/commands/music_database/listall.rs | 3 +- src/commands/music_database/listallinfo.rs | 3 +- src/commands/music_database/listfiles.rs | 3 +- src/commands/music_database/lsinfo.rs | 3 +- src/commands/music_database/readcomments.rs | 3 +- src/commands/music_database/readpicture.rs | 3 +- src/commands/music_database/rescan.rs | 3 +- src/commands/music_database/search.rs | 12 +- src/commands/music_database/searchadd.rs | 12 +- src/commands/music_database/searchaddpl.rs | 12 +- src/commands/music_database/searchcount.rs | 12 +- src/commands/music_database/update.rs | 3 +- .../partition_commands/delpartition.rs | 3 +- .../partition_commands/listpartitions.rs | 3 +- src/commands/partition_commands/moveoutput.rs | 11 +- .../partition_commands/newpartition.rs | 3 +- src/commands/partition_commands/partition.rs | 3 +- src/commands/playback_options/consume.rs | 3 +- src/commands/playback_options/crossfade.rs | 3 +- src/commands/playback_options/getvol.rs | 3 +- src/commands/playback_options/mixrampdb.rs | 11 +- src/commands/playback_options/mixrampdelay.rs | 3 +- src/commands/playback_options/random.rs | 11 +- src/commands/playback_options/repeat.rs | 11 +- .../playback_options/replay_gain_mode.rs | 3 +- .../playback_options/replay_gain_status.rs | 3 +- src/commands/playback_options/setvol.rs | 3 +- src/commands/playback_options/single.rs | 3 +- src/commands/playback_options/volume.rs | 3 +- .../querying_mpd_status/clearerror.rs | 7 +- .../querying_mpd_status/currentsong.rs | 7 +- src/commands/querying_mpd_status/idle.rs | 5 +- src/commands/querying_mpd_status/stats.rs | 11 +- src/commands/querying_mpd_status/status.rs | 3 +- src/commands/queue/add.rs | 3 +- src/commands/queue/addid.rs | 3 +- src/commands/queue/addtagid.rs | 3 +- src/commands/queue/clear.rs | 7 +- src/commands/queue/cleartagid.rs | 3 +- src/commands/queue/delete.rs | 3 +- src/commands/queue/deleteid.rs | 3 +- src/commands/queue/move_.rs | 3 +- src/commands/queue/moveid.rs | 3 +- src/commands/queue/playlist.rs | 3 +- src/commands/queue/playlistfind.rs | 12 +- src/commands/queue/playlistid.rs | 3 +- src/commands/queue/playlistinfo.rs | 3 +- src/commands/queue/playlistsearch.rs | 12 +- src/commands/queue/plchanges.rs | 3 +- src/commands/queue/plchangesposid.rs | 3 +- src/commands/queue/prio.rs | 3 +- src/commands/queue/prioid.rs | 3 +- src/commands/queue/rangeid.rs | 3 +- src/commands/queue/shuffle.rs | 3 +- src/commands/queue/swap.rs | 3 +- src/commands/queue/swapid.rs | 3 +- src/commands/reflection/commands.rs | 11 +- src/commands/reflection/config.rs | 11 +- src/commands/reflection/decoders.rs | 11 +- src/commands/reflection/not_commands.rs | 11 +- src/commands/reflection/url_handlers.rs | 11 +- src/commands/stickers/sticker_dec.rs | 3 +- src/commands/stickers/sticker_delete.rs | 3 +- src/commands/stickers/sticker_find.rs | 3 +- src/commands/stickers/sticker_get.rs | 3 +- src/commands/stickers/sticker_inc.rs | 3 +- src/commands/stickers/sticker_list.rs | 3 +- src/commands/stickers/sticker_set.rs | 3 +- src/commands/stickers/stickernames.rs | 11 +- src/commands/stickers/stickernamestypes.rs | 3 +- src/commands/stickers/stickertypes.rs | 11 +- src/commands/stored_playlists/listplaylist.rs | 3 +- .../stored_playlists/listplaylistinfo.rs | 3 +- .../stored_playlists/listplaylists.rs | 7 +- src/commands/stored_playlists/load.rs | 3 +- src/commands/stored_playlists/playlistadd.rs | 3 +- .../stored_playlists/playlistclear.rs | 3 +- .../stored_playlists/playlistdelete.rs | 3 +- .../stored_playlists/playlistlength.rs | 3 +- src/commands/stored_playlists/playlistmove.rs | 3 +- src/commands/stored_playlists/rename.rs | 11 +- src/commands/stored_playlists/rm.rs | 3 +- src/commands/stored_playlists/save.rs | 3 +- .../stored_playlists/searchplaylist.rs | 12 +- src/filter.rs | 212 ++++++++---------- src/lib.rs | 1 + src/request.rs | 3 +- src/request_tokenizer.rs | 186 +++++++++++++++ 136 files changed, 883 insertions(+), 365 deletions(-) create mode 100644 src/request_tokenizer.rs diff --git a/src/commands.rs b/src/commands.rs index e97a926..4e45553 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -1,9 +1,6 @@ -use std::{ - collections::{HashMap, HashSet}, - str::SplitWhitespace, -}; +use std::collections::{HashMap, HashSet}; -use crate::Request; +use crate::{Request, request_tokenizer::RequestTokenizer}; mod audio_output_devices; mod client_to_client; @@ -53,27 +50,23 @@ pub trait Command { // self.serialize_response().into_bytes() // } - // TODO: `parse_request` should be using a more custom splitter, that can handle - // quoted strings and escape characters. This is what mpd uses to provide arguments - // with spaces and whatnot. - // A function to parse the remaining parts of the command, split by whitespace - fn parse_request(parts: SplitWhitespace<'_>) -> RequestParserResult<'_>; + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_>; fn parse_raw_request(raw: &str) -> RequestParserResult<'_> { let (line, rest) = raw .split_once('\n') .ok_or(RequestParserError::UnexpectedEOF)?; - let mut parts = line.split_whitespace(); + let mut tokenized = RequestTokenizer::new(line); - let command_name = parts + let command_name = tokenized .next() .ok_or(RequestParserError::SyntaxError(0, line.to_string()))? .trim(); debug_assert!(command_name == Self::COMMAND); - Self::parse_request(parts).map(|(req, _)| (req, rest)) + Self::parse_request(tokenized).map(|(req, _)| (req, rest)) } fn parse_response( @@ -381,6 +374,155 @@ pub(crate) use get_next_property; pub(crate) use get_optional_property; pub(crate) use get_property; +/*******************/ + +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, +]; + #[cfg(test)] mod tests { use super::*; diff --git a/src/commands/audio_output_devices/disableoutput.rs b/src/commands/audio_output_devices/disableoutput.rs index 3fb7770..7f0de09 100644 --- a/src/commands/audio_output_devices/disableoutput.rs +++ b/src/commands/audio_output_devices/disableoutput.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for DisableOutput { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; debug_assert!(parts.next().is_none()); diff --git a/src/commands/audio_output_devices/enableoutput.rs b/src/commands/audio_output_devices/enableoutput.rs index 2084b68..361c6bf 100644 --- a/src/commands/audio_output_devices/enableoutput.rs +++ b/src/commands/audio_output_devices/enableoutput.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for EnableOutput { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; debug_assert!(parts.next().is_none()); diff --git a/src/commands/audio_output_devices/outputs.rs b/src/commands/audio_output_devices/outputs.rs index 2512aa4..bc9d875 100644 --- a/src/commands/audio_output_devices/outputs.rs +++ b/src/commands/audio_output_devices/outputs.rs @@ -2,9 +2,12 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct Outputs; @@ -29,7 +32,7 @@ impl Command for Outputs { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Outputs, "")) } diff --git a/src/commands/audio_output_devices/outputset.rs b/src/commands/audio_output_devices/outputset.rs index 69f82a7..f83aed9 100644 --- a/src/commands/audio_output_devices/outputset.rs +++ b/src/commands/audio_output_devices/outputset.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -32,7 +33,7 @@ impl Command for OutputSet { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let attribute_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let attribute_value = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/audio_output_devices/toggleoutput.rs b/src/commands/audio_output_devices/toggleoutput.rs index df5bf4a..5d8dfc7 100644 --- a/src/commands/audio_output_devices/toggleoutput.rs +++ b/src/commands/audio_output_devices/toggleoutput.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for ToggleOutput { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; debug_assert!(parts.next().is_none()); diff --git a/src/commands/client_to_client/channels.rs b/src/commands/client_to_client/channels.rs index 468cc5c..68c1612 100644 --- a/src/commands/client_to_client/channels.rs +++ b/src/commands/client_to_client/channels.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -24,7 +25,7 @@ impl Command for Channels { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Channels, "")) diff --git a/src/commands/client_to_client/readmessages.rs b/src/commands/client_to_client/readmessages.rs index acd46a0..e26fd0a 100644 --- a/src/commands/client_to_client/readmessages.rs +++ b/src/commands/client_to_client/readmessages.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -27,7 +28,7 @@ impl Command for ReadMessages { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ReadMessages, "")) diff --git a/src/commands/client_to_client/sendmessage.rs b/src/commands/client_to_client/sendmessage.rs index 004b121..61b1fff 100644 --- a/src/commands/client_to_client/sendmessage.rs +++ b/src/commands/client_to_client/sendmessage.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -25,7 +26,7 @@ impl Command for SendMessage { format!("{} {} {}", Self::COMMAND, request.channel, request.message) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let channel = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; // TODO: SplitWhitespace::remainder() is unstable, use when stable diff --git a/src/commands/client_to_client/subscribe.rs b/src/commands/client_to_client/subscribe.rs index db8c283..caf1f6d 100644 --- a/src/commands/client_to_client/subscribe.rs +++ b/src/commands/client_to_client/subscribe.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Subscribe { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; debug_assert!(parts.next().is_none()); diff --git a/src/commands/client_to_client/unsubscribe.rs b/src/commands/client_to_client/unsubscribe.rs index d72b249..71a5be2 100644 --- a/src/commands/client_to_client/unsubscribe.rs +++ b/src/commands/client_to_client/unsubscribe.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Unsubscribe { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; debug_assert!(parts.next().is_none()); diff --git a/src/commands/connection_settings/binary_limit.rs b/src/commands/connection_settings/binary_limit.rs index 27e292c..399a6de 100644 --- a/src/commands/connection_settings/binary_limit.rs +++ b/src/commands/connection_settings/binary_limit.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct BinaryLimit; @@ -14,7 +17,7 @@ impl Command for BinaryLimit { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let limit = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let limit = limit .parse() diff --git a/src/commands/connection_settings/close.rs b/src/commands/connection_settings/close.rs index a86cc42..0a0a222 100644 --- a/src/commands/connection_settings/close.rs +++ b/src/commands/connection_settings/close.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Close; @@ -13,7 +14,7 @@ impl Command for Close { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Close, "")) } diff --git a/src/commands/connection_settings/kill.rs b/src/commands/connection_settings/kill.rs index 29d704d..cecb1f7 100644 --- a/src/commands/connection_settings/kill.rs +++ b/src/commands/connection_settings/kill.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Kill; @@ -13,7 +14,7 @@ impl Command for Kill { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Kill, "")) } diff --git a/src/commands/connection_settings/password.rs b/src/commands/connection_settings/password.rs index 6735daf..0dea258 100644 --- a/src/commands/connection_settings/password.rs +++ b/src/commands/connection_settings/password.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct Password; @@ -14,7 +17,7 @@ impl Command for Password { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let password = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/connection_settings/ping.rs b/src/commands/connection_settings/ping.rs index 8448919..88e04f7 100644 --- a/src/commands/connection_settings/ping.rs +++ b/src/commands/connection_settings/ping.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Ping; @@ -13,7 +14,7 @@ impl Command for Ping { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Ping, "")) } diff --git a/src/commands/connection_settings/protocol.rs b/src/commands/connection_settings/protocol.rs index 8017601..8e0155d 100644 --- a/src/commands/connection_settings/protocol.rs +++ b/src/commands/connection_settings/protocol.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, @@ -18,7 +19,7 @@ impl Command for Protocol { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Protocol, "")) } diff --git a/src/commands/connection_settings/protocol_all.rs b/src/commands/connection_settings/protocol_all.rs index a33a672..54978b0 100644 --- a/src/commands/connection_settings/protocol_all.rs +++ b/src/commands/connection_settings/protocol_all.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError}, }; @@ -14,7 +15,7 @@ impl Command for ProtocolAll { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ProtocolAll, "")) } diff --git a/src/commands/connection_settings/protocol_available.rs b/src/commands/connection_settings/protocol_available.rs index f6b798c..feefcf7 100644 --- a/src/commands/connection_settings/protocol_available.rs +++ b/src/commands/connection_settings/protocol_available.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, @@ -18,7 +19,7 @@ impl Command for ProtocolAvailable { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ProtocolAvailable, "")) } diff --git a/src/commands/connection_settings/protocol_clear.rs b/src/commands/connection_settings/protocol_clear.rs index 8c7ea42..0f18ce3 100644 --- a/src/commands/connection_settings/protocol_clear.rs +++ b/src/commands/connection_settings/protocol_clear.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError}, }; @@ -14,7 +15,7 @@ impl Command for ProtocolClear { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ProtocolClear, "")) } diff --git a/src/commands/connection_settings/protocol_disable.rs b/src/commands/connection_settings/protocol_disable.rs index 83ebf9f..6a4bfb8 100644 --- a/src/commands/connection_settings/protocol_disable.rs +++ b/src/commands/connection_settings/protocol_disable.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for ProtocolDisable { format!("{} {}", Self::COMMAND, request.join(" ")) } - fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { return Err(RequestParserError::UnexpectedEOF); diff --git a/src/commands/connection_settings/protocol_enable.rs b/src/commands/connection_settings/protocol_enable.rs index 4686021..961bb42 100644 --- a/src/commands/connection_settings/protocol_enable.rs +++ b/src/commands/connection_settings/protocol_enable.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for ProtocolEnable { format!("{} {}", Self::COMMAND, request.join(" ")) } - fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { return Err(RequestParserError::UnexpectedEOF); diff --git a/src/commands/connection_settings/tag_types.rs b/src/commands/connection_settings/tag_types.rs index 283bea5..ee82801 100644 --- a/src/commands/connection_settings/tag_types.rs +++ b/src/commands/connection_settings/tag_types.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct TagTypes; @@ -16,7 +19,7 @@ impl Command for TagTypes { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::TagTypes, "")) } diff --git a/src/commands/connection_settings/tag_types_all.rs b/src/commands/connection_settings/tag_types_all.rs index 0509d5d..ab636df 100644 --- a/src/commands/connection_settings/tag_types_all.rs +++ b/src/commands/connection_settings/tag_types_all.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError}, }; @@ -14,7 +15,7 @@ impl Command for TagTypesAll { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::TagTypesAll, "")) } diff --git a/src/commands/connection_settings/tag_types_available.rs b/src/commands/connection_settings/tag_types_available.rs index 73bd6c2..58da073 100644 --- a/src/commands/connection_settings/tag_types_available.rs +++ b/src/commands/connection_settings/tag_types_available.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, @@ -18,7 +19,7 @@ impl Command for TagTypesAvailable { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::TagTypesAvailable, "")) } diff --git a/src/commands/connection_settings/tag_types_clear.rs b/src/commands/connection_settings/tag_types_clear.rs index f1b0782..4741b71 100644 --- a/src/commands/connection_settings/tag_types_clear.rs +++ b/src/commands/connection_settings/tag_types_clear.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError}, }; @@ -14,7 +15,7 @@ impl Command for TagTypesClear { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::TagTypesClear, "")) } diff --git a/src/commands/connection_settings/tag_types_disable.rs b/src/commands/connection_settings/tag_types_disable.rs index 17bf455..24ee777 100644 --- a/src/commands/connection_settings/tag_types_disable.rs +++ b/src/commands/connection_settings/tag_types_disable.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for TagTypesDisable { format!("{} {}", Self::COMMAND, request.join(" ")) } - fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { return Err(RequestParserError::UnexpectedEOF); diff --git a/src/commands/connection_settings/tag_types_enable.rs b/src/commands/connection_settings/tag_types_enable.rs index 4eb5366..d5a1133 100644 --- a/src/commands/connection_settings/tag_types_enable.rs +++ b/src/commands/connection_settings/tag_types_enable.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for TagTypesEnable { format!("{} {}", Self::COMMAND, request.join(" ")) } - fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { return Err(RequestParserError::UnexpectedEOF); diff --git a/src/commands/connection_settings/tag_types_reset.rs b/src/commands/connection_settings/tag_types_reset.rs index beae9e3..33a39f3 100644 --- a/src/commands/connection_settings/tag_types_reset.rs +++ b/src/commands/connection_settings/tag_types_reset.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for TagTypesReset { format!("{} {}", Self::COMMAND, request.join(" ")) } - fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { return Err(RequestParserError::UnexpectedEOF); diff --git a/src/commands/controlling_playback/next.rs b/src/commands/controlling_playback/next.rs index e6afa7f..eeae993 100644 --- a/src/commands/controlling_playback/next.rs +++ b/src/commands/controlling_playback/next.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Next; @@ -13,7 +14,7 @@ impl Command for Next { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Next, "")) } diff --git a/src/commands/controlling_playback/pause.rs b/src/commands/controlling_playback/pause.rs index 3ba559f..79da4ac 100644 --- a/src/commands/controlling_playback/pause.rs +++ b/src/commands/controlling_playback/pause.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct Pause; @@ -18,7 +21,7 @@ impl Command for Pause { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let result = match parts.next() { Some("0") => Ok((Request::Pause(Some(false)), "")), Some("1") => Ok((Request::Pause(Some(true)), "")), diff --git a/src/commands/controlling_playback/play.rs b/src/commands/controlling_playback/play.rs index 8e20f48..82a0407 100644 --- a/src/commands/controlling_playback/play.rs +++ b/src/commands/controlling_playback/play.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Play { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songpos = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/controlling_playback/playid.rs b/src/commands/controlling_playback/playid.rs index e06c8ee..dbe908c 100644 --- a/src/commands/controlling_playback/playid.rs +++ b/src/commands/controlling_playback/playid.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for PlayId { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/controlling_playback/previous.rs b/src/commands/controlling_playback/previous.rs index 82eeac6..9958ee1 100644 --- a/src/commands/controlling_playback/previous.rs +++ b/src/commands/controlling_playback/previous.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Previous; @@ -13,7 +14,7 @@ impl Command for Previous { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Previous, "")) } diff --git a/src/commands/controlling_playback/seek.rs b/src/commands/controlling_playback/seek.rs index 57f54e1..e5966b4 100644 --- a/src/commands/controlling_playback/seek.rs +++ b/src/commands/controlling_playback/seek.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -25,7 +26,7 @@ impl Command for Seek { format!("{} {} {}", Self::COMMAND, request.songpos, request.time) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songpos = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/controlling_playback/seekcur.rs b/src/commands/controlling_playback/seekcur.rs index e914119..b2fa2ea 100644 --- a/src/commands/controlling_playback/seekcur.rs +++ b/src/commands/controlling_playback/seekcur.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -32,7 +33,7 @@ impl Command for SeekCur { format!("{} {}", Self::COMMAND, time_str) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let time_raw = match parts.next() { Some(t) => t, None => return Err(RequestParserError::UnexpectedEOF), diff --git a/src/commands/controlling_playback/seekid.rs b/src/commands/controlling_playback/seekid.rs index 25785bb..1fe3eb0 100644 --- a/src/commands/controlling_playback/seekid.rs +++ b/src/commands/controlling_playback/seekid.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -25,7 +26,7 @@ impl Command for SeekId { format!("{} {} {}", Self::COMMAND, request.songid, request.time) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/controlling_playback/stop.rs b/src/commands/controlling_playback/stop.rs index d4f10bf..1ecf637 100644 --- a/src/commands/controlling_playback/stop.rs +++ b/src/commands/controlling_playback/stop.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Stop; @@ -13,7 +14,7 @@ impl Command for Stop { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Stop, "")) } diff --git a/src/commands/mounts_and_neighbors/listmounts.rs b/src/commands/mounts_and_neighbors/listmounts.rs index 8fcff75..4848d39 100644 --- a/src/commands/mounts_and_neighbors/listmounts.rs +++ b/src/commands/mounts_and_neighbors/listmounts.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, @@ -16,7 +17,7 @@ impl Command for ListMounts { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ListMounts, "")) } diff --git a/src/commands/mounts_and_neighbors/listneighbors.rs b/src/commands/mounts_and_neighbors/listneighbors.rs index 0791dbc..544243e 100644 --- a/src/commands/mounts_and_neighbors/listneighbors.rs +++ b/src/commands/mounts_and_neighbors/listneighbors.rs @@ -2,6 +2,7 @@ use std::collections::HashMap; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, @@ -20,7 +21,7 @@ impl Command for ListNeighbors { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ListNeighbors, "")) } diff --git a/src/commands/mounts_and_neighbors/mount.rs b/src/commands/mounts_and_neighbors/mount.rs index 99e2034..e502462 100644 --- a/src/commands/mounts_and_neighbors/mount.rs +++ b/src/commands/mounts_and_neighbors/mount.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -24,7 +25,7 @@ impl Command for Mount { format!("{} {} {}", Self::COMMAND, request.path, request.uri) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let path = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/mounts_and_neighbors/unmount.rs b/src/commands/mounts_and_neighbors/unmount.rs index 7235d0a..42373de 100644 --- a/src/commands/mounts_and_neighbors/unmount.rs +++ b/src/commands/mounts_and_neighbors/unmount.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -18,7 +19,7 @@ impl Command for Unmount { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let path = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/music_database/albumart.rs b/src/commands/music_database/albumart.rs index d27c590..e2835c5 100644 --- a/src/commands/music_database/albumart.rs +++ b/src/commands/music_database/albumart.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, get_property, @@ -33,7 +34,7 @@ impl Command for AlbumArt { format!("{} {} {}", Self::COMMAND, request.uri, request.offset) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, None => return Err(RequestParserError::UnexpectedEOF), diff --git a/src/commands/music_database/count.rs b/src/commands/music_database/count.rs index 63ce253..0985dc6 100644 --- a/src/commands/music_database/count.rs +++ b/src/commands/music_database/count.rs @@ -3,12 +3,13 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, }, common::GroupType, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct Count; @@ -38,8 +39,13 @@ impl Command for Count { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let group = if let Some("group") = parts.next() { let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/music_database/find.rs b/src/commands/music_database/find.rs index a5da1be..edd9d82 100644 --- a/src/commands/music_database/find.rs +++ b/src/commands/music_database/find.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct Find; @@ -37,8 +38,13 @@ impl Command for Find { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window = parts.next(); let mut sort = None; diff --git a/src/commands/music_database/findadd.rs b/src/commands/music_database/findadd.rs index 8e5799c..4aa8803 100644 --- a/src/commands/music_database/findadd.rs +++ b/src/commands/music_database/findadd.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{SongPosition, Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct FindAdd; @@ -38,8 +39,13 @@ impl Command for FindAdd { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window_or_position = parts.next(); let mut sort = None; diff --git a/src/commands/music_database/getfingerprint.rs b/src/commands/music_database/getfingerprint.rs index ab55355..60d0896 100644 --- a/src/commands/music_database/getfingerprint.rs +++ b/src/commands/music_database/getfingerprint.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, @@ -26,7 +27,7 @@ impl Command for GetFingerprint { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = uri .parse() diff --git a/src/commands/music_database/list.rs b/src/commands/music_database/list.rs index d70af24..80308b0 100644 --- a/src/commands/music_database/list.rs +++ b/src/commands/music_database/list.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, common::{GroupType, TagName}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct List; @@ -33,14 +34,19 @@ impl Command for List { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let tagname = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let tagname = tagname .parse() .map_err(|_| RequestParserError::SyntaxError(1, tagname.to_owned()))?; // TODO: This should be optional - let filter = parse_filter(&mut parts)?; + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let group = if let Some("group") = parts.next() { let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/music_database/listall.rs b/src/commands/music_database/listall.rs index 3560272..69020c5 100644 --- a/src/commands/music_database/listall.rs +++ b/src/commands/music_database/listall.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -24,7 +25,7 @@ impl Command for ListAll { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts .next() .map(|s| { diff --git a/src/commands/music_database/listallinfo.rs b/src/commands/music_database/listallinfo.rs index 65ae560..f413e93 100644 --- a/src/commands/music_database/listallinfo.rs +++ b/src/commands/music_database/listallinfo.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -25,7 +26,7 @@ impl Command for ListAllInfo { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts .next() .map(|s| { diff --git a/src/commands/music_database/listfiles.rs b/src/commands/music_database/listfiles.rs index daf4bd5..0eed11d 100644 --- a/src/commands/music_database/listfiles.rs +++ b/src/commands/music_database/listfiles.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -24,7 +25,7 @@ impl Command for ListFiles { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts .next() .map(|s| { diff --git a/src/commands/music_database/lsinfo.rs b/src/commands/music_database/lsinfo.rs index e9bf165..45ffa30 100644 --- a/src/commands/music_database/lsinfo.rs +++ b/src/commands/music_database/lsinfo.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -30,7 +31,7 @@ impl Command for LsInfo { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts .next() .map(|s| { diff --git a/src/commands/music_database/readcomments.rs b/src/commands/music_database/readcomments.rs index c259cd4..27a8fc9 100644 --- a/src/commands/music_database/readcomments.rs +++ b/src/commands/music_database/readcomments.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -21,7 +22,7 @@ impl Command for ReadComments { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = uri .parse() diff --git a/src/commands/music_database/readpicture.rs b/src/commands/music_database/readpicture.rs index 363850d..90feff6 100644 --- a/src/commands/music_database/readpicture.rs +++ b/src/commands/music_database/readpicture.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, get_optional_property, get_property, @@ -34,7 +35,7 @@ impl Command for ReadPicture { format!("{} {} {}", Self::COMMAND, request.uri, request.offset) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, None => return Err(RequestParserError::UnexpectedEOF), diff --git a/src/commands/music_database/rescan.rs b/src/commands/music_database/rescan.rs index 6038af3..a686b6e 100644 --- a/src/commands/music_database/rescan.rs +++ b/src/commands/music_database/rescan.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, @@ -29,7 +30,7 @@ impl Command for Rescan { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts.next().map(|s| s.to_string()); debug_assert!(parts.next().is_none()); diff --git a/src/commands/music_database/search.rs b/src/commands/music_database/search.rs index d553a64..0bacf19 100644 --- a/src/commands/music_database/search.rs +++ b/src/commands/music_database/search.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct Search; @@ -37,8 +38,13 @@ impl Command for Search { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window = parts.next(); let mut sort = None; diff --git a/src/commands/music_database/searchadd.rs b/src/commands/music_database/searchadd.rs index 11ba7e4..995ba67 100644 --- a/src/commands/music_database/searchadd.rs +++ b/src/commands/music_database/searchadd.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{SongPosition, Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct SearchAdd; @@ -38,8 +39,13 @@ impl Command for SearchAdd { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window_or_position = parts.next(); let mut sort = None; diff --git a/src/commands/music_database/searchaddpl.rs b/src/commands/music_database/searchaddpl.rs index 8648f06..a864500 100644 --- a/src/commands/music_database/searchaddpl.rs +++ b/src/commands/music_database/searchaddpl.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{PlaylistName, SongPosition, Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct SearchAddPl; @@ -44,13 +45,18 @@ impl Command for SearchAddPl { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? .to_string(); - let filter = parse_filter(&mut parts)?; + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window_or_position = parts.next(); let mut sort = None; diff --git a/src/commands/music_database/searchcount.rs b/src/commands/music_database/searchcount.rs index 44c3c1b..c3815c2 100644 --- a/src/commands/music_database/searchcount.rs +++ b/src/commands/music_database/searchcount.rs @@ -3,12 +3,13 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, }, common::GroupType, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct SearchCount; @@ -38,8 +39,13 @@ impl Command for SearchCount { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let group = if let Some("group") = parts.next() { let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/music_database/update.rs b/src/commands/music_database/update.rs index 9609433..dfaa7f3 100644 --- a/src/commands/music_database/update.rs +++ b/src/commands/music_database/update.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, @@ -29,7 +30,7 @@ impl Command for Update { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = parts.next().map(|s| s.to_string()); debug_assert!(parts.next().is_none()); diff --git a/src/commands/partition_commands/delpartition.rs b/src/commands/partition_commands/delpartition.rs index 163e31c..2b3f699 100644 --- a/src/commands/partition_commands/delpartition.rs +++ b/src/commands/partition_commands/delpartition.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for DelPartition { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let partition = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/partition_commands/listpartitions.rs b/src/commands/partition_commands/listpartitions.rs index 2eda57f..80d04a1 100644 --- a/src/commands/partition_commands/listpartitions.rs +++ b/src/commands/partition_commands/listpartitions.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -19,7 +20,7 @@ impl Command for ListPartitions { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ListPartitions, "")) } diff --git a/src/commands/partition_commands/moveoutput.rs b/src/commands/partition_commands/moveoutput.rs index 69c78d9..0b8ef12 100644 --- a/src/commands/partition_commands/moveoutput.rs +++ b/src/commands/partition_commands/moveoutput.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct MoveOutput; @@ -14,7 +17,7 @@ impl Command for MoveOutput { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let output_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/partition_commands/newpartition.rs b/src/commands/partition_commands/newpartition.rs index 895438d..23b7ab0 100644 --- a/src/commands/partition_commands/newpartition.rs +++ b/src/commands/partition_commands/newpartition.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for NewPartition { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let partition = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/partition_commands/partition.rs b/src/commands/partition_commands/partition.rs index 4fa70a0..004e9cc 100644 --- a/src/commands/partition_commands/partition.rs +++ b/src/commands/partition_commands/partition.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Partition { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let partition = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/playback_options/consume.rs b/src/commands/playback_options/consume.rs index 6a1aa82..d3bc1c1 100644 --- a/src/commands/playback_options/consume.rs +++ b/src/commands/playback_options/consume.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for Consume { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some(s) => crate::common::BoolOrOneshot::from_str(s) .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, diff --git a/src/commands/playback_options/crossfade.rs b/src/commands/playback_options/crossfade.rs index 34837aa..0583b87 100644 --- a/src/commands/playback_options/crossfade.rs +++ b/src/commands/playback_options/crossfade.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Crossfade { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let seconds = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/playback_options/getvol.rs b/src/commands/playback_options/getvol.rs index 4f5e5ca..a6f0c00 100644 --- a/src/commands/playback_options/getvol.rs +++ b/src/commands/playback_options/getvol.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, @@ -19,7 +20,7 @@ impl Command for GetVol { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::GetVol, "")) } diff --git a/src/commands/playback_options/mixrampdb.rs b/src/commands/playback_options/mixrampdb.rs index cc7a19b..66447a7 100644 --- a/src/commands/playback_options/mixrampdb.rs +++ b/src/commands/playback_options/mixrampdb.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct MixRampDb; @@ -14,7 +17,7 @@ impl Command for MixRampDb { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let db = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/playback_options/mixrampdelay.rs b/src/commands/playback_options/mixrampdelay.rs index b913fb6..4fb4b78 100644 --- a/src/commands/playback_options/mixrampdelay.rs +++ b/src/commands/playback_options/mixrampdelay.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for MixRampDelay { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let seconds = match parts.next() { Some(s) => s .parse::() diff --git a/src/commands/playback_options/random.rs b/src/commands/playback_options/random.rs index 649e0fe..740f680 100644 --- a/src/commands/playback_options/random.rs +++ b/src/commands/playback_options/random.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct Random; @@ -15,7 +18,7 @@ impl Command for Random { format!("{} {}", Self::COMMAND, state) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some("0") => false, Some("1") => true, diff --git a/src/commands/playback_options/repeat.rs b/src/commands/playback_options/repeat.rs index e5d5dd6..62ddcba 100644 --- a/src/commands/playback_options/repeat.rs +++ b/src/commands/playback_options/repeat.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct Repeat; @@ -15,7 +18,7 @@ impl Command for Repeat { format!("{} {}", Self::COMMAND, state) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some("0") => false, Some("1") => true, diff --git a/src/commands/playback_options/replay_gain_mode.rs b/src/commands/playback_options/replay_gain_mode.rs index aaaf903..8999aa3 100644 --- a/src/commands/playback_options/replay_gain_mode.rs +++ b/src/commands/playback_options/replay_gain_mode.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for ReplayGainMode { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let mode = match parts.next() { Some(s) => ReplayGainModeMode::from_str(s) .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, diff --git a/src/commands/playback_options/replay_gain_status.rs b/src/commands/playback_options/replay_gain_status.rs index 9496e90..8b4eee9 100644 --- a/src/commands/playback_options/replay_gain_status.rs +++ b/src/commands/playback_options/replay_gain_status.rs @@ -3,6 +3,7 @@ use std::{collections::HashMap, str::FromStr}; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, get_property, @@ -26,7 +27,7 @@ impl Command for ReplayGainStatus { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ReplayGainStatus, "")) } diff --git a/src/commands/playback_options/setvol.rs b/src/commands/playback_options/setvol.rs index 0c84a38..c5af048 100644 --- a/src/commands/playback_options/setvol.rs +++ b/src/commands/playback_options/setvol.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for SetVol { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let volume = match parts.next() { Some(s) => VolumeValue::from_str(s) .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, diff --git a/src/commands/playback_options/single.rs b/src/commands/playback_options/single.rs index ff76c2d..cfe8a15 100644 --- a/src/commands/playback_options/single.rs +++ b/src/commands/playback_options/single.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for Single { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some(s) => crate::common::BoolOrOneshot::from_str(s) .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, diff --git a/src/commands/playback_options/volume.rs b/src/commands/playback_options/volume.rs index d0cc192..1181a07 100644 --- a/src/commands/playback_options/volume.rs +++ b/src/commands/playback_options/volume.rs @@ -1,6 +1,7 @@ use std::str::FromStr; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -19,7 +20,7 @@ impl Command for Volume { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let change = match parts.next() { Some(s) => VolumeValue::from_str(s) .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, diff --git a/src/commands/querying_mpd_status/clearerror.rs b/src/commands/querying_mpd_status/clearerror.rs index dacef29..f82f993 100644 --- a/src/commands/querying_mpd_status/clearerror.rs +++ b/src/commands/querying_mpd_status/clearerror.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; /// Clears the current error message in status (this is also accomplished by any command that starts playback) @@ -14,7 +15,7 @@ impl Command for ClearError { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ClearError, "")) diff --git a/src/commands/querying_mpd_status/currentsong.rs b/src/commands/querying_mpd_status/currentsong.rs index 4dcffd1..1ce180a 100644 --- a/src/commands/querying_mpd_status/currentsong.rs +++ b/src/commands/querying_mpd_status/currentsong.rs @@ -1,7 +1,8 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; /// Displays the song info of the current song (same song that is identified in status) @@ -19,7 +20,7 @@ impl Command for CurrentSong { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::CurrentSong, "")) diff --git a/src/commands/querying_mpd_status/idle.rs b/src/commands/querying_mpd_status/idle.rs index 4626729..5edc479 100644 --- a/src/commands/querying_mpd_status/idle.rs +++ b/src/commands/querying_mpd_status/idle.rs @@ -1,5 +1,6 @@ -use std::str::{FromStr, SplitWhitespace}; +use std::str::FromStr; +use crate::request_tokenizer::RequestTokenizer; use crate::common::SubSystem; use crate::commands::{ @@ -29,7 +30,7 @@ impl Command for Idle { } } - fn parse_request(mut parts: SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let result = parts .next() .map_or(Ok((Request::Idle(None), "")), |subsystems| { diff --git a/src/commands/querying_mpd_status/stats.rs b/src/commands/querying_mpd_status/stats.rs index 01b23de..4d7438f 100644 --- a/src/commands/querying_mpd_status/stats.rs +++ b/src/commands/querying_mpd_status/stats.rs @@ -2,9 +2,12 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - get_and_parse_optional_property, get_and_parse_property, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + get_and_parse_optional_property, get_and_parse_property, + }, }; pub struct Stats; @@ -29,7 +32,7 @@ impl Command for Stats { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Stats, "")) diff --git a/src/commands/querying_mpd_status/status.rs b/src/commands/querying_mpd_status/status.rs index 3fd38f0..e245876 100644 --- a/src/commands/querying_mpd_status/status.rs +++ b/src/commands/querying_mpd_status/status.rs @@ -3,6 +3,7 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; +use crate::request_tokenizer::RequestTokenizer; use crate::common::{Audio, BoolOrOneshot, SongId, SongPosition}; use crate::commands::{ @@ -167,7 +168,7 @@ impl Command for Status { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Status, "")) diff --git a/src/commands/queue/add.rs b/src/commands/queue/add.rs index 6297b95..ce11c9e 100644 --- a/src/commands/queue/add.rs +++ b/src/commands/queue/add.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -28,7 +29,7 @@ impl Command for Add { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, None => return Err(RequestParserError::UnexpectedEOF), diff --git a/src/commands/queue/addid.rs b/src/commands/queue/addid.rs index 0f45a11..885ddd3 100644 --- a/src/commands/queue/addid.rs +++ b/src/commands/queue/addid.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_next_and_parse_property, @@ -33,7 +34,7 @@ impl Command for AddId { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, None => return Err(RequestParserError::UnexpectedEOF), diff --git a/src/commands/queue/addtagid.rs b/src/commands/queue/addtagid.rs index 3b96006..20cedf3 100644 --- a/src/commands/queue/addtagid.rs +++ b/src/commands/queue/addtagid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -32,7 +33,7 @@ impl Command for AddTagId { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid .parse() diff --git a/src/commands/queue/clear.rs b/src/commands/queue/clear.rs index 1aea67a..9a2e93a 100644 --- a/src/commands/queue/clear.rs +++ b/src/commands/queue/clear.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct Clear; @@ -13,7 +14,7 @@ impl Command for Clear { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Clear, "")) } diff --git a/src/commands/queue/cleartagid.rs b/src/commands/queue/cleartagid.rs index 2af44fb..089e3ee 100644 --- a/src/commands/queue/cleartagid.rs +++ b/src/commands/queue/cleartagid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -25,7 +26,7 @@ impl Command for ClearTagId { format!("{} {} {}", Self::COMMAND, request.songid, request.tag_name) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid .parse() diff --git a/src/commands/queue/delete.rs b/src/commands/queue/delete.rs index 30d9c8b..ba6a9ef 100644 --- a/src/commands/queue/delete.rs +++ b/src/commands/queue/delete.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -19,7 +20,7 @@ impl Command for Delete { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let pos_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let one_or_range = OneOrRange::from_str(pos_or_range) .map_err(|_| RequestParserError::SyntaxError(0, pos_or_range.to_string()))?; diff --git a/src/commands/queue/deleteid.rs b/src/commands/queue/deleteid.rs index 30292ae..8df9afb 100644 --- a/src/commands/queue/deleteid.rs +++ b/src/commands/queue/deleteid.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -17,7 +18,7 @@ impl Command for DeleteId { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id .parse() diff --git a/src/commands/queue/move_.rs b/src/commands/queue/move_.rs index b1c67c7..645020e 100644 --- a/src/commands/queue/move_.rs +++ b/src/commands/queue/move_.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -25,7 +26,7 @@ impl Command for Move { format!("{} {} {}", Self::COMMAND, request.from_or_range, request.to) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let from_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let from_or_range = from_or_range .parse() diff --git a/src/commands/queue/moveid.rs b/src/commands/queue/moveid.rs index 96e3bb9..73d72a9 100644 --- a/src/commands/queue/moveid.rs +++ b/src/commands/queue/moveid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -25,7 +26,7 @@ impl Command for MoveId { format!("{} {} {}", Self::COMMAND, request.id, request.to) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id .parse() diff --git a/src/commands/queue/playlist.rs b/src/commands/queue/playlist.rs index fa4f829..6893287 100644 --- a/src/commands/queue/playlist.rs +++ b/src/commands/queue/playlist.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError}, }; @@ -14,7 +15,7 @@ impl Command for Playlist { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Playlist, "")) } diff --git a/src/commands/queue/playlistfind.rs b/src/commands/queue/playlistfind.rs index 565ee2c..6e40905 100644 --- a/src/commands/queue/playlistfind.rs +++ b/src/commands/queue/playlistfind.rs @@ -2,11 +2,12 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct PlaylistFind; @@ -34,8 +35,13 @@ impl Command for PlaylistFind { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window = parts.next(); let mut sort = None; diff --git a/src/commands/queue/playlistid.rs b/src/commands/queue/playlistid.rs index 7752b7d..00f9a18 100644 --- a/src/commands/queue/playlistid.rs +++ b/src/commands/queue/playlistid.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -17,7 +18,7 @@ impl Command for PlaylistId { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id .parse() diff --git a/src/commands/queue/playlistinfo.rs b/src/commands/queue/playlistinfo.rs index 8bae5a7..df2d1f1 100644 --- a/src/commands/queue/playlistinfo.rs +++ b/src/commands/queue/playlistinfo.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -20,7 +21,7 @@ impl Command for PlaylistInfo { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let one_or_range = parts .next() .map(|s| { diff --git a/src/commands/queue/playlistsearch.rs b/src/commands/queue/playlistsearch.rs index 6547a3c..a1bb34b 100644 --- a/src/commands/queue/playlistsearch.rs +++ b/src/commands/queue/playlistsearch.rs @@ -2,11 +2,12 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{Sort, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct PlaylistSearch; @@ -34,8 +35,13 @@ impl Command for PlaylistSearch { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let filter = parse_filter(&mut parts)?; + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let mut sort_or_window = parts.next(); let mut sort = None; diff --git a/src/commands/queue/plchanges.rs b/src/commands/queue/plchanges.rs index ec3918f..b8cffe0 100644 --- a/src/commands/queue/plchanges.rs +++ b/src/commands/queue/plchanges.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -28,7 +29,7 @@ impl Command for PlChanges { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let version = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let version = version .parse() diff --git a/src/commands/queue/plchangesposid.rs b/src/commands/queue/plchangesposid.rs index c0fc12f..2b329df 100644 --- a/src/commands/queue/plchangesposid.rs +++ b/src/commands/queue/plchangesposid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -28,7 +29,7 @@ impl Command for PlChangesPosId { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let version = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let version = version .parse() diff --git a/src/commands/queue/prio.rs b/src/commands/queue/prio.rs index 568e046..e62ec55 100644 --- a/src/commands/queue/prio.rs +++ b/src/commands/queue/prio.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -25,7 +26,7 @@ impl Command for Prio { format!("{} {} {}", Self::COMMAND, request.prio, request.window) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let prio = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let prio = prio .parse() diff --git a/src/commands/queue/prioid.rs b/src/commands/queue/prioid.rs index e202015..7f46b82 100644 --- a/src/commands/queue/prioid.rs +++ b/src/commands/queue/prioid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -31,7 +32,7 @@ impl Command for PrioId { format!("{} {} {}", Self::COMMAND, request.prio, songids) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let prio = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let prio = prio .parse() diff --git a/src/commands/queue/rangeid.rs b/src/commands/queue/rangeid.rs index a42f45f..0db55be 100644 --- a/src/commands/queue/rangeid.rs +++ b/src/commands/queue/rangeid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -30,7 +31,7 @@ impl Command for RangeId { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid .parse() diff --git a/src/commands/queue/shuffle.rs b/src/commands/queue/shuffle.rs index 45a12a2..aff89eb 100644 --- a/src/commands/queue/shuffle.rs +++ b/src/commands/queue/shuffle.rs @@ -1,5 +1,6 @@ use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -20,7 +21,7 @@ impl Command for Shuffle { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let range = parts .next() .map(|range| { diff --git a/src/commands/queue/swap.rs b/src/commands/queue/swap.rs index 407fc78..47b73e2 100644 --- a/src/commands/queue/swap.rs +++ b/src/commands/queue/swap.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -30,7 +31,7 @@ impl Command for Swap { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songpos1 = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songpos1 = songpos1 .parse() diff --git a/src/commands/queue/swapid.rs b/src/commands/queue/swapid.rs index 9b091c1..d7ea100 100644 --- a/src/commands/queue/swapid.rs +++ b/src/commands/queue/swapid.rs @@ -2,6 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::{ Request, + request_tokenizer::RequestTokenizer, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, @@ -25,7 +26,7 @@ impl Command for SwapId { format!("{} {} {}", Self::COMMAND, request.songid1, request.songid2) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let songid1 = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid1 = songid1 .parse() diff --git a/src/commands/reflection/commands.rs b/src/commands/reflection/commands.rs index 1d1bb20..ea413ef 100644 --- a/src/commands/reflection/commands.rs +++ b/src/commands/reflection/commands.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct Commands; @@ -16,7 +19,7 @@ impl Command for Commands { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Commands, "")) } diff --git a/src/commands/reflection/config.rs b/src/commands/reflection/config.rs index 71376c4..743871a 100644 --- a/src/commands/reflection/config.rs +++ b/src/commands/reflection/config.rs @@ -2,9 +2,12 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - get_and_parse_property, get_property, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + get_and_parse_property, get_property, + }, }; pub struct Config; @@ -25,7 +28,7 @@ impl Command for Config { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Config, "")) } diff --git a/src/commands/reflection/decoders.rs b/src/commands/reflection/decoders.rs index 5129a08..1751f75 100644 --- a/src/commands/reflection/decoders.rs +++ b/src/commands/reflection/decoders.rs @@ -1,8 +1,11 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct Decoders; @@ -25,7 +28,7 @@ impl Command for Decoders { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::Decoders, "")) } diff --git a/src/commands/reflection/not_commands.rs b/src/commands/reflection/not_commands.rs index 21b6b6e..0742f51 100644 --- a/src/commands/reflection/not_commands.rs +++ b/src/commands/reflection/not_commands.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct NotCommands; @@ -16,7 +19,7 @@ impl Command for NotCommands { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::NotCommands, "")) } diff --git a/src/commands/reflection/url_handlers.rs b/src/commands/reflection/url_handlers.rs index 5c8b44a..61480cc 100644 --- a/src/commands/reflection/url_handlers.rs +++ b/src/commands/reflection/url_handlers.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct UrlHandlers; @@ -16,7 +19,7 @@ impl Command for UrlHandlers { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::UrlHandlers, "")) } diff --git a/src/commands/stickers/sticker_dec.rs b/src/commands/stickers/sticker_dec.rs index b572a65..af1a10c 100644 --- a/src/commands/stickers/sticker_dec.rs +++ b/src/commands/stickers/sticker_dec.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -34,7 +35,7 @@ impl Command for StickerDec { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_delete.rs b/src/commands/stickers/sticker_delete.rs index bf1ef8f..14c59cf 100644 --- a/src/commands/stickers/sticker_delete.rs +++ b/src/commands/stickers/sticker_delete.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -32,7 +33,7 @@ impl Command for StickerDelete { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_find.rs b/src/commands/stickers/sticker_find.rs index c64af11..68c5a6f 100644 --- a/src/commands/stickers/sticker_find.rs +++ b/src/commands/stickers/sticker_find.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -50,7 +51,7 @@ impl Command for StickerFind { cmd } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_get.rs b/src/commands/stickers/sticker_get.rs index 3fc41c7..6ab59f9 100644 --- a/src/commands/stickers/sticker_get.rs +++ b/src/commands/stickers/sticker_get.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_next_property, @@ -34,7 +35,7 @@ impl Command for StickerGet { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_inc.rs b/src/commands/stickers/sticker_inc.rs index 0aea5d2..5bc4562 100644 --- a/src/commands/stickers/sticker_inc.rs +++ b/src/commands/stickers/sticker_inc.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -34,7 +35,7 @@ impl Command for StickerInc { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_list.rs b/src/commands/stickers/sticker_list.rs index cfbbf8b..bb66a98 100644 --- a/src/commands/stickers/sticker_list.rs +++ b/src/commands/stickers/sticker_list.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -29,7 +30,7 @@ impl Command for StickerList { format!("{} {} {}", Self::COMMAND, request.sticker_type, request.uri) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/sticker_set.rs b/src/commands/stickers/sticker_set.rs index ea4b8ff..17aa0fa 100644 --- a/src/commands/stickers/sticker_set.rs +++ b/src/commands/stickers/sticker_set.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -34,7 +35,7 @@ impl Command for StickerSet { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type .parse() diff --git a/src/commands/stickers/stickernames.rs b/src/commands/stickers/stickernames.rs index 1b148d4..d899581 100644 --- a/src/commands/stickers/stickernames.rs +++ b/src/commands/stickers/stickernames.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct StickerNames; @@ -16,7 +19,7 @@ impl Command for StickerNames { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::StickerNames, "")) diff --git a/src/commands/stickers/stickernamestypes.rs b/src/commands/stickers/stickernamestypes.rs index 0d43ebb..f3ed9bc 100644 --- a/src/commands/stickers/stickernamestypes.rs +++ b/src/commands/stickers/stickernamestypes.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -21,7 +22,7 @@ impl Command for StickerNamesTypes { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().map(|s| s.to_string()); debug_assert!(parts.next().is_none()); diff --git a/src/commands/stickers/stickertypes.rs b/src/commands/stickers/stickertypes.rs index eaba3b0..331d6e6 100644 --- a/src/commands/stickers/stickertypes.rs +++ b/src/commands/stickers/stickertypes.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, }; pub struct StickerTypes; @@ -16,7 +19,7 @@ impl Command for StickerTypes { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::StickerTypes, "")) diff --git a/src/commands/stored_playlists/listplaylist.rs b/src/commands/stored_playlists/listplaylist.rs index a2f890f..5842dc9 100644 --- a/src/commands/stored_playlists/listplaylist.rs +++ b/src/commands/stored_playlists/listplaylist.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, @@ -31,7 +32,7 @@ impl Command for ListPlaylist { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name .parse::() diff --git a/src/commands/stored_playlists/listplaylistinfo.rs b/src/commands/stored_playlists/listplaylistinfo.rs index 168c469..c21f88a 100644 --- a/src/commands/stored_playlists/listplaylistinfo.rs +++ b/src/commands/stored_playlists/listplaylistinfo.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -28,7 +29,7 @@ impl Command for ListPlaylistInfo { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name .parse::() diff --git a/src/commands/stored_playlists/listplaylists.rs b/src/commands/stored_playlists/listplaylists.rs index b15b553..25f09d7 100644 --- a/src/commands/stored_playlists/listplaylists.rs +++ b/src/commands/stored_playlists/listplaylists.rs @@ -1,5 +1,6 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError}, }; pub struct ListPlaylists; @@ -13,7 +14,7 @@ impl Command for ListPlaylists { Self::COMMAND.to_string() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); Ok((Request::ListPlaylists, "")) } diff --git a/src/commands/stored_playlists/load.rs b/src/commands/stored_playlists/load.rs index 00a5d27..91db802 100644 --- a/src/commands/stored_playlists/load.rs +++ b/src/commands/stored_playlists/load.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -39,7 +40,7 @@ impl Command for Load { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/playlistadd.rs b/src/commands/stored_playlists/playlistadd.rs index 291ece1..0862791 100644 --- a/src/commands/stored_playlists/playlistadd.rs +++ b/src/commands/stored_playlists/playlistadd.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -44,7 +45,7 @@ impl Command for PlaylistAdd { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/playlistclear.rs b/src/commands/stored_playlists/playlistclear.rs index 576ca92..9c5aa64 100644 --- a/src/commands/stored_playlists/playlistclear.rs +++ b/src/commands/stored_playlists/playlistclear.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for PlaylistClear { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/playlistdelete.rs b/src/commands/stored_playlists/playlistdelete.rs index 7ecd1e8..5a78c48 100644 --- a/src/commands/stored_playlists/playlistdelete.rs +++ b/src/commands/stored_playlists/playlistdelete.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -30,7 +31,7 @@ impl Command for PlaylistDelete { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/playlistlength.rs b/src/commands/stored_playlists/playlistlength.rs index e76e0c1..ac2dfe3 100644 --- a/src/commands/stored_playlists/playlistlength.rs +++ b/src/commands/stored_playlists/playlistlength.rs @@ -3,6 +3,7 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, @@ -27,7 +28,7 @@ impl Command for PlaylistLength { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/playlistmove.rs b/src/commands/stored_playlists/playlistmove.rs index f8697d5..65c6573 100644 --- a/src/commands/stored_playlists/playlistmove.rs +++ b/src/commands/stored_playlists/playlistmove.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -39,7 +40,7 @@ impl Command for PlaylistMove { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/rename.rs b/src/commands/stored_playlists/rename.rs index fc32756..dc4b2da 100644 --- a/src/commands/stored_playlists/rename.rs +++ b/src/commands/stored_playlists/rename.rs @@ -1,8 +1,11 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + request_tokenizer::RequestTokenizer, + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, }; pub struct Rename; @@ -27,7 +30,7 @@ impl Command for Rename { ) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let old_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/rm.rs b/src/commands/stored_playlists/rm.rs index c2ea26a..3fce081 100644 --- a/src/commands/stored_playlists/rm.rs +++ b/src/commands/stored_playlists/rm.rs @@ -1,4 +1,5 @@ use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -17,7 +18,7 @@ impl Command for Rm { format!("{} {}", Self::COMMAND, request) } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/save.rs b/src/commands/stored_playlists/save.rs index 882d49c..b38cdcf 100644 --- a/src/commands/stored_playlists/save.rs +++ b/src/commands/stored_playlists/save.rs @@ -1,6 +1,7 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -32,7 +33,7 @@ impl Command for Save { } } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() .ok_or(RequestParserError::UnexpectedEOF)? diff --git a/src/commands/stored_playlists/searchplaylist.rs b/src/commands/stored_playlists/searchplaylist.rs index 0246777..5ac60ee 100644 --- a/src/commands/stored_playlists/searchplaylist.rs +++ b/src/commands/stored_playlists/searchplaylist.rs @@ -1,12 +1,13 @@ use serde::{Deserialize, Serialize}; use crate::{ + request_tokenizer::RequestTokenizer, commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, common::{PlaylistName, WindowRange}, - filter::{Filter, parse_filter}, + filter::Filter, }; pub struct SearchPlaylist; @@ -27,13 +28,18 @@ impl Command for SearchPlaylist { unimplemented!() } - fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { + fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name .parse::() .map_err(|_| RequestParserError::SyntaxError(0, name.to_owned()))?; - let filter = parse_filter(&mut parts)?; + let filter = match parts.next() { + Some(f) => { + Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))? + } + None => return Err(RequestParserError::UnexpectedEOF), + }; let range = parts .next() diff --git a/src/filter.rs b/src/filter.rs index 5502ab7..16aba8a 100644 --- a/src/filter.rs +++ b/src/filter.rs @@ -1,4 +1,4 @@ -use std::{fmt, str::SplitWhitespace}; +use std::fmt; use serde::{Deserialize, Serialize}; @@ -147,13 +147,10 @@ impl fmt::Display for Filter { } } -pub fn parse_filter(parts: &mut SplitWhitespace<'_>) -> Result { - // TODO: count parentheses and extract the full filter string - unimplemented!() -} - -fn parse_filter_str(string: &str) -> Result { - unimplemented!() +impl Filter { + pub fn parse(input: &str) -> Result { + unimplemented!() + } } #[cfg(test)] @@ -162,180 +159,163 @@ mod tests { #[test] fn test_parse_filter_eq_tag() { - let mut parts = "(artist == 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist == 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CommandDependent, false, - ) + )), ); - let mut parts = "(artist != 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist != 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CommandDependent, true, - ) + )), ); } #[test] fn test_parse_filter_contains() { - let mut parts = "(album contains 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album contains 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CommandDependent, false, - ), + )) ); - let mut parts = "(album !contains 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album !contains 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CommandDependent, true, - ), + )), ); } #[test] fn test_parse_filter_starts_with() { - let mut parts = "(title starts_with 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title starts_with 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CommandDependent, false, - ), + )), ); - let mut parts = "(title !starts_with 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title !starts_with 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CommandDependent, true, - ), + )), ); } #[test] fn test_parse_filter_perl_regex_positive() { - let mut parts = "(composer =~ 'Beethoven.*')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(composer =~ 'Beethoven.*')"); assert_eq!( filter, - Filter::PerlRegex(Tag::Composer("Beethoven.*".to_string())), + Ok(Filter::PerlRegex(Tag::Composer("Beethoven.*".to_string()))), ); } #[test] fn test_parse_filter_perl_regex_negative() { - let mut parts = "(genre !~ 'Pop.*')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(genre !~ 'Pop.*')"); assert_eq!( filter, - Filter::NegPerlRegex(Tag::Genre("Pop.*".to_string())), + Ok(Filter::NegPerlRegex(Tag::Genre("Pop.*".to_string()))), ); } #[test] fn test_parse_filter_base() { - let mut parts = "(base 'Rock/Classics')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); - assert_eq!(filter, Filter::Base("Rock/Classics".to_string()),); + let filter = Filter::parse("(base 'Rock/Classics')"); + assert_eq!(filter, Ok(Filter::Base("Rock/Classics".to_string()))); } #[test] fn test_parse_filter_modified_since() { - let mut parts = "(modified-since '1622505600')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); - assert_eq!(filter, Filter::ModifiedSince(1622505600),); + let filter = Filter::parse("(modified-since '1622505600')"); + assert_eq!(filter, Ok(Filter::ModifiedSince(1622505600))); } #[test] fn test_parse_filter_audio_added_since() { - let mut parts = "(added-since '1622505600')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); - assert_eq!(filter, Filter::ModifiedSince(1622505600),); + let filter = Filter::parse("(added-since '1622505600')"); + assert_eq!(filter, Ok(Filter::ModifiedSince(1622505600))); } #[test] fn test_parse_filter_audio_format_eq() { - let mut parts = "(AudioFormat == '44100:16:2')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(AudioFormat == '44100:16:2')"); assert_eq!( filter, - Filter::AudioFormatEq { + Ok(Filter::AudioFormatEq { sample_rate: 44100, bits: 16, channels: 2, - }, + }), ); } #[test] fn test_parse_filter_audio_format_eq_mask() { - let mut parts = "(AudioFormat =~ '44100:*:2')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(AudioFormat =~ '44100:*:2')"); assert_eq!( filter, - Filter::AudioFormatEqMask { + Ok(Filter::AudioFormatEqMask { sample_rate: Some(44100), bits: None, channels: Some(2), - }, + }), ); } #[test] fn test_parse_filter_prio_cmp() { - let mut parts = "(prio >= 42)".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(prio >= 42)"); assert_eq!( filter, - Filter::PrioCmp(ComparisonOperator::GreaterThanOrEqual, 42), + Ok(Filter::PrioCmp(ComparisonOperator::GreaterThanOrEqual, 42)), ); } #[test] fn test_parse_filter_not() { - let mut parts = "(!(artist == 'The Beatles'))".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(!(artist == 'The Beatles'))"); assert_eq!( filter, - Filter::Not(Box::new(Filter::EqTag( + Ok(Filter::Not(Box::new(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CommandDependent, false, - ))), + )))), ); } #[test] fn test_parse_filter_and() { - let mut parts = - "((artist == 'The Beatles') AND (album == 'Abbey Road'))".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("((artist == 'The Beatles') AND (album == 'Abbey Road'))"); assert_eq!( filter, - Filter::And( + Ok(Filter::And( Box::new(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CommandDependent, @@ -346,173 +326,159 @@ mod tests { CaseSensitivity::CommandDependent, false, )), - ), + )), ); } #[test] fn test_parse_filter_explicitly_case_sensitive() { - let mut parts = "(artist eq_cs 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist eq_cs 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CaseSensitive, false, - ), + )), ); - let mut parts = "(artist !eq_cs 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist !eq_cs 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CaseSensitive, true, - ), + )), ); - let mut parts = "(album contains_cs 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album contains_cs 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CaseSensitive, false, - ), + )), ); - let mut parts = "(album !contains_cs 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album !contains_cs 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CaseSensitive, true, - ), + )), ); - let mut parts = "(title starts_with_cs 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title starts_with_cs 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CaseSensitive, false, - ), + )), ); - let mut parts = "(title !starts_with_cs 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title !starts_with_cs 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CaseSensitive, true, - ), + )), ); } #[test] fn test_parse_filter_explicitly_case_insensitive() { - let mut parts = "(artist eq_ci 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist eq_ci 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CaseInsensitive, false, - ), + )), ); - let mut parts = "(artist !eq_ci 'The Beatles')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(artist !eq_ci 'The Beatles')"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CaseInsensitive, true, - ), + )), ); - let mut parts = "(album contains_ci 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album contains_ci 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CaseInsensitive, false, - ), + )), ); - let mut parts = "(album !contains_ci 'Greatest Hits')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(album !contains_ci 'Greatest Hits')"); assert_eq!( filter, - Filter::Contains( + Ok(Filter::Contains( Tag::Album("Greatest Hits".to_string()), CaseSensitivity::CaseInsensitive, true, - ), + )), ); - let mut parts = "(title starts_with_ci 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title starts_with_ci 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CaseInsensitive, false, - ), + )), ); - let mut parts = "(title !starts_with_ci 'Symphony No. ')".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(title !starts_with_ci 'Symphony No. ')"); assert_eq!( filter, - Filter::StartsWith( + Ok(Filter::StartsWith( Tag::Title("Symphony No. ".to_string()), CaseSensitivity::CaseInsensitive, true, - ), + )), ); } #[test] fn test_parse_filter_string_escapes() { - let mut parts = "(Artist == \"foo\\'bar\\\"\")".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(Artist == \"foo\\'bar\\\"\")"); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("foo'bar\"".to_string()), CaseSensitivity::CommandDependent, false, - ), + )), ); } #[test] fn test_parse_filter_excessive_whitespace() { - let mut parts = "(\tartist\n == 'The Beatles' ) ".split_whitespace(); - let filter = parse_filter(&mut parts).unwrap(); + let filter = Filter::parse("(\tartist\n == 'The Beatles' ) "); assert_eq!( filter, - Filter::EqTag( + Ok(Filter::EqTag( Tag::Artist("The Beatles".to_string()), CaseSensitivity::CommandDependent, false, - ) + )), ); } } diff --git a/src/lib.rs b/src/lib.rs index 5519fb9..946d18e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,6 +3,7 @@ //! //! It does not provide any client or server implementation +mod request_tokenizer; mod commands; mod common; mod filter; diff --git a/src/request.rs b/src/request.rs index 02c56ef..4868abb 100644 --- a/src/request.rs +++ b/src/request.rs @@ -1,5 +1,6 @@ use serde::{Deserialize, Serialize}; +use crate::request_tokenizer::RequestTokenizer; use crate::common::*; use crate::commands::*; @@ -222,7 +223,7 @@ impl Request { let (line, rest) = raw .split_once('\n') .ok_or(RequestParserError::UnexpectedEOF)?; - let mut parts = line.split_whitespace(); + let mut parts = RequestTokenizer::new(line); match parts .next() diff --git a/src/request_tokenizer.rs b/src/request_tokenizer.rs new file mode 100644 index 0000000..faab359 --- /dev/null +++ b/src/request_tokenizer.rs @@ -0,0 +1,186 @@ +use crate::commands::*; + +#[derive(Clone, Debug)] +pub struct RequestTokenizer<'a> { + pub(super) inner: &'a str, + pub(super) cursor: usize, +} + +impl<'a> RequestTokenizer<'a> { + #[inline] + pub fn new(input: &'a str) -> Self { + Self { + inner: input, + cursor: 0, + } + } +} + +#[inline] +fn parse_command_name(input: &str) -> Option<&str> { + // TODO: Create static trie of command names on startup for more efficient matching + let mut sorted_commands = COMMAND_NAMES.to_vec(); + sorted_commands.sort_by_key(|b| std::cmp::Reverse(b.len())); + sorted_commands + .into_iter() + .find(|&command| input.starts_with(command)) +} + +impl<'a> Iterator for RequestTokenizer<'a> { + type Item = &'a str; + + // Split on strings and whitespace + #[inline] + fn next(&mut self) -> Option<&'a str> { + #[inline] + fn is_whitespace(byte: u8) -> bool { + byte.is_ascii_whitespace() + } + + let bytes = self.inner.as_bytes(); + let len = bytes.len(); + + let should_parse_command = self.cursor == 0; + + // Skip leading whitespace + while self.cursor < len && is_whitespace(bytes[self.cursor]) { + self.cursor += 1; + } + + if should_parse_command { + if let Some(command) = parse_command_name(&self.inner[self.cursor..]) { + self.cursor += command.len(); + return Some(command); + } else { + return None; + } + } + + if self.cursor >= len { + return None; + } + + let start = self.cursor; + + if bytes[self.cursor] == b'"' { + // Quoted string + self.cursor += 1; // Skip opening quote + while self.cursor < len { + if bytes[self.cursor] == b'\\' + && self.cursor + 1 < len + && bytes[self.cursor + 1] == b'"' + { + self.cursor += 2; // Skip escaped quote + continue; + } + if bytes[self.cursor] == b'"' { + let end = self.cursor; + self.cursor += 1; // Skip closing quote + return Some(&self.inner[start + 1..end]); + } + self.cursor += 1; + } + // If we reach here, there was no closing quote + Some(&self.inner[start + 1..]) + } else { + // Unquoted string + while self.cursor < len && !is_whitespace(bytes[self.cursor]) { + self.cursor += 1; + } + Some(&self.inner[start..self.cursor]) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.inner.len())) + } +} + +#[cfg(test)] +mod tests { + use super::RequestTokenizer; + + #[test] + fn test_request_tokenizer() { + let input = r#"add arg1 "arg two" arg3 "arg four with spaces""#; + let mut tokenizer = RequestTokenizer::new(input); + + assert_eq!(tokenizer.next(), Some("add")); + assert_eq!(tokenizer.next(), Some("arg1")); + assert_eq!(tokenizer.next(), Some("arg two")); + assert_eq!(tokenizer.next(), Some("arg3")); + assert_eq!(tokenizer.next(), Some("arg four with spaces")); + assert_eq!(tokenizer.next(), None); + } + + #[test] + fn test_request_tokenizer_invalid_command() { + let input = r#"invalid_command arg1 "arg two" arg3 "arg four with spaces""#; + let mut tokenizer = RequestTokenizer::new(input); + + assert_eq!(tokenizer.next(), None); + } + + #[test] + fn test_request_tokenizer_unclosed_quote() { + let input = r#"add arg1 "arg two arg3"#; + let mut tokenizer = RequestTokenizer::new(input); + + assert_eq!(tokenizer.next(), Some("add")); + assert_eq!(tokenizer.next(), Some("arg1")); + assert_eq!(tokenizer.next(), Some("arg two arg3")); + assert_eq!(tokenizer.next(), None); + } + + #[test] + fn test_request_tokenizer_only_whitespace() { + let input = r#" "#; + let mut tokenizer = RequestTokenizer::new(input); + assert_eq!(tokenizer.next(), None); + } + + #[test] + fn test_request_tokenizer_escape_quotes() { + let input = r#"add "arg with a \" quote" arg2"#; + let mut tokenizer = RequestTokenizer::new(input); + + assert_eq!(tokenizer.next(), Some("add")); + assert_eq!(tokenizer.next(), Some(r#"arg with a \" quote"#)); + assert_eq!(tokenizer.next(), Some("arg2")); + assert_eq!(tokenizer.next(), None); + } + + #[test] + fn test_request_tokenizer_multitoken_command() { + let input = r#"protocol all arg"#; + let mut tokenizer = RequestTokenizer::new(input); + assert_eq!(tokenizer.next(), Some("protocol all")); + assert_eq!(tokenizer.next(), Some("arg")); + assert_eq!(tokenizer.next(), None); + + let input = r#"protocol arg"#; + let mut tokenizer = RequestTokenizer::new(input); + assert_eq!(tokenizer.next(), Some("protocol")); + assert_eq!(tokenizer.next(), Some("arg")); + assert_eq!(tokenizer.next(), None); + } + + // TODO: Implement ignoring spacing within command names + #[test] + #[ignore = "Currently, arbitrary spacing within commands names is not supported"] + fn test_request_tokenizer_ignore_spacing_in_command() { + let input = r#" protocol all arg "#; + + let mut tokenizer = RequestTokenizer::new(input); + assert_eq!(tokenizer.next(), Some("protocol all")); + assert_eq!(tokenizer.next(), Some("arg")); + assert_eq!(tokenizer.next(), None); + + let input = r#" protocol arg "#; + let mut tokenizer = RequestTokenizer::new(input); + assert_eq!(tokenizer.next(), Some("protocol")); + assert_eq!(tokenizer.next(), Some("arg")); + assert_eq!(tokenizer.next(), None); + } +}