From 181b437b80d5cc823ced2961b32bd086b0afdc0d Mon Sep 17 00:00:00 2001 From: h7x4 Date: Fri, 21 Nov 2025 16:48:55 +0900 Subject: [PATCH] WIP: serialize requests --- src/commands.rs | 15 +++++++ .../audio_output_devices/disableoutput.rs | 16 ++++++-- .../audio_output_devices/enableoutput.rs | 16 ++++++-- src/commands/audio_output_devices/outputs.rs | 5 +++ .../audio_output_devices/outputset.rs | 29 +++++++++++-- .../audio_output_devices/toggleoutput.rs | 16 ++++++-- src/commands/client_to_client/channels.rs | 16 ++++++-- src/commands/client_to_client/readmessages.rs | 16 ++++++-- src/commands/client_to_client/sendmessage.rs | 22 ++++++++-- src/commands/client_to_client/subscribe.rs | 14 +++++-- src/commands/client_to_client/unsubscribe.rs | 14 +++++-- .../connection_settings/binary_limit.rs | 5 +++ src/commands/connection_settings/close.rs | 5 +++ src/commands/connection_settings/kill.rs | 5 +++ src/commands/connection_settings/password.rs | 5 +++ src/commands/connection_settings/ping.rs | 5 +++ src/commands/connection_settings/protocol.rs | 5 +++ .../connection_settings/protocol_all.rs | 5 +++ .../connection_settings/protocol_available.rs | 5 +++ .../connection_settings/protocol_clear.rs | 5 +++ .../connection_settings/protocol_disable.rs | 8 ++++ .../connection_settings/protocol_enable.rs | 8 ++++ src/commands/connection_settings/tag_types.rs | 9 +++- .../connection_settings/tag_types_all.rs | 5 +++ .../tag_types_available.rs | 5 +++ .../connection_settings/tag_types_clear.rs | 5 +++ .../connection_settings/tag_types_disable.rs | 8 ++++ .../connection_settings/tag_types_enable.rs | 8 ++++ .../connection_settings/tag_types_reset.rs | 8 ++++ src/commands/controlling_playback/next.rs | 5 +++ src/commands/controlling_playback/pause.rs | 9 ++++ src/commands/controlling_playback/play.rs | 5 +++ src/commands/controlling_playback/playid.rs | 5 +++ src/commands/controlling_playback/previous.rs | 5 +++ src/commands/controlling_playback/seek.rs | 13 ++++++ src/commands/controlling_playback/seekcur.rs | 20 +++++++++ src/commands/controlling_playback/seekid.rs | 13 ++++++ src/commands/controlling_playback/stop.rs | 5 +++ .../mounts_and_neighbors/listmounts.rs | 5 +++ .../mounts_and_neighbors/listneighbors.rs | 5 +++ src/commands/mounts_and_neighbors/mount.rs | 13 ++++++ src/commands/mounts_and_neighbors/unmount.rs | 7 ++++ src/commands/music_database/albumart.rs | 13 +++++- src/commands/music_database/count.rs | 18 +++++++- src/commands/music_database/find.rs | 22 +++++++++- src/commands/music_database/findadd.rs | 28 ++++++++++++- src/commands/music_database/getfingerprint.rs | 14 +++++-- src/commands/music_database/list.rs | 29 ++++++++++--- src/commands/music_database/listall.rs | 18 ++++++-- src/commands/music_database/listallinfo.rs | 18 ++++++-- src/commands/music_database/listfiles.rs | 18 ++++++-- src/commands/music_database/lsinfo.rs | 17 ++++++-- src/commands/music_database/readcomments.rs | 14 +++++-- src/commands/music_database/readpicture.rs | 13 +++++- src/commands/music_database/rescan.rs | 17 ++++++-- src/commands/music_database/search.rs | 22 +++++++++- src/commands/music_database/searchadd.rs | 28 ++++++++++++- src/commands/music_database/searchaddpl.rs | 34 ++++++++++++++- src/commands/music_database/searchcount.rs | 18 +++++++- src/commands/music_database/update.rs | 17 ++++++-- .../partition_commands/delpartition.rs | 14 +++++-- .../partition_commands/listpartitions.rs | 16 ++++++-- src/commands/partition_commands/moveoutput.rs | 5 +++ .../partition_commands/newpartition.rs | 14 +++++-- src/commands/partition_commands/partition.rs | 14 +++++-- src/commands/playback_options/consume.rs | 14 +++++-- src/commands/playback_options/crossfade.rs | 5 +++ src/commands/playback_options/getvol.rs | 5 +++ src/commands/playback_options/mixrampdb.rs | 5 +++ src/commands/playback_options/mixrampdelay.rs | 5 +++ src/commands/playback_options/random.rs | 6 +++ src/commands/playback_options/repeat.rs | 6 +++ .../playback_options/replay_gain_mode.rs | 5 +++ .../playback_options/replay_gain_status.rs | 5 +++ src/commands/playback_options/setvol.rs | 5 +++ src/commands/playback_options/single.rs | 14 +++++-- src/commands/playback_options/volume.rs | 5 +++ .../querying_mpd_status/clearerror.rs | 5 +++ .../querying_mpd_status/currentsong.rs | 5 +++ src/commands/querying_mpd_status/idle.rs | 17 ++++++++ src/commands/querying_mpd_status/stats.rs | 5 +++ src/commands/querying_mpd_status/status.rs | 5 +++ src/commands/queue/add.rs | 18 +++++++- src/commands/queue/addid.rs | 16 +++++++- src/commands/queue/addtagid.rs | 21 ++++++++++ src/commands/queue/clear.rs | 5 +++ src/commands/queue/cleartagid.rs | 14 +++++++ src/commands/queue/delete.rs | 5 +++ src/commands/queue/deleteid.rs | 6 +++ src/commands/queue/move_.rs | 14 +++++++ src/commands/queue/moveid.rs | 14 +++++++ src/commands/queue/playlist.rs | 5 +++ src/commands/queue/playlistfind.rs | 24 ++++++++++- src/commands/queue/playlistid.rs | 6 +++ src/commands/queue/playlistinfo.rs | 9 ++++ src/commands/queue/playlistsearch.rs | 24 ++++++++++- src/commands/queue/plchanges.rs | 17 ++++++++ src/commands/queue/plchangesposid.rs | 17 ++++++++ src/commands/queue/prio.rs | 14 +++++++ src/commands/queue/prioid.rs | 20 +++++++++ src/commands/queue/rangeid.rs | 27 ++++++++++-- src/commands/queue/shuffle.rs | 9 ++++ src/commands/queue/swap.rs | 19 +++++++++ src/commands/queue/swapid.rs | 14 +++++++ src/commands/reflection/commands.rs | 9 +++- src/commands/reflection/config.rs | 5 +++ src/commands/reflection/decoders.rs | 5 +++ src/commands/reflection/not_commands.rs | 9 +++- src/commands/reflection/url_handlers.rs | 9 +++- src/commands/stickers/sticker_dec.rs | 31 ++++++++++++-- src/commands/stickers/sticker_delete.rs | 29 +++++++++++-- src/commands/stickers/sticker_find.rs | 40 +++++++++++++++--- src/commands/stickers/sticker_get.rs | 29 +++++++++++-- src/commands/stickers/sticker_inc.rs | 31 ++++++++++++-- src/commands/stickers/sticker_list.rs | 22 ++++++++-- src/commands/stickers/sticker_set.rs | 31 ++++++++++++-- src/commands/stickers/stickernames.rs | 5 +++ src/commands/stickers/stickernamestypes.rs | 14 +++++-- src/commands/stickers/stickertypes.rs | 5 +++ src/commands/stored_playlists/listplaylist.rs | 19 ++++++++- .../stored_playlists/listplaylistinfo.rs | 18 +++++++- .../stored_playlists/listplaylists.rs | 5 +++ src/commands/stored_playlists/load.rs | 36 ++++++++++++++-- src/commands/stored_playlists/playlistadd.rs | 41 +++++++++++++++++-- .../stored_playlists/playlistclear.rs | 14 +++++-- .../stored_playlists/playlistdelete.rs | 27 ++++++++++-- .../stored_playlists/playlistlength.rs | 14 +++++-- src/commands/stored_playlists/playlistmove.rs | 36 ++++++++++++++-- src/commands/stored_playlists/rename.rs | 18 ++++++++ src/commands/stored_playlists/rm.rs | 14 +++++-- src/commands/stored_playlists/save.rs | 29 +++++++++++-- .../stored_playlists/searchplaylist.rs | 18 +++++++- 132 files changed, 1661 insertions(+), 156 deletions(-) diff --git a/src/commands.rs b/src/commands.rs index d0d5239..e97a926 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -34,10 +34,25 @@ pub use stickers::*; pub use stored_playlists::*; pub trait Command { + // // The request sent from the client to the server + type Request; + + // The response sent from the server to the client type Response; + // The command name used within the protocol const COMMAND: &'static str; + fn serialize_request(&self, request: Self::Request) -> String; + fn serialize_request_to_bytes(&self, request: Self::Request) -> Vec { + self.serialize_request(request).into_bytes() + } + + // fn serialize_response(&self) -> String; + // fn serialize_response_to_bytes(&self) -> Vec { + // 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. diff --git a/src/commands/audio_output_devices/disableoutput.rs b/src/commands/audio_output_devices/disableoutput.rs index df3a565..3fb7770 100644 --- a/src/commands/audio_output_devices/disableoutput.rs +++ b/src/commands/audio_output_devices/disableoutput.rs @@ -1,14 +1,24 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::AudioOutputId, }; pub struct DisableOutput; +pub type DisableOutputRequest = AudioOutputId; + impl Command for DisableOutput { + type Request = DisableOutputRequest; type Response = (); const COMMAND: &'static str = "disableoutput"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/audio_output_devices/enableoutput.rs b/src/commands/audio_output_devices/enableoutput.rs index e229490..2084b68 100644 --- a/src/commands/audio_output_devices/enableoutput.rs +++ b/src/commands/audio_output_devices/enableoutput.rs @@ -1,14 +1,24 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::AudioOutputId, }; pub struct EnableOutput; +pub type EnableOutputRequest = AudioOutputId; + impl Command for EnableOutput { + type Request = EnableOutputRequest; type Response = (); const COMMAND: &'static str = "enableoutput"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/audio_output_devices/outputs.rs b/src/commands/audio_output_devices/outputs.rs index 90544c3..2512aa4 100644 --- a/src/commands/audio_output_devices/outputs.rs +++ b/src/commands/audio_output_devices/outputs.rs @@ -21,9 +21,14 @@ pub struct Output { pub type OutputsResponse = Vec; impl Command for Outputs { + type Request = (); type Response = OutputsResponse; const COMMAND: &'static str = "outputs"; + fn serialize_request(&self, _: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 d87e9d2..69f82a7 100644 --- a/src/commands/audio_output_devices/outputset.rs +++ b/src/commands/audio_output_devices/outputset.rs @@ -1,14 +1,37 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::AudioOutputId, }; pub struct OutputSet; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct OutputSetRequest { + pub output_id: AudioOutputId, + pub attribute_name: String, + pub attribute_value: String, +} + impl Command for OutputSet { + type Request = OutputSetRequest; type Response = (); const COMMAND: &'static str = "outputset"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {}", + Self::COMMAND, + request.output_id, + request.attribute_name, + request.attribute_value + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let attribute_name = 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 a9da3a0..df5bf4a 100644 --- a/src/commands/audio_output_devices/toggleoutput.rs +++ b/src/commands/audio_output_devices/toggleoutput.rs @@ -1,14 +1,24 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::AudioOutputId, }; pub struct ToggleOutput; +pub type ToggleOutputRequest = AudioOutputId; + impl Command for ToggleOutput { + type Request = ToggleOutputRequest; type Response = (); const COMMAND: &'static str = "toggleoutput"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/client_to_client/channels.rs b/src/commands/client_to_client/channels.rs index ff2f7c4..468cc5c 100644 --- a/src/commands/client_to_client/channels.rs +++ b/src/commands/client_to_client/channels.rs @@ -1,21 +1,29 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, + common::ChannelName, }; pub struct Channels; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ChannelsResponse { - pub channels: Vec, + pub channels: Vec, } impl Command for Channels { + type Request = (); type Response = ChannelsResponse; const COMMAND: &'static str = "channels"; + fn serialize_request(&self, _: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/client_to_client/readmessages.rs b/src/commands/client_to_client/readmessages.rs index f82ba6e..acd46a0 100644 --- a/src/commands/client_to_client/readmessages.rs +++ b/src/commands/client_to_client/readmessages.rs @@ -1,8 +1,11 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, + common::ChannelName, }; pub struct ReadMessages; @@ -11,14 +14,19 @@ pub type ReadMessagesResponse = Vec; #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ReadMessagesResponseEntry { - channel: String, + channel: ChannelName, message: String, } impl Command for ReadMessages { + type Request = (); type Response = ReadMessagesResponse; const COMMAND: &'static str = "readmessages"; + fn serialize_request(&self, _: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/client_to_client/sendmessage.rs b/src/commands/client_to_client/sendmessage.rs index 07b848a..004b121 100644 --- a/src/commands/client_to_client/sendmessage.rs +++ b/src/commands/client_to_client/sendmessage.rs @@ -1,14 +1,30 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::ChannelName, }; pub struct SendMessage; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SendMessageRequest { + pub channel: ChannelName, + pub message: String, +} + impl Command for SendMessage { + type Request = SendMessageRequest; type Response = (); const COMMAND: &'static str = "sendmessage"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.channel, request.message) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let channel = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/client_to_client/subscribe.rs b/src/commands/client_to_client/subscribe.rs index 9fefc68..db8c283 100644 --- a/src/commands/client_to_client/subscribe.rs +++ b/src/commands/client_to_client/subscribe.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::ChannelName, }; pub struct Subscribe; impl Command for Subscribe { + type Request = ChannelName; type Response = (); const COMMAND: &'static str = "subscribe"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/client_to_client/unsubscribe.rs b/src/commands/client_to_client/unsubscribe.rs index 9d19286..d72b249 100644 --- a/src/commands/client_to_client/unsubscribe.rs +++ b/src/commands/client_to_client/unsubscribe.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::ChannelName, }; pub struct Unsubscribe; impl Command for Unsubscribe { + type Request = ChannelName; type Response = (); const COMMAND: &'static str = "unsubscribe"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; diff --git a/src/commands/connection_settings/binary_limit.rs b/src/commands/connection_settings/binary_limit.rs index bb262b1..27e292c 100644 --- a/src/commands/connection_settings/binary_limit.rs +++ b/src/commands/connection_settings/binary_limit.rs @@ -6,9 +6,14 @@ use crate::commands::{ pub struct BinaryLimit; impl Command for BinaryLimit { + type Request = u64; type Response = (); const COMMAND: &'static str = "binarylimit"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let limit = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let limit = limit diff --git a/src/commands/connection_settings/close.rs b/src/commands/connection_settings/close.rs index 4d22899..a86cc42 100644 --- a/src/commands/connection_settings/close.rs +++ b/src/commands/connection_settings/close.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Close; impl Command for Close { + type Request = (); type Response = (); const COMMAND: &'static str = "close"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 58382f8..29d704d 100644 --- a/src/commands/connection_settings/kill.rs +++ b/src/commands/connection_settings/kill.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Kill; impl Command for Kill { + type Request = (); type Response = (); const COMMAND: &'static str = "kill"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 94ff0d2..6735daf 100644 --- a/src/commands/connection_settings/password.rs +++ b/src/commands/connection_settings/password.rs @@ -6,9 +6,14 @@ use crate::commands::{ pub struct Password; impl Command for Password { + type Request = String; type Response = (); const COMMAND: &'static str = "password"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let password = parts .next() diff --git a/src/commands/connection_settings/ping.rs b/src/commands/connection_settings/ping.rs index 47286fa..8448919 100644 --- a/src/commands/connection_settings/ping.rs +++ b/src/commands/connection_settings/ping.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Ping; impl Command for Ping { + type Request = (); type Response = (); const COMMAND: &'static str = "ping"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 aa45bc1..8017601 100644 --- a/src/commands/connection_settings/protocol.rs +++ b/src/commands/connection_settings/protocol.rs @@ -10,9 +10,14 @@ pub struct Protocol; pub type ProtocolResponse = Vec; impl Command for Protocol { + type Request = (); type Response = ProtocolResponse; const COMMAND: &'static str = "protocol"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 30e5f7b..a33a672 100644 --- a/src/commands/connection_settings/protocol_all.rs +++ b/src/commands/connection_settings/protocol_all.rs @@ -6,9 +6,14 @@ use crate::{ pub struct ProtocolAll; impl Command for ProtocolAll { + type Request = (); type Response = (); const COMMAND: &'static str = "protocol all"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 a98c7f8..f6b798c 100644 --- a/src/commands/connection_settings/protocol_available.rs +++ b/src/commands/connection_settings/protocol_available.rs @@ -10,9 +10,14 @@ pub struct ProtocolAvailable; pub type ProtocolAvailableResponse = Vec; impl Command for ProtocolAvailable { + type Request = (); type Response = ProtocolAvailableResponse; const COMMAND: &'static str = "protocol available"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 b03a6ae..8c7ea42 100644 --- a/src/commands/connection_settings/protocol_clear.rs +++ b/src/commands/connection_settings/protocol_clear.rs @@ -6,9 +6,14 @@ use crate::{ pub struct ProtocolClear; impl Command for ProtocolClear { + type Request = (); type Response = (); const COMMAND: &'static str = "protocol clear"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 c547ce6..83ebf9f 100644 --- a/src/commands/connection_settings/protocol_disable.rs +++ b/src/commands/connection_settings/protocol_disable.rs @@ -3,14 +3,22 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::Feature, }; pub struct ProtocolDisable; +pub type ProtocolDisableRequest = Vec; + impl Command for ProtocolDisable { + type Request = ProtocolDisableRequest; type Response = (); const COMMAND: &'static str = "protocol disable"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request.join(" ")) + } + fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { diff --git a/src/commands/connection_settings/protocol_enable.rs b/src/commands/connection_settings/protocol_enable.rs index fe463f0..4686021 100644 --- a/src/commands/connection_settings/protocol_enable.rs +++ b/src/commands/connection_settings/protocol_enable.rs @@ -3,14 +3,22 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::Feature, }; pub struct ProtocolEnable; +pub type ProtocolEnableRequest = Vec; + impl Command for ProtocolEnable { + type Request = ProtocolEnableRequest; type Response = (); const COMMAND: &'static str = "protocol enable"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request.join(" ")) + } + fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { diff --git a/src/commands/connection_settings/tag_types.rs b/src/commands/connection_settings/tag_types.rs index 236a09d..283bea5 100644 --- a/src/commands/connection_settings/tag_types.rs +++ b/src/commands/connection_settings/tag_types.rs @@ -1,6 +1,6 @@ use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, }; pub struct TagTypes; @@ -8,9 +8,14 @@ pub struct TagTypes; pub type TagTypesResponse = Vec; impl Command for TagTypes { + type Request = (); type Response = TagTypesResponse; const COMMAND: &'static str = "tagtypes"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 1913850..0509d5d 100644 --- a/src/commands/connection_settings/tag_types_all.rs +++ b/src/commands/connection_settings/tag_types_all.rs @@ -6,9 +6,14 @@ use crate::{ pub struct TagTypesAll; impl Command for TagTypesAll { + type Request = (); type Response = (); const COMMAND: &'static str = "tagtypes all"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 89156fe..73bd6c2 100644 --- a/src/commands/connection_settings/tag_types_available.rs +++ b/src/commands/connection_settings/tag_types_available.rs @@ -10,9 +10,14 @@ pub struct TagTypesAvailable; pub type TagTypesAvailableResponse = Vec; impl Command for TagTypesAvailable { + type Request = (); type Response = TagTypesAvailableResponse; const COMMAND: &'static str = "tagtypes available"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 dd25494..f1b0782 100644 --- a/src/commands/connection_settings/tag_types_clear.rs +++ b/src/commands/connection_settings/tag_types_clear.rs @@ -6,9 +6,14 @@ use crate::{ pub struct TagTypesClear; impl Command for TagTypesClear { + type Request = (); type Response = (); const COMMAND: &'static str = "tagtypes clear"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 6aa6197..17bf455 100644 --- a/src/commands/connection_settings/tag_types_disable.rs +++ b/src/commands/connection_settings/tag_types_disable.rs @@ -3,14 +3,22 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::TagName, }; pub struct TagTypesDisable; +pub type TagTypesDisableRequest = Vec; + impl Command for TagTypesDisable { + type Request = TagTypesDisableRequest; type Response = (); const COMMAND: &'static str = "tagtypes disable"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request.join(" ")) + } + fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { diff --git a/src/commands/connection_settings/tag_types_enable.rs b/src/commands/connection_settings/tag_types_enable.rs index 7580ef9..4eb5366 100644 --- a/src/commands/connection_settings/tag_types_enable.rs +++ b/src/commands/connection_settings/tag_types_enable.rs @@ -3,14 +3,22 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::TagName, }; pub struct TagTypesEnable; +pub type TagTypesEnableRequest = Vec; + impl Command for TagTypesEnable { + type Request = TagTypesEnableRequest; type Response = (); const COMMAND: &'static str = "tagtypes enable"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request.join(" ")) + } + fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { diff --git a/src/commands/connection_settings/tag_types_reset.rs b/src/commands/connection_settings/tag_types_reset.rs index 049f1a5..beae9e3 100644 --- a/src/commands/connection_settings/tag_types_reset.rs +++ b/src/commands/connection_settings/tag_types_reset.rs @@ -3,14 +3,22 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::TagName, }; pub struct TagTypesReset; +pub type TagTypesResetRequest = Vec; + impl Command for TagTypesReset { + type Request = TagTypesResetRequest; type Response = (); const COMMAND: &'static str = "tagtypes reset"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request.join(" ")) + } + fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mut parts = parts.peekable(); if parts.peek().is_none() { diff --git a/src/commands/controlling_playback/next.rs b/src/commands/controlling_playback/next.rs index d0122f1..e6afa7f 100644 --- a/src/commands/controlling_playback/next.rs +++ b/src/commands/controlling_playback/next.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Next; impl Command for Next { + type Request = (); type Response = (); const COMMAND: &'static str = "next"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 7714e99..3ba559f 100644 --- a/src/commands/controlling_playback/pause.rs +++ b/src/commands/controlling_playback/pause.rs @@ -6,9 +6,18 @@ use crate::commands::{ pub struct Pause; impl Command for Pause { + type Request = Option; type Response = (); const COMMAND: &'static str = "pause"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(true) => format!("{} 1", Self::COMMAND), + Some(false) => format!("{} 0", Self::COMMAND), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let result = match parts.next() { Some("0") => Ok((Request::Pause(Some(false)), "")), diff --git a/src/commands/controlling_playback/play.rs b/src/commands/controlling_playback/play.rs index e4b0ffb..8e20f48 100644 --- a/src/commands/controlling_playback/play.rs +++ b/src/commands/controlling_playback/play.rs @@ -9,9 +9,14 @@ use crate::{ pub struct Play; impl Command for Play { + type Request = SongPosition; type Response = (); const COMMAND: &'static str = "play"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songpos = match parts.next() { Some(s) => s diff --git a/src/commands/controlling_playback/playid.rs b/src/commands/controlling_playback/playid.rs index 738148b..e06c8ee 100644 --- a/src/commands/controlling_playback/playid.rs +++ b/src/commands/controlling_playback/playid.rs @@ -9,9 +9,14 @@ use crate::{ pub struct PlayId; impl Command for PlayId { + type Request = SongId; type Response = (); const COMMAND: &'static str = "playid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid = match parts.next() { Some(s) => s diff --git a/src/commands/controlling_playback/previous.rs b/src/commands/controlling_playback/previous.rs index ff60ffc..82eeac6 100644 --- a/src/commands/controlling_playback/previous.rs +++ b/src/commands/controlling_playback/previous.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Previous; impl Command for Previous { + type Request = (); type Response = (); const COMMAND: &'static str = "previous"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 d328d21..57f54e1 100644 --- a/src/commands/controlling_playback/seek.rs +++ b/src/commands/controlling_playback/seek.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, @@ -8,10 +10,21 @@ use crate::{ pub struct Seek; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SeekRequest { + pub songpos: SongPosition, + pub time: TimeWithFractions, +} + impl Command for Seek { + type Request = SeekRequest; type Response = (); const COMMAND: &'static str = "seek"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.songpos, request.time) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songpos = match parts.next() { Some(s) => s diff --git a/src/commands/controlling_playback/seekcur.rs b/src/commands/controlling_playback/seekcur.rs index 1715717..e914119 100644 --- a/src/commands/controlling_playback/seekcur.rs +++ b/src/commands/controlling_playback/seekcur.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, @@ -8,10 +10,28 @@ use crate::{ pub struct SeekCur; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SeekCurRequest { + pub mode: SeekMode, + pub time: TimeWithFractions, +} + impl Command for SeekCur { + type Request = SeekCurRequest; type Response = (); const COMMAND: &'static str = "seekcur"; + fn serialize_request(&self, request: Self::Request) -> String { + let time_str = match request.mode { + SeekMode::Absolute => format!("{}", request.time), + SeekMode::Relative if request.time >= 0.0 => format!("+{}", request.time), + SeekMode::Relative => format!("-{}", -request.time), + SeekMode::RelativeReverse => unimplemented!(), // TODO: should this happen? + }; + + format!("{} {}", Self::COMMAND, time_str) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let time_raw = match parts.next() { Some(t) => t, diff --git a/src/commands/controlling_playback/seekid.rs b/src/commands/controlling_playback/seekid.rs index 411fbb5..25785bb 100644 --- a/src/commands/controlling_playback/seekid.rs +++ b/src/commands/controlling_playback/seekid.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, @@ -8,10 +10,21 @@ use crate::{ pub struct SeekId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SeekIdRequest { + pub songid: SongId, + pub time: TimeWithFractions, +} + impl Command for SeekId { + type Request = SeekIdRequest; type Response = (); const COMMAND: &'static str = "seekid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.songid, request.time) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid = match parts.next() { Some(s) => s diff --git a/src/commands/controlling_playback/stop.rs b/src/commands/controlling_playback/stop.rs index 154c537..d4f10bf 100644 --- a/src/commands/controlling_playback/stop.rs +++ b/src/commands/controlling_playback/stop.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Stop; impl Command for Stop { + type Request = (); type Response = (); const COMMAND: &'static str = "stop"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 96a2e58..8fcff75 100644 --- a/src/commands/mounts_and_neighbors/listmounts.rs +++ b/src/commands/mounts_and_neighbors/listmounts.rs @@ -8,9 +8,14 @@ use crate::{ pub struct ListMounts; impl Command for ListMounts { + type Request = (); type Response = Vec; const COMMAND: &'static str = "listmounts"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 098feac..0791dbc 100644 --- a/src/commands/mounts_and_neighbors/listneighbors.rs +++ b/src/commands/mounts_and_neighbors/listneighbors.rs @@ -12,9 +12,14 @@ pub struct ListNeighbors; pub type ListNeighborsResponse = HashMap; impl Command for ListNeighbors { + type Request = (); type Response = ListNeighborsResponse; const COMMAND: &'static str = "listneighbors"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 31c4ff5..99e2034 100644 --- a/src/commands/mounts_and_neighbors/mount.rs +++ b/src/commands/mounts_and_neighbors/mount.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ @@ -7,10 +9,21 @@ use crate::{ pub struct Mount; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MountRequest { + pub path: String, + pub uri: String, +} + impl Command for Mount { + type Request = MountRequest; type Response = (); const COMMAND: &'static str = "mount"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.path, request.uri) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let path = parts .next() diff --git a/src/commands/mounts_and_neighbors/unmount.rs b/src/commands/mounts_and_neighbors/unmount.rs index 0a04acc..7235d0a 100644 --- a/src/commands/mounts_and_neighbors/unmount.rs +++ b/src/commands/mounts_and_neighbors/unmount.rs @@ -7,10 +7,17 @@ use crate::{ pub struct Unmount; +pub type UnmountRequest = String; + impl Command for Unmount { + type Request = UnmountRequest; type Response = (); const COMMAND: &'static str = "unmount"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let path = parts .next() diff --git a/src/commands/music_database/albumart.rs b/src/commands/music_database/albumart.rs index ad977ae..d27c590 100644 --- a/src/commands/music_database/albumart.rs +++ b/src/commands/music_database/albumart.rs @@ -7,11 +7,17 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, get_property, }, - common::Offset, + common::{Offset, Uri}, }; pub struct AlbumArt; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AlbumArtRequest { + uri: Uri, + offset: Offset, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AlbumArtResponse { pub size: usize, @@ -19,9 +25,14 @@ pub struct AlbumArtResponse { } impl Command for AlbumArt { + type Request = AlbumArtRequest; type Response = AlbumArtResponse; const COMMAND: &'static str = "albumart"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.uri, request.offset) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, diff --git a/src/commands/music_database/count.rs b/src/commands/music_database/count.rs index 4de8833..63ce253 100644 --- a/src/commands/music_database/count.rs +++ b/src/commands/music_database/count.rs @@ -7,11 +7,18 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, }, - filter::parse_filter, + common::GroupType, + filter::{Filter, parse_filter}, }; pub struct Count; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct CountRequest { + filter: Filter, + group: Option, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct CountResponse { pub songs: usize, @@ -19,9 +26,18 @@ pub struct CountResponse { } impl Command for Count { + type Request = CountRequest; type Response = CountResponse; const COMMAND: &'static str = "count"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(group) = request.group { + cmd.push_str(&format!(" group {}", group)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/find.rs b/src/commands/music_database/find.rs index 48c6852..a5da1be 100644 --- a/src/commands/music_database/find.rs +++ b/src/commands/music_database/find.rs @@ -5,18 +5,38 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct Find; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct FindRequest { + filter: Filter, + sort: Option, + window: Option, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct FindResponse {} impl Command for Find { + type Request = FindRequest; type Response = FindResponse; const COMMAND: &'static str = "find"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/findadd.rs b/src/commands/music_database/findadd.rs index 019c21a..8e5799c 100644 --- a/src/commands/music_database/findadd.rs +++ b/src/commands/music_database/findadd.rs @@ -1,17 +1,43 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{SongPosition, Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct FindAdd; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct FindAddRequest { + filter: Filter, + sort: Option, + window: Option, + position: Option, +} + impl Command for FindAdd { + type Request = FindAddRequest; type Response = (); const COMMAND: &'static str = "findadd"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + if let Some(position) = request.position { + cmd.push_str(&format!(" position {}", position)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/getfingerprint.rs b/src/commands/music_database/getfingerprint.rs index 7e33fbf..ab55355 100644 --- a/src/commands/music_database/getfingerprint.rs +++ b/src/commands/music_database/getfingerprint.rs @@ -2,9 +2,12 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, get_and_parse_property, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, get_and_parse_property, + }, + common::Uri, }; pub struct GetFingerprint; @@ -15,9 +18,14 @@ pub struct GetFingerprintResponse { } impl Command for GetFingerprint { + type Request = Uri; type Response = GetFingerprintResponse; const COMMAND: &'static str = "getfingerprint"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = uri diff --git a/src/commands/music_database/list.rs b/src/commands/music_database/list.rs index eb90c0c..d70af24 100644 --- a/src/commands/music_database/list.rs +++ b/src/commands/music_database/list.rs @@ -1,24 +1,43 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, - filter::parse_filter, + common::{GroupType, TagName}, + filter::{Filter, parse_filter}, }; pub struct List; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ListRequest { + tagname: TagName, + filter: Filter, + group: Option, +} + pub type ListResponse = Vec; impl Command for List { + type Request = ListRequest; type Response = ListResponse; const COMMAND: &'static str = "list"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {} {}", Self::COMMAND, request.tagname, request.filter); + if let Some(group) = request.group { + cmd.push_str(&format!(" group {}", group)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { - let tagtype = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; - let tagtype = tagtype + let tagname = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; + let tagname = tagname .parse() - .map_err(|_| RequestParserError::SyntaxError(1, tagtype.to_owned()))?; + .map_err(|_| RequestParserError::SyntaxError(1, tagname.to_owned()))?; // TODO: This should be optional let filter = parse_filter(&mut parts)?; @@ -36,7 +55,7 @@ impl Command for List { debug_assert!(parts.next().is_none()); - Ok((Request::List(tagtype, filter, group), "")) + Ok((Request::List(tagname, filter, group), "")) } fn parse_response( diff --git a/src/commands/music_database/listall.rs b/src/commands/music_database/listall.rs index 9fa780e..3560272 100644 --- a/src/commands/music_database/listall.rs +++ b/src/commands/music_database/listall.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::Uri, }; pub struct ListAll; @@ -9,9 +12,18 @@ pub struct ListAll; pub type ListAllResponse = Vec; impl Command for ListAll { + type Request = Option; type Response = ListAllResponse; const COMMAND: &'static str = "listall"; + fn serialize_request(&self, request: Self::Request) -> String { + if let Some(uri) = request { + format!("{} {}", Self::COMMAND, uri) + } else { + Self::COMMAND.to_string() + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts .next() diff --git a/src/commands/music_database/listallinfo.rs b/src/commands/music_database/listallinfo.rs index 9b02d07..65ae560 100644 --- a/src/commands/music_database/listallinfo.rs +++ b/src/commands/music_database/listallinfo.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::Uri, }; pub struct ListAllInfo; @@ -10,9 +13,18 @@ pub struct ListAllInfo; pub type ListAllInfoResponse = Vec; impl Command for ListAllInfo { + type Request = Option; type Response = ListAllInfoResponse; const COMMAND: &'static str = "listallinfo"; + fn serialize_request(&self, request: Self::Request) -> String { + if let Some(uri) = request { + format!("{} {}", Self::COMMAND, uri) + } else { + Self::COMMAND.to_string() + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts .next() diff --git a/src/commands/music_database/listfiles.rs b/src/commands/music_database/listfiles.rs index c3673a8..daf4bd5 100644 --- a/src/commands/music_database/listfiles.rs +++ b/src/commands/music_database/listfiles.rs @@ -1,6 +1,9 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::Uri, }; pub struct ListFiles; @@ -9,9 +12,18 @@ pub struct ListFiles; pub type ListFilesResponse = Vec; impl Command for ListFiles { + type Request = Option; type Response = ListFilesResponse; const COMMAND: &'static str = "listfiles"; + fn serialize_request(&self, request: Self::Request) -> String { + if let Some(uri) = request { + format!("{} {}", Self::COMMAND, uri) + } else { + Self::COMMAND.to_string() + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts .next() diff --git a/src/commands/music_database/lsinfo.rs b/src/commands/music_database/lsinfo.rs index 601d548..e9bf165 100644 --- a/src/commands/music_database/lsinfo.rs +++ b/src/commands/music_database/lsinfo.rs @@ -1,8 +1,11 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, expect_property_type, + }, + common::Uri, }; pub struct LsInfo; @@ -16,9 +19,17 @@ pub struct LsInfoResponseEntry { } impl Command for LsInfo { + type Request = Option; type Response = LsInfoResponse; const COMMAND: &'static str = "lsinfo"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(uri) => format!("{} {}", Self::COMMAND, uri), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts .next() diff --git a/src/commands/music_database/readcomments.rs b/src/commands/music_database/readcomments.rs index d4f8da6..c259cd4 100644 --- a/src/commands/music_database/readcomments.rs +++ b/src/commands/music_database/readcomments.rs @@ -1,8 +1,11 @@ use std::collections::HashMap; -use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, - ResponseAttributes, ResponseParserError, +use crate::{ + commands::{ + Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, + ResponseAttributes, ResponseParserError, + }, + common::Uri, }; pub struct ReadComments; @@ -10,9 +13,14 @@ pub struct ReadComments; pub type ReadCommentsResponse = HashMap; impl Command for ReadComments { + type Request = Uri; type Response = ReadCommentsResponse; const COMMAND: &'static str = "readcomments"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = uri diff --git a/src/commands/music_database/readpicture.rs b/src/commands/music_database/readpicture.rs index ca25957..363850d 100644 --- a/src/commands/music_database/readpicture.rs +++ b/src/commands/music_database/readpicture.rs @@ -7,11 +7,17 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, get_optional_property, get_property, }, - common::Offset, + common::{Offset, Uri}, }; pub struct ReadPicture; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ReadPictureRequest { + pub uri: Uri, + pub offset: Offset, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct ReadPictureResponse { pub size: usize, @@ -20,9 +26,14 @@ pub struct ReadPictureResponse { } impl Command for ReadPicture { + type Request = ReadPictureRequest; type Response = Option; const COMMAND: &'static str = "readpicture"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.uri, request.offset) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, diff --git a/src/commands/music_database/rescan.rs b/src/commands/music_database/rescan.rs index 5c186a2..6038af3 100644 --- a/src/commands/music_database/rescan.rs +++ b/src/commands/music_database/rescan.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, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + get_and_parse_property, + }, + common::Uri, }; pub struct Rescan; @@ -15,9 +18,17 @@ pub struct RescanResponse { } impl Command for Rescan { + type Request = Option; type Response = RescanResponse; const COMMAND: &'static str = "rescan"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(uri) => format!("{} {}", Self::COMMAND, uri), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts.next().map(|s| s.to_string()); diff --git a/src/commands/music_database/search.rs b/src/commands/music_database/search.rs index 51d121a..d553a64 100644 --- a/src/commands/music_database/search.rs +++ b/src/commands/music_database/search.rs @@ -5,18 +5,38 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct Search; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SearchRequest { + filter: Filter, + sort: Option, + window: Option, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SearchResponse {} impl Command for Search { + type Request = SearchRequest; type Response = SearchResponse; const COMMAND: &'static str = "search"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/searchadd.rs b/src/commands/music_database/searchadd.rs index 2cc5b5d..11ba7e4 100644 --- a/src/commands/music_database/searchadd.rs +++ b/src/commands/music_database/searchadd.rs @@ -1,17 +1,43 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{SongPosition, Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct SearchAdd; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SearchAddRequest { + filter: Filter, + sort: Option, + window: Option, + position: Option, +} + impl Command for SearchAdd { + type Request = SearchAddRequest; type Response = (); const COMMAND: &'static str = "searchadd"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + if let Some(position) = request.position { + cmd.push_str(&format!(" position {}", position)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/searchaddpl.rs b/src/commands/music_database/searchaddpl.rs index 2e7fb8e..8648f06 100644 --- a/src/commands/music_database/searchaddpl.rs +++ b/src/commands/music_database/searchaddpl.rs @@ -1,17 +1,49 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{PlaylistName, SongPosition, Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct SearchAddPl; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SearchAddPlRequest { + playlist_name: PlaylistName, + filter: Filter, + sort: Option, + window: Option, + position: Option, +} + impl Command for SearchAddPl { + type Request = SearchAddPlRequest; type Response = (); const COMMAND: &'static str = "searchaddpl"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!( + "{} {} {}", + Self::COMMAND, + request.playlist_name, + request.filter + ); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + if let Some(position) = request.position { + cmd.push_str(&format!(" position {}", position)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/music_database/searchcount.rs b/src/commands/music_database/searchcount.rs index bc4cf08..44c3c1b 100644 --- a/src/commands/music_database/searchcount.rs +++ b/src/commands/music_database/searchcount.rs @@ -7,11 +7,18 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_and_parse_property, }, - filter::parse_filter, + common::GroupType, + filter::{Filter, parse_filter}, }; pub struct SearchCount; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SearchCountRequest { + filter: Filter, + group: Option, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct SearchCountResponse { pub songs: usize, @@ -19,9 +26,18 @@ pub struct SearchCountResponse { } impl Command for SearchCount { + type Request = SearchCountRequest; type Response = SearchCountResponse; const COMMAND: &'static str = "searchcount"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(group) = request.group { + cmd.push_str(&format!(" group {}", group)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/music_database/update.rs b/src/commands/music_database/update.rs index d07d797..9609433 100644 --- a/src/commands/music_database/update.rs +++ b/src/commands/music_database/update.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, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + get_and_parse_property, + }, + common::Uri, }; pub struct Update; @@ -15,9 +18,17 @@ pub struct UpdateResponse { } impl Command for Update { + type Request = Option; type Response = UpdateResponse; const COMMAND: &'static str = "update"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(uri) => format!("{} {}", Self::COMMAND, uri), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = parts.next().map(|s| s.to_string()); diff --git a/src/commands/partition_commands/delpartition.rs b/src/commands/partition_commands/delpartition.rs index f55e162..163e31c 100644 --- a/src/commands/partition_commands/delpartition.rs +++ b/src/commands/partition_commands/delpartition.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::PartitionName, }; pub struct DelPartition; impl Command for DelPartition { + type Request = PartitionName; type Response = (); const COMMAND: &'static str = "delpartition"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let partition = parts .next() diff --git a/src/commands/partition_commands/listpartitions.rs b/src/commands/partition_commands/listpartitions.rs index bec0592..2eda57f 100644 --- a/src/commands/partition_commands/listpartitions.rs +++ b/src/commands/partition_commands/listpartitions.rs @@ -1,16 +1,24 @@ -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, - expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, + common::PartitionName, }; pub struct ListPartitions; -pub type ListPartitionsResponse = Vec; +pub type ListPartitionsResponse = Vec; impl Command for ListPartitions { + type Request = (); type Response = ListPartitionsResponse; const COMMAND: &'static str = "listpartitions"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 fb99c85..69c78d9 100644 --- a/src/commands/partition_commands/moveoutput.rs +++ b/src/commands/partition_commands/moveoutput.rs @@ -6,9 +6,14 @@ use crate::commands::{ pub struct MoveOutput; impl Command for MoveOutput { + type Request = String; type Response = (); const COMMAND: &'static str = "moveoutput"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let output_name = parts .next() diff --git a/src/commands/partition_commands/newpartition.rs b/src/commands/partition_commands/newpartition.rs index b8fd3b1..895438d 100644 --- a/src/commands/partition_commands/newpartition.rs +++ b/src/commands/partition_commands/newpartition.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::PartitionName, }; pub struct NewPartition; impl Command for NewPartition { + type Request = PartitionName; type Response = (); const COMMAND: &'static str = "newpartition"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let partition = parts .next() diff --git a/src/commands/partition_commands/partition.rs b/src/commands/partition_commands/partition.rs index 162e5e5..4fa70a0 100644 --- a/src/commands/partition_commands/partition.rs +++ b/src/commands/partition_commands/partition.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::PartitionName, }; pub struct Partition; impl Command for Partition { + type Request = PartitionName; type Response = (); const COMMAND: &'static str = "partition"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let partition = parts .next() diff --git a/src/commands/playback_options/consume.rs b/src/commands/playback_options/consume.rs index 2bdbb3b..6a1aa82 100644 --- a/src/commands/playback_options/consume.rs +++ b/src/commands/playback_options/consume.rs @@ -1,16 +1,24 @@ use std::str::FromStr; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::BoolOrOneshot, }; pub struct Consume; impl Command for Consume { + type Request = BoolOrOneshot; type Response = (); const COMMAND: &'static str = "consume"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some(s) => crate::common::BoolOrOneshot::from_str(s) diff --git a/src/commands/playback_options/crossfade.rs b/src/commands/playback_options/crossfade.rs index f22ce86..34837aa 100644 --- a/src/commands/playback_options/crossfade.rs +++ b/src/commands/playback_options/crossfade.rs @@ -9,9 +9,14 @@ use crate::{ pub struct Crossfade; impl Command for Crossfade { + type Request = Seconds; type Response = (); const COMMAND: &'static str = "crossfade"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let seconds = match parts.next() { Some(s) => s diff --git a/src/commands/playback_options/getvol.rs b/src/commands/playback_options/getvol.rs index 6b5745e..4f5e5ca 100644 --- a/src/commands/playback_options/getvol.rs +++ b/src/commands/playback_options/getvol.rs @@ -11,9 +11,14 @@ use crate::{ pub struct GetVol; impl Command for GetVol { + type Request = (); type Response = VolumeValue; const COMMAND: &'static str = "getvol"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 e554324..cc7a19b 100644 --- a/src/commands/playback_options/mixrampdb.rs +++ b/src/commands/playback_options/mixrampdb.rs @@ -6,9 +6,14 @@ use crate::commands::{ pub struct MixRampDb; impl Command for MixRampDb { + type Request = f32; type Response = (); const COMMAND: &'static str = "mixrampdb"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let db = match parts.next() { Some(s) => s diff --git a/src/commands/playback_options/mixrampdelay.rs b/src/commands/playback_options/mixrampdelay.rs index d5b0c7e..b913fb6 100644 --- a/src/commands/playback_options/mixrampdelay.rs +++ b/src/commands/playback_options/mixrampdelay.rs @@ -9,9 +9,14 @@ use crate::{ pub struct MixRampDelay; impl Command for MixRampDelay { + type Request = Seconds; type Response = (); const COMMAND: &'static str = "mixrampdelay"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let seconds = match parts.next() { Some(s) => s diff --git a/src/commands/playback_options/random.rs b/src/commands/playback_options/random.rs index 9ff1c1d..649e0fe 100644 --- a/src/commands/playback_options/random.rs +++ b/src/commands/playback_options/random.rs @@ -6,9 +6,15 @@ use crate::commands::{ pub struct Random; impl Command for Random { + type Request = bool; type Response = (); const COMMAND: &'static str = "random"; + fn serialize_request(&self, request: Self::Request) -> String { + let state = if request { "1" } else { "0" }; + format!("{} {}", Self::COMMAND, state) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some("0") => false, diff --git a/src/commands/playback_options/repeat.rs b/src/commands/playback_options/repeat.rs index f834eb7..e5d5dd6 100644 --- a/src/commands/playback_options/repeat.rs +++ b/src/commands/playback_options/repeat.rs @@ -6,9 +6,15 @@ use crate::commands::{ pub struct Repeat; impl Command for Repeat { + type Request = bool; type Response = (); const COMMAND: &'static str = "repeat"; + fn serialize_request(&self, request: Self::Request) -> String { + let state = if request { "1" } else { "0" }; + format!("{} {}", Self::COMMAND, state) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some("0") => false, diff --git a/src/commands/playback_options/replay_gain_mode.rs b/src/commands/playback_options/replay_gain_mode.rs index ccb0d7f..aaaf903 100644 --- a/src/commands/playback_options/replay_gain_mode.rs +++ b/src/commands/playback_options/replay_gain_mode.rs @@ -11,9 +11,14 @@ use crate::{ pub struct ReplayGainMode; impl Command for ReplayGainMode { + type Request = ReplayGainModeMode; type Response = (); const COMMAND: &'static str = "replay_gain_mode"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let mode = match parts.next() { Some(s) => ReplayGainModeMode::from_str(s) diff --git a/src/commands/playback_options/replay_gain_status.rs b/src/commands/playback_options/replay_gain_status.rs index c790959..9496e90 100644 --- a/src/commands/playback_options/replay_gain_status.rs +++ b/src/commands/playback_options/replay_gain_status.rs @@ -18,9 +18,14 @@ pub struct ReplayGainStatusResponse { } impl Command for ReplayGainStatus { + type Request = (); type Response = ReplayGainStatusResponse; const COMMAND: &'static str = "replay_gain_status"; + fn serialize_request(&self, _: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 cce458b..0c84a38 100644 --- a/src/commands/playback_options/setvol.rs +++ b/src/commands/playback_options/setvol.rs @@ -11,9 +11,14 @@ use crate::{ pub struct SetVol; impl Command for SetVol { + type Request = VolumeValue; type Response = (); const COMMAND: &'static str = "setvol"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let volume = match parts.next() { Some(s) => VolumeValue::from_str(s) diff --git a/src/commands/playback_options/single.rs b/src/commands/playback_options/single.rs index 8bda831..ff76c2d 100644 --- a/src/commands/playback_options/single.rs +++ b/src/commands/playback_options/single.rs @@ -1,16 +1,24 @@ use std::str::FromStr; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::BoolOrOneshot, }; pub struct Single; impl Command for Single { + type Request = BoolOrOneshot; type Response = (); const COMMAND: &'static str = "single"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let state = match parts.next() { Some(s) => crate::common::BoolOrOneshot::from_str(s) diff --git a/src/commands/playback_options/volume.rs b/src/commands/playback_options/volume.rs index 7d76127..d0cc192 100644 --- a/src/commands/playback_options/volume.rs +++ b/src/commands/playback_options/volume.rs @@ -11,9 +11,14 @@ use crate::{ pub struct Volume; impl Command for Volume { + type Request = VolumeValue; type Response = (); const COMMAND: &'static str = "volume"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let change = match parts.next() { Some(s) => VolumeValue::from_str(s) diff --git a/src/commands/querying_mpd_status/clearerror.rs b/src/commands/querying_mpd_status/clearerror.rs index bbc00ae..dacef29 100644 --- a/src/commands/querying_mpd_status/clearerror.rs +++ b/src/commands/querying_mpd_status/clearerror.rs @@ -6,9 +6,14 @@ use crate::commands::{ pub struct ClearError; impl Command for ClearError { + type Request = (); type Response = (); const COMMAND: &'static str = "clearerror"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/querying_mpd_status/currentsong.rs b/src/commands/querying_mpd_status/currentsong.rs index 28005b0..4dcffd1 100644 --- a/src/commands/querying_mpd_status/currentsong.rs +++ b/src/commands/querying_mpd_status/currentsong.rs @@ -11,9 +11,14 @@ pub struct CurrentSong; pub struct CurrentSongResponse {} impl Command for CurrentSong { + type Request = (); type Response = CurrentSongResponse; const COMMAND: &'static str = "currentsong"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/querying_mpd_status/idle.rs b/src/commands/querying_mpd_status/idle.rs index 0f6524f..4626729 100644 --- a/src/commands/querying_mpd_status/idle.rs +++ b/src/commands/querying_mpd_status/idle.rs @@ -8,10 +8,27 @@ use crate::commands::{ pub struct Idle; +pub type IdleRequest = Option>; + impl Command for Idle { + type Request = IdleRequest; type Response = (); const COMMAND: &'static str = "idle"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(subsystems) => { + let subsystems_str = subsystems + .iter() + .map(|subsystem| subsystem.to_string()) + .collect::>() + .join(","); + format!("{} {}", Self::COMMAND, subsystems_str) + } + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: SplitWhitespace<'_>) -> RequestParserResult<'_> { let result = parts .next() diff --git a/src/commands/querying_mpd_status/stats.rs b/src/commands/querying_mpd_status/stats.rs index aa3f923..01b23de 100644 --- a/src/commands/querying_mpd_status/stats.rs +++ b/src/commands/querying_mpd_status/stats.rs @@ -21,9 +21,14 @@ pub struct StatsResponse { } impl Command for Stats { + type Request = (); type Response = StatsResponse; const COMMAND: &'static str = "stats"; + fn serialize_request(&self, _: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/querying_mpd_status/status.rs b/src/commands/querying_mpd_status/status.rs index 527d614..3fd38f0 100644 --- a/src/commands/querying_mpd_status/status.rs +++ b/src/commands/querying_mpd_status/status.rs @@ -159,9 +159,14 @@ fn parse_status_response( pub struct Status; impl Command for Status { + type Request = (); type Response = StatusResponse; const COMMAND: &'static str = "status"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/queue/add.rs b/src/commands/queue/add.rs index cb3dccc..6297b95 100644 --- a/src/commands/queue/add.rs +++ b/src/commands/queue/add.rs @@ -1,17 +1,33 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - common::SongPosition, + common::{SongPosition, Uri}, }; pub struct Add; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AddRequest { + uri: Uri, + position: Option, +} + impl Command for Add { + type Request = AddRequest; type Response = (); const COMMAND: &'static str = "add"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.position { + Some(position) => format!("{} {} {}", Self::COMMAND, request.uri, position), + None => format!("{} {}", Self::COMMAND, request.uri), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, diff --git a/src/commands/queue/addid.rs b/src/commands/queue/addid.rs index 87f347f..0f45a11 100644 --- a/src/commands/queue/addid.rs +++ b/src/commands/queue/addid.rs @@ -5,20 +5,34 @@ use crate::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, get_next_and_parse_property, }, - common::{SongId, SongPosition}, + common::{SongId, SongPosition, Uri}, }; pub struct AddId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AddIdRequest { + pub uri: Uri, + pub position: Option, +} + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct AddIdResponse { pub id: SongId, } impl Command for AddId { + type Request = AddIdRequest; type Response = AddIdResponse; const COMMAND: &'static str = "addid"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.position { + Some(pos) => format!("{} {} {}", Self::COMMAND, request.uri, pos), + None => format!("{} {}", Self::COMMAND, request.uri), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let uri = match parts.next() { Some(s) => s, diff --git a/src/commands/queue/addtagid.rs b/src/commands/queue/addtagid.rs index 6d37442..3b96006 100644 --- a/src/commands/queue/addtagid.rs +++ b/src/commands/queue/addtagid.rs @@ -1,16 +1,37 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{SongId, TagName, TagValue}, }; pub struct AddTagId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct AddTagIdRequest { + pub songid: SongId, + pub tag_name: TagName, + pub tag_value: TagValue, +} + impl Command for AddTagId { + type Request = AddTagIdRequest; type Response = (); const COMMAND: &'static str = "addtagid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {}", + Self::COMMAND, + request.songid, + request.tag_name, + request.tag_value + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid diff --git a/src/commands/queue/clear.rs b/src/commands/queue/clear.rs index 9587691..1aea67a 100644 --- a/src/commands/queue/clear.rs +++ b/src/commands/queue/clear.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct Clear; impl Command for Clear { + type Request = (); type Response = (); const COMMAND: &'static str = "clear"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 85840d4..2af44fb 100644 --- a/src/commands/queue/cleartagid.rs +++ b/src/commands/queue/cleartagid.rs @@ -1,16 +1,30 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{SongId, TagName}, }; pub struct ClearTagId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ClearTagIdRequest { + pub songid: SongId, + pub tag_name: TagName, +} + impl Command for ClearTagId { + type Request = ClearTagIdRequest; type Response = (); const COMMAND: &'static str = "cleartagid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.songid, request.tag_name) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid diff --git a/src/commands/queue/delete.rs b/src/commands/queue/delete.rs index e6ef0c1..30d9c8b 100644 --- a/src/commands/queue/delete.rs +++ b/src/commands/queue/delete.rs @@ -11,9 +11,14 @@ use crate::{ pub struct Delete; impl Command for Delete { + type Request = OneOrRange; type Response = (); const COMMAND: &'static str = "delete"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let pos_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let one_or_range = OneOrRange::from_str(pos_or_range) diff --git a/src/commands/queue/deleteid.rs b/src/commands/queue/deleteid.rs index b86773f..30292ae 100644 --- a/src/commands/queue/deleteid.rs +++ b/src/commands/queue/deleteid.rs @@ -3,14 +3,20 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::SongId, }; pub struct DeleteId; impl Command for DeleteId { + type Request = SongId; type Response = (); const COMMAND: &'static str = "deleteid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id diff --git a/src/commands/queue/move_.rs b/src/commands/queue/move_.rs index d627853..b1c67c7 100644 --- a/src/commands/queue/move_.rs +++ b/src/commands/queue/move_.rs @@ -1,16 +1,30 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{AbsouluteRelativeSongPosition, OneOrRange}, }; pub struct Move; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MoveRequest { + pub from_or_range: OneOrRange, + pub to: AbsouluteRelativeSongPosition, +} + impl Command for Move { + type Request = MoveRequest; type Response = (); const COMMAND: &'static str = "move"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.from_or_range, request.to) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let from_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let from_or_range = from_or_range diff --git a/src/commands/queue/moveid.rs b/src/commands/queue/moveid.rs index e59f22e..96e3bb9 100644 --- a/src/commands/queue/moveid.rs +++ b/src/commands/queue/moveid.rs @@ -1,16 +1,30 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{AbsouluteRelativeSongPosition, SongId}, }; pub struct MoveId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct MoveIdRequest { + pub id: SongId, + pub to: AbsouluteRelativeSongPosition, +} + impl Command for MoveId { + type Request = MoveIdRequest; type Response = (); const COMMAND: &'static str = "moveid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.id, request.to) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id diff --git a/src/commands/queue/playlist.rs b/src/commands/queue/playlist.rs index ac63aa8..fa4f829 100644 --- a/src/commands/queue/playlist.rs +++ b/src/commands/queue/playlist.rs @@ -6,9 +6,14 @@ use crate::{ pub struct Playlist; impl Command for Playlist { + type Request = (); type Response = (); const COMMAND: &'static str = "playlist"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 50b5418..565ee2c 100644 --- a/src/commands/queue/playlistfind.rs +++ b/src/commands/queue/playlistfind.rs @@ -1,17 +1,39 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct PlaylistFind; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaylistFindRequest { + filter: Filter, + sort: Option, + window: Option, +} + impl Command for PlaylistFind { + type Request = PlaylistFindRequest; type Response = (); const COMMAND: &'static str = "playlistfind"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/queue/playlistid.rs b/src/commands/queue/playlistid.rs index 64fcfff..7752b7d 100644 --- a/src/commands/queue/playlistid.rs +++ b/src/commands/queue/playlistid.rs @@ -3,14 +3,20 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::SongId, }; pub struct PlaylistId; impl Command for PlaylistId { + type Request = SongId; type Response = (); const COMMAND: &'static str = "playlistid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = id diff --git a/src/commands/queue/playlistinfo.rs b/src/commands/queue/playlistinfo.rs index 0115f0e..8bae5a7 100644 --- a/src/commands/queue/playlistinfo.rs +++ b/src/commands/queue/playlistinfo.rs @@ -3,14 +3,23 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::OneOrRange, }; pub struct PlaylistInfo; impl Command for PlaylistInfo { + type Request = Option; type Response = (); const COMMAND: &'static str = "playlistinfo"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(one_or_range) => format!("{} {}", Self::COMMAND, one_or_range), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let one_or_range = parts .next() diff --git a/src/commands/queue/playlistsearch.rs b/src/commands/queue/playlistsearch.rs index d054023..6547a3c 100644 --- a/src/commands/queue/playlistsearch.rs +++ b/src/commands/queue/playlistsearch.rs @@ -1,17 +1,39 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - filter::parse_filter, + common::{Sort, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct PlaylistSearch; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaylistSearchRequest { + filter: Filter, + sort: Option, + window: Option, +} + impl Command for PlaylistSearch { + type Request = PlaylistSearchRequest; type Response = (); const COMMAND: &'static str = "playlistsearch"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!("{} {}", Self::COMMAND, request.filter); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let filter = parse_filter(&mut parts)?; diff --git a/src/commands/queue/plchanges.rs b/src/commands/queue/plchanges.rs index fdb04d1..ec3918f 100644 --- a/src/commands/queue/plchanges.rs +++ b/src/commands/queue/plchanges.rs @@ -1,16 +1,33 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{Version, WindowRange}, }; pub struct PlChanges; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlChangesRequest { + pub version: Version, + pub window: Option, +} + impl Command for PlChanges { + type Request = PlChangesRequest; type Response = (); const COMMAND: &'static str = "plchanges"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.window { + Some(window) => format!("{} {} {}", Self::COMMAND, request.version, window), + None => format!("{} {}", Self::COMMAND, request.version), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let version = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let version = version diff --git a/src/commands/queue/plchangesposid.rs b/src/commands/queue/plchangesposid.rs index 16bb20f..c0fc12f 100644 --- a/src/commands/queue/plchangesposid.rs +++ b/src/commands/queue/plchangesposid.rs @@ -1,16 +1,33 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{Version, WindowRange}, }; pub struct PlChangesPosId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlChangesPosIdRequest { + pub version: Version, + pub window: Option, +} + impl Command for PlChangesPosId { + type Request = PlChangesPosIdRequest; type Response = (); const COMMAND: &'static str = "plchangesposid"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.window { + Some(window) => format!("{} {} {}", Self::COMMAND, request.version, window), + None => format!("{} {}", Self::COMMAND, request.version), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let version = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let version = version diff --git a/src/commands/queue/prio.rs b/src/commands/queue/prio.rs index fbbf87c..568e046 100644 --- a/src/commands/queue/prio.rs +++ b/src/commands/queue/prio.rs @@ -1,16 +1,30 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{Priority, WindowRange}, }; pub struct Prio; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PrioRequest { + pub prio: Priority, + pub window: WindowRange, +} + impl Command for Prio { + type Request = PrioRequest; type Response = (); const COMMAND: &'static str = "prio"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.prio, request.window) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let prio = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let prio = prio diff --git a/src/commands/queue/prioid.rs b/src/commands/queue/prioid.rs index f2f9fe8..e202015 100644 --- a/src/commands/queue/prioid.rs +++ b/src/commands/queue/prioid.rs @@ -1,16 +1,36 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{Priority, SongId}, }; pub struct PrioId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PrioIdRequest { + pub prio: Priority, + pub songids: Vec, +} + impl Command for PrioId { + type Request = PrioIdRequest; type Response = (); const COMMAND: &'static str = "prioid"; + fn serialize_request(&self, request: Self::Request) -> String { + let songids = request + .songids + .iter() + .map(|id| id.to_string()) + .collect::>() + .join(","); + format!("{} {} {}", Self::COMMAND, request.prio, songids) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let prio = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let prio = prio diff --git a/src/commands/queue/rangeid.rs b/src/commands/queue/rangeid.rs index 68c6818..a42f45f 100644 --- a/src/commands/queue/rangeid.rs +++ b/src/commands/queue/rangeid.rs @@ -1,30 +1,49 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::{SongId, TimeInterval}, }; pub struct RangeId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RangeIdRequest { + songid: SongId, + time_interval: TimeInterval, +} + impl Command for RangeId { + type Request = RangeIdRequest; type Response = (); const COMMAND: &'static str = "rangeid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {}", + Self::COMMAND, + request.songid, + request.time_interval + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = songid .parse() .map_err(|_| RequestParserError::SyntaxError(0, songid.to_string()))?; - let timeinterval = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; - let timeinterval = timeinterval + let time_interval = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; + let time_interval = time_interval .parse() - .map_err(|_| RequestParserError::SyntaxError(0, timeinterval.to_string()))?; + .map_err(|_| RequestParserError::SyntaxError(0, time_interval.to_string()))?; debug_assert!(parts.next().is_none()); - Ok((Request::RangeId(songid, timeinterval), "")) + Ok((Request::RangeId(songid, time_interval), "")) } fn parse_response( diff --git a/src/commands/queue/shuffle.rs b/src/commands/queue/shuffle.rs index b0e8410..45a12a2 100644 --- a/src/commands/queue/shuffle.rs +++ b/src/commands/queue/shuffle.rs @@ -3,14 +3,23 @@ use crate::{ commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::OneOrRange, }; pub struct Shuffle; impl Command for Shuffle { + type Request = Option; type Response = (); const COMMAND: &'static str = "shuffle"; + fn serialize_request(&self, request: Self::Request) -> String { + match request { + Some(range) => format!("{} {}", Self::COMMAND, range), + None => Self::COMMAND.to_string(), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let range = parts .next() diff --git a/src/commands/queue/swap.rs b/src/commands/queue/swap.rs index 7f58813..407fc78 100644 --- a/src/commands/queue/swap.rs +++ b/src/commands/queue/swap.rs @@ -1,16 +1,35 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::SongPosition, }; pub struct Swap; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SwapRequest { + pub songpos1: SongPosition, + pub songpos2: SongPosition, +} + impl Command for Swap { + type Request = SwapRequest; type Response = (); const COMMAND: &'static str = "swap"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {}", + Self::COMMAND, + request.songpos1, + request.songpos2 + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songpos1 = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songpos1 = songpos1 diff --git a/src/commands/queue/swapid.rs b/src/commands/queue/swapid.rs index cb1e79b..9b091c1 100644 --- a/src/commands/queue/swapid.rs +++ b/src/commands/queue/swapid.rs @@ -1,16 +1,30 @@ +use serde::{Deserialize, Serialize}; + use crate::{ Request, commands::{ Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, + common::SongId, }; pub struct SwapId; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SwapIdRequest { + pub songid1: SongId, + pub songid2: SongId, +} + impl Command for SwapId { + type Request = SwapIdRequest; type Response = (); const COMMAND: &'static str = "swapid"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.songid1, request.songid2) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let songid1 = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid1 = songid1 diff --git a/src/commands/reflection/commands.rs b/src/commands/reflection/commands.rs index f117408..1d1bb20 100644 --- a/src/commands/reflection/commands.rs +++ b/src/commands/reflection/commands.rs @@ -1,6 +1,6 @@ use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, }; pub struct Commands; @@ -8,9 +8,14 @@ pub struct Commands; pub type CommandsResponse = Vec; impl Command for Commands { + type Request = (); type Response = CommandsResponse; const COMMAND: &'static str = "commands"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 d012ea2..71376c4 100644 --- a/src/commands/reflection/config.rs +++ b/src/commands/reflection/config.rs @@ -17,9 +17,14 @@ pub struct ConfigResponse { } impl Command for Config { + type Request = (); type Response = ConfigResponse; const COMMAND: &'static str = "config"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 fe5a6a0..5129a08 100644 --- a/src/commands/reflection/decoders.rs +++ b/src/commands/reflection/decoders.rs @@ -17,9 +17,14 @@ pub struct Decoder { pub type DecodersResponse = Vec; impl Command for Decoders { + type Request = (); type Response = DecodersResponse; const COMMAND: &'static str = "decoders"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 b0b088a..21b6b6e 100644 --- a/src/commands/reflection/not_commands.rs +++ b/src/commands/reflection/not_commands.rs @@ -1,6 +1,6 @@ use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, }; pub struct NotCommands; @@ -8,9 +8,14 @@ pub struct NotCommands; pub type NotCommandsResponse = Vec; impl Command for NotCommands { + type Request = (); type Response = NotCommandsResponse; const COMMAND: &'static str = "notcommands"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 27bf5a5..5c8b44a 100644 --- a/src/commands/reflection/url_handlers.rs +++ b/src/commands/reflection/url_handlers.rs @@ -1,6 +1,6 @@ use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, }; pub struct UrlHandlers; @@ -8,9 +8,14 @@ pub struct UrlHandlers; pub type UrlHandlersResponse = Vec; impl Command for UrlHandlers { + type Request = (); type Response = UrlHandlersResponse; const COMMAND: &'static str = "urlhandlers"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 db9fa77..b572a65 100644 --- a/src/commands/stickers/sticker_dec.rs +++ b/src/commands/stickers/sticker_dec.rs @@ -1,14 +1,39 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{StickerType, Uri}, }; pub struct StickerDec; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerDecRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, + pub value: usize, +} + impl Command for StickerDec { + type Request = StickerDecRequest; type Response = (); const COMMAND: &'static str = "sticker dec"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name, + request.value + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_delete.rs b/src/commands/stickers/sticker_delete.rs index a9daf2a..bf1ef8f 100644 --- a/src/commands/stickers/sticker_delete.rs +++ b/src/commands/stickers/sticker_delete.rs @@ -1,14 +1,37 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{StickerType, Uri}, }; pub struct StickerDelete; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerDeleteRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, +} + impl Command for StickerDelete { + type Request = StickerDeleteRequest; type Response = (); const COMMAND: &'static str = "sticker delete"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_find.rs b/src/commands/stickers/sticker_find.rs index a3c3b78..c64af11 100644 --- a/src/commands/stickers/sticker_find.rs +++ b/src/commands/stickers/sticker_find.rs @@ -1,12 +1,26 @@ use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, expect_property_type, + }, + common::{Sort, StickerType, Uri, WindowRange}, }; pub struct StickerFind; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerFindRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, + pub sort: Option, + pub window: Option, +} + +pub type StickerFindResponse = Vec; + #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct StickerFindResponseEntry { pub uri: String, @@ -14,12 +28,28 @@ pub struct StickerFindResponseEntry { pub value: String, } -pub type StickerFindResponse = Vec; - impl Command for StickerFind { + type Request = StickerFindRequest; type Response = StickerFindResponse; const COMMAND: &'static str = "sticker find"; + fn serialize_request(&self, request: Self::Request) -> String { + let mut cmd = format!( + "{} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name + ); + if let Some(sort) = request.sort { + cmd.push_str(&format!(" sort {}", sort)); + } + if let Some(window) = request.window { + cmd.push_str(&format!(" window {}", window)); + } + cmd + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_get.rs b/src/commands/stickers/sticker_get.rs index e355c43..3fc41c7 100644 --- a/src/commands/stickers/sticker_get.rs +++ b/src/commands/stickers/sticker_get.rs @@ -1,16 +1,39 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, get_next_property, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, get_next_property, + }, + common::{StickerType, Uri}, }; pub struct StickerGet; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerGetRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, +} + pub type StickerGetResponse = String; impl Command for StickerGet { + type Request = StickerGetRequest; type Response = StickerGetResponse; const COMMAND: &'static str = "sticker get"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_inc.rs b/src/commands/stickers/sticker_inc.rs index 012da77..0aea5d2 100644 --- a/src/commands/stickers/sticker_inc.rs +++ b/src/commands/stickers/sticker_inc.rs @@ -1,14 +1,39 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{StickerType, Uri}, }; pub struct StickerInc; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerIncRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, + pub value: usize, +} + impl Command for StickerInc { + type Request = StickerIncRequest; type Response = (); const COMMAND: &'static str = "sticker inc"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name, + request.value + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_list.rs b/src/commands/stickers/sticker_list.rs index 9360fe8..cfbbf8b 100644 --- a/src/commands/stickers/sticker_list.rs +++ b/src/commands/stickers/sticker_list.rs @@ -1,18 +1,34 @@ use std::collections::HashMap; -use crate::commands::{ - Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, - ResponseAttributes, ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, GenericResponseValue, Request, RequestParserError, RequestParserResult, + ResponseAttributes, ResponseParserError, + }, + common::{StickerType, Uri}, }; pub struct StickerList; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerListRequest { + pub sticker_type: StickerType, + pub uri: Uri, +} + pub type StickerListResponse = HashMap; impl Command for StickerList { + type Request = StickerListRequest; type Response = StickerListResponse; const COMMAND: &'static str = "sticker list"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {} {}", Self::COMMAND, request.sticker_type, request.uri) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/sticker_set.rs b/src/commands/stickers/sticker_set.rs index 8983b5b..ea4b8ff 100644 --- a/src/commands/stickers/sticker_set.rs +++ b/src/commands/stickers/sticker_set.rs @@ -1,14 +1,39 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{StickerType, Uri}, }; pub struct StickerSet; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct StickerSetRequest { + pub sticker_type: StickerType, + pub uri: Uri, + pub name: String, + pub value: String, +} + impl Command for StickerSet { + type Request = StickerSetRequest; type Response = (); const COMMAND: &'static str = "sticker set"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {} {} {}", + Self::COMMAND, + request.sticker_type, + request.uri, + request.name, + request.value + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let sticker_type = sticker_type diff --git a/src/commands/stickers/stickernames.rs b/src/commands/stickers/stickernames.rs index d61515a..1b148d4 100644 --- a/src/commands/stickers/stickernames.rs +++ b/src/commands/stickers/stickernames.rs @@ -8,9 +8,14 @@ pub struct StickerNames; pub type StickerNamesResponse = Vec; impl Command for StickerNames { + type Request = (); type Response = StickerNamesResponse; const COMMAND: &'static str = "stickernames"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/stickers/stickernamestypes.rs b/src/commands/stickers/stickernamestypes.rs index 055bac5..0d43ebb 100644 --- a/src/commands/stickers/stickernamestypes.rs +++ b/src/commands/stickers/stickernamestypes.rs @@ -1,8 +1,11 @@ use std::collections::HashMap; -use crate::commands::{ - Command, Request, RequestParserResult, ResponseAttributes, - ResponseParserError, expect_property_type, +use crate::{ + commands::{ + Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError, + expect_property_type, + }, + common::StickerType, }; pub struct StickerNamesTypes; @@ -10,9 +13,14 @@ pub struct StickerNamesTypes; pub type StickerNamesTypesResponse = HashMap>; impl Command for StickerNamesTypes { + type Request = StickerType; type Response = StickerNamesTypesResponse; const COMMAND: &'static str = "stickernamestypes"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let sticker_type = parts.next().map(|s| s.to_string()); diff --git a/src/commands/stickers/stickertypes.rs b/src/commands/stickers/stickertypes.rs index 2c4d00d..eaba3b0 100644 --- a/src/commands/stickers/stickertypes.rs +++ b/src/commands/stickers/stickertypes.rs @@ -8,9 +8,14 @@ pub struct StickerTypes; pub type StickerTypesResponse = Vec; impl Command for StickerTypes { + type Request = (); type Response = StickerTypesResponse; const COMMAND: &'static str = "stickertypes"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { debug_assert!(parts.next().is_none()); diff --git a/src/commands/stored_playlists/listplaylist.rs b/src/commands/stored_playlists/listplaylist.rs index f0c512f..a2f890f 100644 --- a/src/commands/stored_playlists/listplaylist.rs +++ b/src/commands/stored_playlists/listplaylist.rs @@ -1,19 +1,36 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, expect_property_type, }, - common::PlaylistName, + common::{PlaylistName, WindowRange}, }; pub struct ListPlaylist; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ListPlaylistRequest { + name: PlaylistName, + range: Option, +} + pub type ListPlaylistResponse = Vec; impl Command for ListPlaylist { + type Request = ListPlaylistRequest; type Response = ListPlaylistResponse; const COMMAND: &'static str = "listplaylist"; + fn serialize_request(&self, request: Self::Request) -> String { + if let Some(range) = request.range { + format!("{} {} {}", Self::COMMAND, request.name, range) + } else { + format!("{} {}", Self::COMMAND, request.name) + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name diff --git a/src/commands/stored_playlists/listplaylistinfo.rs b/src/commands/stored_playlists/listplaylistinfo.rs index 88dd60f..168c469 100644 --- a/src/commands/stored_playlists/listplaylistinfo.rs +++ b/src/commands/stored_playlists/listplaylistinfo.rs @@ -1,17 +1,33 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - common::PlaylistName, + common::{PlaylistName, WindowRange}, }; pub struct ListPlaylistInfo; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct ListPlaylistInfoRequest { + name: PlaylistName, + range: Option, +} + impl Command for ListPlaylistInfo { + type Request = ListPlaylistInfoRequest; type Response = (); const COMMAND: &'static str = "listplaylistinfo"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.range { + Some(range) => format!("{} {} {}", Self::COMMAND, request.name, range), + None => format!("{} {}", Self::COMMAND, request.name), + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name diff --git a/src/commands/stored_playlists/listplaylists.rs b/src/commands/stored_playlists/listplaylists.rs index 94b171c..b15b553 100644 --- a/src/commands/stored_playlists/listplaylists.rs +++ b/src/commands/stored_playlists/listplaylists.rs @@ -5,9 +5,14 @@ use crate::commands::{ pub struct ListPlaylists; impl Command for ListPlaylists { + type Request = (); type Response = (); const COMMAND: &'static str = "listplaylists"; + fn serialize_request(&self, _request: Self::Request) -> String { + Self::COMMAND.to_string() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> 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 eda89a7..00a5d27 100644 --- a/src/commands/stored_playlists/load.rs +++ b/src/commands/stored_playlists/load.rs @@ -1,14 +1,44 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{PlaylistName, SongPosition, WindowRange}, }; pub struct Load; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct LoadRequest { + name: PlaylistName, + range: Option, + position: Option, +} + impl Command for Load { + type Request = LoadRequest; type Response = (); const COMMAND: &'static str = "load"; + fn serialize_request(&self, request: Self::Request) -> String { + match (request.range, request.position) { + (Some(range), Some(pos)) => { + format!("{} {} {} {}", Self::COMMAND, request.name, range, pos) + } + (Some(range), None) => { + format!("{} {} {}", Self::COMMAND, request.name, range) + } + (None, Some(pos)) => { + format!("{} {} {}", Self::COMMAND, request.name, pos) + } + (None, None) => { + format!("{} {}", Self::COMMAND, request.name) + } + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/playlistadd.rs b/src/commands/stored_playlists/playlistadd.rs index d5f6ff4..291ece1 100644 --- a/src/commands/stored_playlists/playlistadd.rs +++ b/src/commands/stored_playlists/playlistadd.rs @@ -1,14 +1,49 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{PlaylistName, SongPosition, Uri}, }; pub struct PlaylistAdd; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaylistAddRequest { + pub playlist_name: PlaylistName, + pub uri: Uri, + pub position: Option, +} + impl Command for PlaylistAdd { + type Request = PlaylistAddRequest; type Response = (); const COMMAND: &'static str = "playlistadd"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.position { + Some(pos) => { + format!( + "{} {} {} {}", + Self::COMMAND, + request.playlist_name, + request.uri, + pos + ) + } + None => { + format!( + "{} {} {}", + Self::COMMAND, + request.playlist_name, + request.uri + ) + } + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/playlistclear.rs b/src/commands/stored_playlists/playlistclear.rs index 95137f6..576ca92 100644 --- a/src/commands/stored_playlists/playlistclear.rs +++ b/src/commands/stored_playlists/playlistclear.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::PlaylistName, }; pub struct PlaylistClear; impl Command for PlaylistClear { + type Request = PlaylistName; type Response = (); const COMMAND: &'static str = "playlistclear"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/playlistdelete.rs b/src/commands/stored_playlists/playlistdelete.rs index fa7b4ec..7ecd1e8 100644 --- a/src/commands/stored_playlists/playlistdelete.rs +++ b/src/commands/stored_playlists/playlistdelete.rs @@ -1,14 +1,35 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{OneOrRange, PlaylistName}, }; pub struct PlaylistDelete; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaylistDeleteRequest { + pub playlist_name: PlaylistName, + pub position: OneOrRange, +} + impl Command for PlaylistDelete { + type Request = PlaylistDeleteRequest; type Response = (); const COMMAND: &'static str = "playlistdelete"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {}", + Self::COMMAND, + request.playlist_name, + request.position + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/playlistlength.rs b/src/commands/stored_playlists/playlistlength.rs index 9ff541d..e76e0c1 100644 --- a/src/commands/stored_playlists/playlistlength.rs +++ b/src/commands/stored_playlists/playlistlength.rs @@ -2,9 +2,12 @@ use std::collections::HashMap; use serde::{Deserialize, Serialize}; -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, get_and_parse_property, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, get_and_parse_property, + }, + common::PlaylistName, }; pub struct PlaylistLength; @@ -16,9 +19,14 @@ pub struct PlaylistLengthResponse { } impl Command for PlaylistLength { + type Request = PlaylistName; type Response = PlaylistLengthResponse; const COMMAND: &'static str = "playlistlength"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/playlistmove.rs b/src/commands/stored_playlists/playlistmove.rs index 8319bff..f8697d5 100644 --- a/src/commands/stored_playlists/playlistmove.rs +++ b/src/commands/stored_playlists/playlistmove.rs @@ -1,14 +1,44 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{OneOrRange, PlaylistName, SongPosition}, }; pub struct PlaylistMove; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct PlaylistMoveRequest { + pub playlist_name: PlaylistName, + pub from: Option, + pub to: SongPosition, +} + impl Command for PlaylistMove { + type Request = PlaylistMoveRequest; type Response = (); const COMMAND: &'static str = "playlistmove"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.from { + Some(from) => { + format!( + "{} {} {} {}", + Self::COMMAND, + request.playlist_name, + from, + request.to + ) + } + None => { + format!("{} {} {}", Self::COMMAND, request.playlist_name, request.to) + } + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/rename.rs b/src/commands/stored_playlists/rename.rs index 5d3a371..fc32756 100644 --- a/src/commands/stored_playlists/rename.rs +++ b/src/commands/stored_playlists/rename.rs @@ -1,3 +1,5 @@ +use serde::{Deserialize, Serialize}; + use crate::commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, @@ -5,10 +7,26 @@ use crate::commands::{ pub struct Rename; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RenameRequest { + pub old_name: String, + pub new_name: String, +} + impl Command for Rename { + type Request = RenameRequest; type Response = (); const COMMAND: &'static str = "rename"; + fn serialize_request(&self, request: Self::Request) -> String { + format!( + "{} {} {}", + Self::COMMAND, + request.old_name, + request.new_name + ) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let old_name = parts .next() diff --git a/src/commands/stored_playlists/rm.rs b/src/commands/stored_playlists/rm.rs index 621b25f..c2ea26a 100644 --- a/src/commands/stored_playlists/rm.rs +++ b/src/commands/stored_playlists/rm.rs @@ -1,14 +1,22 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::PlaylistName, }; pub struct Rm; impl Command for Rm { + type Request = PlaylistName; type Response = (); const COMMAND: &'static str = "rm"; + fn serialize_request(&self, request: Self::Request) -> String { + format!("{} {}", Self::COMMAND, request) + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/save.rs b/src/commands/stored_playlists/save.rs index 28dc86a..882d49c 100644 --- a/src/commands/stored_playlists/save.rs +++ b/src/commands/stored_playlists/save.rs @@ -1,14 +1,37 @@ -use crate::commands::{ - Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, - ResponseParserError, +use serde::{Deserialize, Serialize}; + +use crate::{ + commands::{ + Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, + ResponseParserError, + }, + common::{PlaylistName, SaveMode}, }; pub struct Save; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SaveRequest { + pub playlist_name: PlaylistName, + pub mode: Option, +} + impl Command for Save { + type Request = SaveRequest; type Response = (); const COMMAND: &'static str = "save"; + fn serialize_request(&self, request: Self::Request) -> String { + match request.mode { + Some(mode) => { + format!("{} {} {}", Self::COMMAND, request.playlist_name, mode) + } + None => { + format!("{} {}", Self::COMMAND, request.playlist_name) + } + } + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let playlist_name = parts .next() diff --git a/src/commands/stored_playlists/searchplaylist.rs b/src/commands/stored_playlists/searchplaylist.rs index d102382..0246777 100644 --- a/src/commands/stored_playlists/searchplaylist.rs +++ b/src/commands/stored_playlists/searchplaylist.rs @@ -1,18 +1,32 @@ +use serde::{Deserialize, Serialize}; + use crate::{ commands::{ Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, }, - common::PlaylistName, - filter::parse_filter, + common::{PlaylistName, WindowRange}, + filter::{Filter, parse_filter}, }; pub struct SearchPlaylist; +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct SearchPlaylistRequest { + pub name: PlaylistName, + pub filter: Filter, + pub range: Option, +} + impl Command for SearchPlaylist { + type Request = SearchPlaylistRequest; type Response = (); const COMMAND: &'static str = "searchplaylist"; + fn serialize_request(&self, request: Self::Request) -> String { + unimplemented!() + } + fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> { let name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let name = name