1 Commits

Author SHA1 Message Date
aa56ba5a7f WIP 2025-02-25 21:36:34 +01:00
163 changed files with 1479 additions and 5052 deletions

View File

@@ -83,8 +83,8 @@ jobs:
target: ${{ gitea.ref_name }}/coverage/ target: ${{ gitea.ref_name }}/coverage/
username: gitea-web username: gitea-web
ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }} ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }}
host: pages.pvv.ntnu.no host: bekkalokk.pvv.ntnu.no
known-hosts: "pages.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2QjfFB+city1SYqltkVqWACfo1j37k+oQQfj13mtgg" known-hosts: "bekkalokk.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEI6VSaDrMG8+flg4/AeHlAFIen8RUzWh6URQKqFegSx"
docs: docs:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -107,6 +107,6 @@ jobs:
target: ${{ gitea.ref_name }}/docs/ target: ${{ gitea.ref_name }}/docs/
username: gitea-web username: gitea-web
ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }} ssh-key: ${{ secrets.WEB_SYNC_SSH_KEY }}
host: pages.pvv.ntnu.no host: bekkalokk.pvv.ntnu.no
known-hosts: "pages.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH2QjfFB+city1SYqltkVqWACfo1j37k+oQQfj13mtgg" known-hosts: "bekkalokk.pvv.ntnu.no ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEI6VSaDrMG8+flg4/AeHlAFIen8RUzWh6URQKqFegSx"

4
.gitignore vendored
View File

@@ -1,5 +1,3 @@
/target /target
result result
result-* result-*
Cargo.lock

95
Cargo.lock generated Normal file
View File

@@ -0,0 +1,95 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "diff"
version = "0.1.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8"
[[package]]
name = "empidee"
version = "0.1.0"
dependencies = [
"indoc",
"pretty_assertions",
"serde",
]
[[package]]
name = "indoc"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b248f5224d1d606005e02c97f5aa4e88eeb230488bcc03bc9ca4d7991399f2b5"
[[package]]
name = "pretty_assertions"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d"
dependencies = [
"diff",
"yansi",
]
[[package]]
name = "proc-macro2"
version = "1.0.88"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c3a7fc5db1e57d5a779a352c8cdb57b29aa4c40cc69c3a68a7fedc815fbf2f9"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b5b9d34b8991d19d98081b46eacdd8eb58c6f2b201139f7c5f643cc155a633af"
dependencies = [
"proc-macro2",
]
[[package]]
name = "serde"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c8e3592472072e6e22e0a54d5904d9febf8508f65fb8552499a1abc7d1078c3a"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.210"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "243902eda00fad750862fc144cea25caca5e20d615af0a81bee94ca738f1df1f"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "syn"
version = "2.0.82"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "unicode-ident"
version = "1.0.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e91b56cd4cadaeb79bbf1a5645f6b4f8dc5bde8834ad5894a8db35fda9efa1fe"
[[package]]
name = "yansi"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049"

View File

@@ -7,17 +7,12 @@ authors = [
description = "A rust implementation of the mpd protocol, both client and serverside" description = "A rust implementation of the mpd protocol, both client and serverside"
repository = "https://git.pvv.ntnu.no/Grzegorz/empidee" repository = "https://git.pvv.ntnu.no/Grzegorz/empidee"
documentation = "https://pages.pvv.ntnu.no/Grzegorz/empidee/main/docs/empidee/" documentation = "https://pages.pvv.ntnu.no/Grzegorz/empidee/main/docs/empidee/"
edition = "2024" edition = "2021"
rust-version = "1.85.0" rust-version = "1.83.0"
[dependencies] [dependencies]
chrono = { version = "0.4.42", features = ["serde"] } serde = { version = "1.0.210", features = ["derive"] }
lalrpop-util = { version = "0.22.2", features = ["lexer"] }
serde = { version = "1.0.228", features = ["derive"] }
[dev-dependencies] [dev-dependencies]
indoc = "2.0.7" indoc = "2.0.5"
pretty_assertions = "1.4.1" pretty_assertions = "1.4.1"
[build-dependencies]
lalrpop = "0.22.2"

View File

@@ -1,8 +0,0 @@
fn main() {
lalrpop::process_root().unwrap();
// let debug_mode = std::env::var("PROFILE").unwrap() == "debug";
// lalrpop::Configuration::new()
// .emit_comments(debug_mode)
// .process()
// .unwrap();
}

12
flake.lock generated
View File

@@ -2,11 +2,11 @@
"nodes": { "nodes": {
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1763421233, "lastModified": 1739866667,
"narHash": "sha256-Stk9ZYRkGrnnpyJ4eqt9eQtdFWRRIvMxpNRf4sIegnw=", "narHash": "sha256-EO1ygNKZlsAC9avfcwHkKGMsmipUk1Uc0TbrEZpkn64=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "89c2b2330e733d6cdb5eae7b899326930c2c0648", "rev": "73cf49b8ad837ade2de76f87eb53fc85ed5d4680",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -29,11 +29,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1763692705, "lastModified": 1740277845,
"narHash": "sha256-tCKCyMYU0Vy+ph/xswlNsYXXjnFVweWBV+ew/5FS9tA=", "narHash": "sha256-NNU0CdiaSbAeZ8tpDG4aFi9qtcdlItRvk8Xns9oBrVU=",
"owner": "oxalica", "owner": "oxalica",
"repo": "rust-overlay", "repo": "rust-overlay",
"rev": "6fbf5d328dce1828d887b8ee7d44a785196a34e7", "rev": "f933070c29f9c1c5457447a51903f27f76ebb519",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -36,7 +36,6 @@
default = pkgs.mkShell { default = pkgs.mkShell {
nativeBuildInputs = [ nativeBuildInputs = [
toolchain toolchain
pkgs.cargo-edit
]; ];
RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library"; RUST_SRC_PATH = "${toolchain}/lib/rustlib/src/rust/library";

View File

@@ -1 +0,0 @@
style_edition = "2024"

View File

@@ -1,4 +1,9 @@
use crate::{Request, request_tokenizer::RequestTokenizer, response_tokenizer::ResponseAttributes}; use std::{
collections::{HashMap, HashSet},
str::SplitWhitespace,
};
use crate::Request;
mod audio_output_devices; mod audio_output_devices;
mod client_to_client; mod client_to_client;
@@ -28,69 +33,40 @@ pub use reflection::*;
pub use stickers::*; pub use stickers::*;
pub use stored_playlists::*; pub use stored_playlists::*;
/// A trait modelling the request/response pair of a single MPD command.
pub trait Command { 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; type Response;
// The command name used within the protocol
/// The command name used within the protocol
const COMMAND: &'static str; const COMMAND: &'static str;
/// Serialize the request into a string. // TODO: `parse_request` should be using a more custom splitter, that can handle
/// This should optimally produce an input that can be parsed by [`parse_request`] // quoted strings and escape characters. This is what mpd uses to provide arguments
fn serialize_request(&self, request: Self::Request) -> String; // with spaces and whatnot.
/// Serialize the request into a bytestring. // A function to parse the remaining parts of the command, split by whitespace
fn serialize_request_to_bytes(&self, request: Self::Request) -> Vec<u8> { fn parse_request(parts: SplitWhitespace<'_>) -> RequestParserResult<'_>;
self.serialize_request(request).into_bytes()
}
// fn serialize_response(&self) -> String;
// fn serialize_response_to_bytes(&self) -> Vec<u8> {
// self.serialize_response().into_bytes()
// }
/// Parse the request from its tokenized parts.
///
/// Note that this assumes only the parts after the command name are passed in, e.g.
///
/// ```ignore
/// arg1 "arg2 arg3"
/// ```
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_>;
/// Parse the raw request string into a request and the remaining unparsed string.
/// This assumes the raw string starts with the command name, e.g.
///
/// ```ignore
/// command_name arg1 "arg2 arg3"
/// ```
fn parse_raw_request(raw: &str) -> RequestParserResult<'_> { fn parse_raw_request(raw: &str) -> RequestParserResult<'_> {
let (line, rest) = raw let (line, rest) = raw
.split_once('\n') .split_once('\n')
.ok_or(RequestParserError::UnexpectedEOF)?; .ok_or(RequestParserError::UnexpectedEOF)?;
let mut tokenized = RequestTokenizer::new(line); let mut parts = line.split_whitespace();
let command_name = tokenized let command_name = parts
.next() .next()
.ok_or(RequestParserError::SyntaxError(0, line.to_string()))? .ok_or(RequestParserError::SyntaxError(0, line.to_string()))?
.trim(); .trim();
debug_assert!(command_name == Self::COMMAND); debug_assert!(command_name == Self::COMMAND);
Self::parse_request(tokenized).map(|(req, _)| (req, rest)) Self::parse_request(parts).map(|(req, _)| (req, rest))
} }
/// Parse the response from its tokenized parts. fn parse_response(
/// See also [`parse_raw_response`]. parts: ResponseAttributes<'_>,
fn parse_response(parts: ResponseAttributes<'_>) -> ResponseParserResult<'_, Self::Response>; ) -> Result<Self::Response, ResponseParserError<'_>>;
/// Parse the raw response string into a response. fn parse_raw_response(raw: &str) -> Result<Self::Response, ResponseParserError<'_>> {
fn parse_raw_response(raw: &str) -> ResponseParserResult<'_, Self::Response> { Self::parse_response(ResponseAttributes::new(raw)?)
Self::parse_response(ResponseAttributes::new(raw))
} }
} }
@@ -106,8 +82,6 @@ pub enum RequestParserError {
MissingNewline, MissingNewline,
} }
pub type ResponseParserResult<'a, T> = Result<T, ResponseParserError<'a>>;
// TODO: should these be renamed to fit the mpd docs? // TODO: should these be renamed to fit the mpd docs?
// "Attribute" instead of "Property"? // "Attribute" instead of "Property"?
#[derive(Debug, Clone, PartialEq)] #[derive(Debug, Clone, PartialEq)]
@@ -122,9 +96,6 @@ pub enum ResponseParserError<'a> {
/// A property was found in the response that was not expected. /// A property was found in the response that was not expected.
UnexpectedProperty(&'a str), UnexpectedProperty(&'a str),
/// A property was found multiple times in the response, but was only expected once.
DuplicateProperty(&'a str),
/// The property value is parsable, but the value is invalid or nonsensical. /// The property value is parsable, but the value is invalid or nonsensical.
InvalidProperty(&'a str, &'a str), InvalidProperty(&'a str, &'a str),
@@ -136,151 +107,297 @@ pub enum ResponseParserError<'a> {
// MissingNewline, // MissingNewline,
} }
pub type GenericResponseResult<'a> = Result<GenericResponse<'a>, &'a str>;
pub type GenericResponse<'a> = HashMap<&'a str, GenericResponseValue<'a>>;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GenericResponseValue<'a> {
Text(&'a str),
Binary(&'a [u8]),
// Many(Vec<GenericResponseValue<'a>>),
}
#[derive(Debug, Clone, PartialEq)]
pub struct ResponseAttributes<'a>(Vec<(&'a str, GenericResponseValue<'a>)>);
impl<'a> ResponseAttributes<'a> {
pub fn new(raw: &'a str) -> Result<Self, ResponseParserError<'a>> {
let mut parts = Vec::new();
let mut lines = raw.lines();
loop {
let line = lines.next().ok_or(ResponseParserError::UnexpectedEOF)?;
if line.is_empty() {
println!("Warning: empty line in response");
continue;
}
if line == "OK" {
break;
}
let mut keyval = line.splitn(2, ": ");
let key = keyval
.next()
.ok_or(ResponseParserError::SyntaxError(0, line))?;
// TODO: handle binary data, also verify binarylimit
let value = keyval
.next()
.ok_or(ResponseParserError::SyntaxError(0, line))?;
parts.push((key, GenericResponseValue::Text(value)));
}
Ok(parts.into())
}
// pub fn get<'a>(&self, key: &str) -> Option<&GenericResponseValue<'a>> {
// self.0.iter().find_map(|(k, v)| if k == &key { Some(v) } else { None })
// }
}
impl ResponseAttributes<'_> {
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
}
impl<'a> From<HashMap<&'a str, GenericResponseValue<'a>>> for ResponseAttributes<'a> {
fn from(map: HashMap<&'a str, GenericResponseValue<'a>>) -> Self {
Self(map.into_iter().collect())
}
}
impl<'a> From<ResponseAttributes<'a>> for HashMap<&'a str, GenericResponseValue<'a>> {
fn from(val: ResponseAttributes<'a>) -> Self {
debug_assert!({
let mut uniq = HashSet::new();
val.0.iter().all(move |x| uniq.insert(*x))
});
val.0.into_iter().collect()
}
}
impl<'a> From<ResponseAttributes<'a>> for Vec<(&'a str, GenericResponseValue<'a>)> {
fn from(val: ResponseAttributes<'a>) -> Self {
val.0
}
}
impl<'a> From<Vec<(&'a str, GenericResponseValue<'a>)>> for ResponseAttributes<'a> {
fn from(val: Vec<(&'a str, GenericResponseValue<'a>)>) -> Self {
Self(val)
}
}
// TODO: There should probably be a helper that lets you extract and verify one, two or maybe
// three properties without having to allocate a hashmap to get a nice API. We can retrieve
// the properties by name with a loop on the inner vec.
/*******************/
/* Parsing Helpers */
/*******************/ /*******************/
pub const COMMAND_NAMES: &[&str] = &[ macro_rules! _expect_property_type {
// Audio output devices ($property:expr, $name:expr, $variant:ident) => {
DisableOutput::COMMAND, match $property {
EnableOutput::COMMAND, Some(crate::commands::GenericResponseValue::$variant(value)) => Some(value),
Outputs::COMMAND, Some(value) => {
OutputSet::COMMAND, let actual_type = match value {
ToggleOutput::COMMAND, crate::commands::GenericResponseValue::Text(_) => "Text",
// Client to client crate::commands::GenericResponseValue::Binary(_) => "Binary",
Channels::COMMAND, };
ReadMessages::COMMAND, return Err(
SendMessage::COMMAND, crate::commands::ResponseParserError::UnexpectedPropertyType(
Subscribe::COMMAND, $name,
Unsubscribe::COMMAND, actual_type,
// Connection settings ),
BinaryLimit::COMMAND, );
Close::COMMAND, }
Kill::COMMAND, None => None,
Password::COMMAND, }
Ping::COMMAND, };
Protocol::COMMAND, }
ProtocolAll::COMMAND,
ProtocolAvailable::COMMAND, macro_rules! _parse_optional_property_type {
ProtocolClear::COMMAND, ($name:expr, $property:expr) => {
ProtocolDisable::COMMAND, $property
ProtocolEnable::COMMAND, .map(|value| {
TagTypes::COMMAND, value.parse().map_err(|_| {
TagTypesAll::COMMAND, crate::commands::ResponseParserError::InvalidProperty($name, value)
TagTypesAvailable::COMMAND, })
TagTypesClear::COMMAND, })
TagTypesDisable::COMMAND, .transpose()?
TagTypesEnable::COMMAND, };
TagTypesReset::COMMAND, }
// Controlling playback
Next::COMMAND, macro_rules! _unwrap_optional_property_type {
Pause::COMMAND, ($name:expr, $property:expr) => {
Play::COMMAND, match $property {
PlayId::COMMAND, Some(value) => value,
Previous::COMMAND, None => return Err(crate::commands::ResponseParserError::MissingProperty($name)),
Seek::COMMAND, }
SeekCur::COMMAND, };
SeekId::COMMAND, }
Stop::COMMAND,
// Mounts and neighbors macro_rules! expect_optional_property_type {
ListMounts::COMMAND, ($property:expr, $name:expr, $variant:ident) => {
ListNeighbors::COMMAND, crate::commands::_expect_property_type!($property, $name, $variant)
Mount::COMMAND, };
Unmount::COMMAND, }
// Music database
AlbumArt::COMMAND, macro_rules! expect_property_type {
Count::COMMAND, ($property:expr, $name:expr, $variant:ident) => {{
Find::COMMAND, let prop = crate::commands::_expect_property_type!($property, $name, $variant);
FindAdd::COMMAND, crate::commands::_unwrap_optional_property_type!($name, prop)
GetFingerprint::COMMAND, }};
List::COMMAND, }
ListAll::COMMAND,
ListAllInfo::COMMAND, macro_rules! get_optional_property {
ListFiles::COMMAND, ($parts:expr, $name:literal, $variant:ident) => {
LsInfo::COMMAND, crate::commands::_expect_property_type!({ $parts.get($name).map(|v| *v) }, $name, $variant)
ReadComments::COMMAND, };
ReadPicture::COMMAND, }
Rescan::COMMAND,
Search::COMMAND, macro_rules! get_property {
SearchAdd::COMMAND, ($parts:expr, $name:literal, $variant:ident) => {{
SearchAddPl::COMMAND, let prop = crate::commands::_expect_property_type!(
SearchCount::COMMAND, { $parts.get($name).map(|v| *v) },
Update::COMMAND, $name,
// Partition commands $variant
DelPartition::COMMAND, );
ListPartitions::COMMAND, crate::commands::_unwrap_optional_property_type!($name, prop)
MoveOutput::COMMAND, }};
NewPartition::COMMAND, }
Partition::COMMAND,
// Playback options macro_rules! get_and_parse_optional_property {
Consume::COMMAND, ($parts:ident, $name:literal, $variant:ident) => {{
Crossfade::COMMAND, let prop = crate::commands::_expect_property_type!(
GetVol::COMMAND, { $parts.get($name).map(|v| *v) },
MixRampDb::COMMAND, $name,
MixRampDelay::COMMAND, $variant
Random::COMMAND, );
Repeat::COMMAND, crate::commands::_parse_optional_property_type!($name, prop)
ReplayGainMode::COMMAND, }};
ReplayGainStatus::COMMAND, }
SetVol::COMMAND,
Single::COMMAND, macro_rules! get_and_parse_property {
Volume::COMMAND, ($parts:ident, $name:literal, $variant:ident) => {{
// Querying mpd status let prop = crate::commands::_expect_property_type!(
ClearError::COMMAND, { $parts.get($name).map(|v| *v) },
CurrentSong::COMMAND, $name,
Idle::COMMAND, $variant
Stats::COMMAND, );
Status::COMMAND, let prop = crate::commands::_parse_optional_property_type!($name, prop);
// Queue crate::commands::_unwrap_optional_property_type!($name, prop)
Add::COMMAND, }};
AddId::COMMAND, }
AddTagId::COMMAND,
Clear::COMMAND, macro_rules! get_next_optional_property {
ClearTagId::COMMAND, ($parts:ident, $variant:ident) => {
Delete::COMMAND, match $parts.next() {
DeleteId::COMMAND, Some((name, value)) => {
Move::COMMAND, crate::commands::_expect_property_type!({ Some(value) }, name, $variant)
MoveId::COMMAND, .map(|value| (name, value))
Playlist::COMMAND, }
PlaylistFind::COMMAND, None => None,
PlaylistId::COMMAND, }
PlaylistInfo::COMMAND, };
PlaylistSearch::COMMAND, }
PlChanges::COMMAND,
PlChangesPosId::COMMAND, macro_rules! get_next_property {
Prio::COMMAND, ($parts:ident, $variant:ident) => {
PrioId::COMMAND, match $parts.next() {
RangeId::COMMAND, Some((name, value)) => (
Shuffle::COMMAND, name,
Swap::COMMAND, crate::commands::_expect_property_type!({ Some(value) }, name, $variant).unwrap(),
SwapId::COMMAND, ),
// Reflection None => return Err(crate::commands::ResponseParserError::UnexpectedEOF),
Commands::COMMAND, }
Config::COMMAND, };
Decoders::COMMAND, }
NotCommands::COMMAND,
UrlHandlers::COMMAND, macro_rules! get_next_and_parse_optional_property {
// Stickers ($parts:ident, $variant:ident) => {
StickerDec::COMMAND, match $parts.next() {
StickerDelete::COMMAND, Some((name, value)) => {
StickerFind::COMMAND, let prop = crate::commands::_expect_property_type!({ Some(value) }, name, $variant);
StickerGet::COMMAND, prop.map(|value| {
StickerInc::COMMAND, (
StickerList::COMMAND, name,
StickerSet::COMMAND, crate::commands::_parse_optional_property_type!(name, value),
StickerNames::COMMAND, )
StickerNamesTypes::COMMAND, })
StickerTypes::COMMAND, }
// Stored playlists None => None,
ListPlaylist::COMMAND, }
ListPlaylistInfo::COMMAND, };
ListPlaylists::COMMAND, }
Load::COMMAND,
PlaylistAdd::COMMAND, macro_rules! get_next_and_parse_property {
PlaylistClear::COMMAND, ($parts:ident, $variant:ident) => {
PlaylistDelete::COMMAND, match $parts.next() {
PlaylistLength::COMMAND, Some((name, value)) => {
PlaylistMove::COMMAND, let prop = crate::commands::_expect_property_type!({ Some(value) }, name, $variant);
Rename::COMMAND, let prop = crate::commands::_parse_optional_property_type!(name, prop);
Rm::COMMAND, (
Save::COMMAND, name,
SearchPlaylist::COMMAND, crate::commands::_unwrap_optional_property_type!(name, prop),
]; )
}
None => return Err(crate::commands::ResponseParserError::UnexpectedEOF),
}
};
}
pub(crate) use _expect_property_type;
pub(crate) use _parse_optional_property_type;
pub(crate) use _unwrap_optional_property_type;
pub(crate) use expect_property_type;
// pub(crate) use expect_optional_property_type;
pub(crate) use get_and_parse_optional_property;
pub(crate) use get_and_parse_property;
// pub(crate) use get_next_and_parse_optional_property;
pub(crate) use get_next_and_parse_property;
// pub(crate) use get_next_optional_property;
pub(crate) use get_next_property;
pub(crate) use get_optional_property;
pub(crate) use get_property;
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(debug_assertions)]
fn test_valid_hashmap_uniqueness_assert() {
let valid_maplike_attrs: ResponseAttributes = vec![
("a", GenericResponseValue::Text("1")),
("A", GenericResponseValue::Text("2")),
("A ", GenericResponseValue::Text("3")),
("b", GenericResponseValue::Text("4")),
]
.into();
let map: HashMap<_, _> = valid_maplike_attrs.into();
assert_eq!(map.len(), 4);
}
#[test]
#[cfg(debug_assertions)]
#[should_panic]
fn test_invalid_hashmap_uniqueness_assert() {
let invalid_maplike_attrs: ResponseAttributes = vec![
("a", GenericResponseValue::Text("1")),
("b", GenericResponseValue::Text("2")),
("c", GenericResponseValue::Text("3")),
("a", GenericResponseValue::Text("4")),
]
.into();
let map: HashMap<_, _> = invalid_maplike_attrs.into();
assert_eq!(map.len(), 4);
}
}

View File

@@ -1,32 +1,20 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::AudioOutputId, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct DisableOutput; pub struct DisableOutput;
pub type DisableOutputRequest = AudioOutputId;
impl Command for DisableOutput { impl Command for DisableOutput {
type Request = DisableOutputRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "disableoutput"; const COMMAND: &'static str = "disableoutput";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let output_id = output_id
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, output_id.to_owned()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::DisableOutput(output_id), "")) Ok((Request::DisableOutput(output_id.to_string()), ""))
} }
fn parse_response( fn parse_response(

View File

@@ -1,37 +1,25 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::AudioOutputId, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct EnableOutput; pub struct EnableOutput;
pub type EnableOutputRequest = AudioOutputId;
impl Command for EnableOutput { impl Command for EnableOutput {
type Request = EnableOutputRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "enableoutput"; const COMMAND: &'static str = "enableoutput";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let output_id = output_id
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, output_id.to_owned()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::EnableOutput(output_id), "")) Ok((Request::EnableOutput(output_id.to_string()), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -2,18 +2,15 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
common::AudioOutputId,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct Outputs; pub struct Outputs;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct Output { pub struct Output {
pub id: AudioOutputId, pub id: u64,
pub name: String, pub name: String,
pub plugin: String, pub plugin: String,
pub enabled: bool, pub enabled: bool,
@@ -23,147 +20,17 @@ pub struct Output {
pub type OutputsResponse = Vec<Output>; pub type OutputsResponse = Vec<Output>;
impl Command for Outputs { impl Command for Outputs {
type Request = ();
type Response = OutputsResponse; type Response = OutputsResponse;
const COMMAND: &'static str = "outputs"; const COMMAND: &'static str = "outputs";
fn serialize_request(&self, _: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Outputs, "")) Ok((Request::Outputs, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, _parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let mut outputs = Vec::new(); unimplemented!()
let mut id: Option<AudioOutputId> = None;
let mut name: Option<String> = None;
let mut plugin: Option<String> = None;
let mut enabled: Option<bool> = None;
let mut attributes: HashMap<String, String> = HashMap::new();
for (k, v) in parts.into_vec()?.into_iter() {
match k {
"outputid" => {
// Reset and store the previous output if all fields are present
if let (Some(id), Some(name), Some(plugin), Some(enabled)) =
(id.take(), name.take(), plugin.take(), enabled.take())
{
outputs.push(Output {
id,
name,
plugin,
enabled,
attribute: attributes,
});
}
attributes = HashMap::new();
let id_s = expect_property_type!(Some(v), k, Text);
id = Some(
id_s.parse()
.map_err(|_| ResponseParserError::SyntaxError(0, id_s))?,
);
}
"outputname" => {
name = Some(expect_property_type!(Some(v), k, Text).to_string());
}
"plugin" => {
plugin = Some(expect_property_type!(Some(v), k, Text).to_string());
}
"outputenabled" => {
let val_s = expect_property_type!(Some(v), k, Text);
let val: u64 = val_s
.parse::<u64>()
.map_err(|_| ResponseParserError::SyntaxError(0, val_s))?;
enabled = Some(val != 0);
}
"attribute" => {
let value = expect_property_type!(Some(v), k, Text);
let mut parts = value.splitn(2, '=');
let attr_key = parts
.next()
.ok_or(ResponseParserError::SyntaxError(0, value))?
.to_string();
let attr_value = parts
.next()
.ok_or(ResponseParserError::SyntaxError(0, value))?
.to_string();
attributes.insert(attr_key, attr_value);
}
_ => {
return Err(ResponseParserError::UnexpectedProperty(k));
}
}
}
// Store the last output if all fields are present
if let (Some(id), Some(name), Some(plugin), Some(enabled)) =
(id.take(), name.take(), plugin.take(), enabled.take())
{
outputs.push(Output {
id,
name,
plugin,
enabled,
attribute: attributes,
});
}
Ok(outputs)
}
}
#[cfg(test)]
mod tests {
use indoc::indoc;
use super::*;
#[test]
fn test_parse_response() {
let input = indoc! {"
outputid: 0
outputname: PipeWire Sound Server
plugin: pipewire
outputenabled: 1
outputid: 1
outputname: Visualizer feed
plugin: fifo
outputenabled: 1
attribute: fifo_path=/tmp/empidee-visualizer.fifo
OK
"};
let result = Outputs::parse_raw_response(input);
assert_eq!(
result,
Ok(vec![
Output {
id: 0,
name: "PipeWire Sound Server".to_string(),
plugin: "pipewire".to_string(),
enabled: true,
attribute: HashMap::new(),
},
Output {
id: 1,
name: "Visualizer feed".to_string(),
plugin: "fifo".to_string(),
enabled: true,
attribute: {
let mut map = HashMap::new();
map.insert(
"fifo_path".to_string(),
"/tmp/empidee-visualizer.fifo".to_string(),
);
map
},
},
])
);
} }
} }

View File

@@ -1,41 +1,16 @@
use serde::{Deserialize, Serialize}; use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
use crate::{ ResponseParserError,
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError},
common::AudioOutputId,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct OutputSet; 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 { impl Command for OutputSet {
type Request = OutputSetRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "outputset"; const COMMAND: &'static str = "outputset";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!(
"{} {} {} {}",
Self::COMMAND,
request.output_id,
request.attribute_name,
request.attribute_value
)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let output_id = output_id
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, output_id.to_owned()))?;
let attribute_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let attribute_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let attribute_value = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let attribute_value = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
@@ -43,7 +18,7 @@ impl Command for OutputSet {
Ok(( Ok((
Request::OutputSet( Request::OutputSet(
output_id, output_id.to_string(),
attribute_name.to_string(), attribute_name.to_string(),
attribute_value.to_string(), attribute_value.to_string(),
), ),
@@ -53,7 +28,7 @@ impl Command for OutputSet {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,37 +1,25 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::AudioOutputId, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ToggleOutput; pub struct ToggleOutput;
pub type ToggleOutputRequest = AudioOutputId;
impl Command for ToggleOutput { impl Command for ToggleOutput {
type Request = ToggleOutputRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "toggleoutput"; const COMMAND: &'static str = "toggleoutput";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let output_id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let output_id = output_id
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, output_id.to_owned()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ToggleOutput(output_id), "")) Ok((Request::ToggleOutput(output_id.to_string()), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,29 +1,22 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
common::ChannelName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct Channels; pub struct Channels;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChannelsResponse { pub struct ChannelsResponse {
pub channels: Vec<ChannelName>, pub channels: Vec<String>,
} }
impl Command for Channels { impl Command for Channels {
type Request = ();
type Response = ChannelsResponse; type Response = ChannelsResponse;
const COMMAND: &'static str = "channels"; const COMMAND: &'static str = "channels";
fn serialize_request(&self, _: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Channels, "")) Ok((Request::Channels, ""))
@@ -31,16 +24,13 @@ impl Command for Channels {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; let parts: Vec<_> = parts.into();
let mut channel_names = Vec::with_capacity(parts.len()); let mut channel_names = Vec::with_capacity(parts.len());
for (key, value) in parts { for (key, value) in parts {
debug_assert!(key == "channels"); debug_assert!(key == "channels");
let channel_name = expect_property_type!(Some(value), "channels", Text); let channel_name = expect_property_type!(Some(value), "channels", Text);
let channel_name = channel_name channel_names.push(channel_name.to_string());
.parse()
.map_err(|_| ResponseParserError::SyntaxError(0, channel_name))?;
channel_names.push(channel_name);
} }
Ok(ChannelsResponse { Ok(ChannelsResponse {
@@ -67,11 +57,7 @@ mod tests {
assert_eq!( assert_eq!(
response, response,
ChannelsResponse { ChannelsResponse {
channels: vec![ channels: vec!["foo".to_string(), "bar".to_string(), "baz".to_string()]
"foo".parse().unwrap(),
"bar".parse().unwrap(),
"baz".parse().unwrap(),
]
} }
); );
} }

View File

@@ -1,32 +1,22 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
common::ChannelName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct ReadMessages; pub struct ReadMessages;
pub type ReadMessagesResponse = Vec<ReadMessagesResponseEntry>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReadMessagesResponseEntry { pub struct ReadMessagesResponse {
channel: ChannelName, pub messages: Vec<(String, String)>,
message: String,
} }
impl Command for ReadMessages { impl Command for ReadMessages {
type Request = ();
type Response = ReadMessagesResponse; type Response = ReadMessagesResponse;
const COMMAND: &'static str = "readmessages"; const COMMAND: &'static str = "readmessages";
fn serialize_request(&self, _: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ReadMessages, "")) Ok((Request::ReadMessages, ""))
@@ -34,8 +24,8 @@ impl Command for ReadMessages {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; let parts: Vec<_> = parts.into();
debug_assert!(parts.len() % 2 == 0); debug_assert!(parts.len() % 2 == 0);
let mut messages = Vec::with_capacity(parts.len() / 2); let mut messages = Vec::with_capacity(parts.len() / 2);
@@ -47,17 +37,13 @@ impl Command for ReadMessages {
debug_assert!(ckey == "channel"); debug_assert!(ckey == "channel");
debug_assert!(mkey == "message"); debug_assert!(mkey == "message");
let channel = expect_property_type!(Some(cvalue), "channel", Text); let channel = expect_property_type!(Some(cvalue), "channel", Text).to_string();
let channel = channel
.parse()
.map_err(|_| ResponseParserError::SyntaxError(0, channel))?;
let message = expect_property_type!(Some(mvalue), "message", Text).to_string(); let message = expect_property_type!(Some(mvalue), "message", Text).to_string();
messages.push(ReadMessagesResponseEntry { channel, message }); messages.push((channel, message));
} }
Ok(messages) Ok(ReadMessagesResponse { messages })
} }
} }
@@ -79,16 +65,12 @@ mod tests {
let result = ReadMessages::parse_raw_response(input); let result = ReadMessages::parse_raw_response(input);
assert_eq!( assert_eq!(
result, result,
Ok(vec![ Ok(ReadMessagesResponse {
ReadMessagesResponseEntry { messages: vec![
channel: "channel1".parse().unwrap(), ("channel1".to_string(), "message1".to_string()),
message: "message1".to_string(), ("channel2".to_string(), "message2".to_string()),
}, ]
ReadMessagesResponseEntry { })
channel: "channel2".parse().unwrap(),
message: "message2".to_string(),
},
])
); );
} }
} }

View File

@@ -1,46 +1,28 @@
use serde::{Deserialize, Serialize}; use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
use crate::{ ResponseParserError,
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError},
common::ChannelName,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct SendMessage; pub struct SendMessage;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SendMessageRequest {
pub channel: ChannelName,
pub message: String,
}
impl Command for SendMessage { impl Command for SendMessage {
type Request = SendMessageRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "sendmessage"; const COMMAND: &'static str = "sendmessage";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.channel, request.message)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let channel = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel = channel
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel.to_owned()))?;
// TODO: SplitWhitespace::remainder() is unstable, use when stable // TODO: SplitWhitespace::remainder() is unstable, use when stable
let message = parts.collect::<Vec<_>>().join(" "); let message = parts.collect::<Vec<_>>().join(" ");
debug_assert!(!message.is_empty()); debug_assert!(!message.is_empty());
Ok((Request::SendMessage(channel, message), "")) Ok((Request::SendMessage(channel.to_string(), message), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,35 +1,25 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::ChannelName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Subscribe; pub struct Subscribe;
impl Command for Subscribe { impl Command for Subscribe {
type Request = ChannelName;
type Response = (); type Response = ();
const COMMAND: &'static str = "subscribe"; const COMMAND: &'static str = "subscribe";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel_name = channel_name
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel_name.to_owned()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Subscribe(channel_name), "")) Ok((Request::Subscribe(channel_name.to_string()), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,35 +1,25 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::ChannelName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Unsubscribe; pub struct Unsubscribe;
impl Command for Unsubscribe { impl Command for Unsubscribe {
type Request = ChannelName;
type Response = (); type Response = ();
const COMMAND: &'static str = "unsubscribe"; const COMMAND: &'static str = "unsubscribe";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let channel_name = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let channel_name = channel_name
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, channel_name.to_owned()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Unsubscribe(channel_name), "")) Ok((Request::Unsubscribe(channel_name.to_string()), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,21 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct BinaryLimit; pub struct BinaryLimit;
impl Command for BinaryLimit { impl Command for BinaryLimit {
type Request = u64;
type Response = (); type Response = ();
const COMMAND: &'static str = "binarylimit"; const COMMAND: &'static str = "binarylimit";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let limit = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let limit = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let limit = limit let limit = limit
.parse() .parse()
@@ -26,7 +20,7 @@ impl Command for BinaryLimit {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Close; pub struct Close;
impl Command for Close { impl Command for Close {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "close"; const COMMAND: &'static str = "close";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Close, "")) Ok((Request::Close, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Kill; pub struct Kill;
impl Command for Kill { impl Command for Kill {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "kill"; const COMMAND: &'static str = "kill";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Kill, "")) Ok((Request::Kill, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,21 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct Password; pub struct Password;
impl Command for Password { impl Command for Password {
type Request = String;
type Response = (); type Response = ();
const COMMAND: &'static str = "password"; const COMMAND: &'static str = "password";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let password = parts let password = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
@@ -26,7 +20,7 @@ impl Command for Password {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Ping; pub struct Ping;
impl Command for Ping { impl Command for Ping {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "ping"; const COMMAND: &'static str = "ping";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Ping, "")) Ok((Request::Ping, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,41 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct Protocol; pub struct Protocol;
pub type ProtocolResponse = Vec<String>;
impl Command for Protocol { impl Command for Protocol {
type Request = (); type Response = ();
type Response = ProtocolResponse;
const COMMAND: &'static str = "protocol"; const COMMAND: &'static str = "protocol";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Protocol, "")) Ok((Request::Protocol, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts_: Vec<_> = parts.into_vec()?; unimplemented!()
if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != "feature") {
return Err(ResponseParserError::UnexpectedProperty(k));
}
let list = parts_
.into_iter()
.map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
.collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)
} }
} }

View File

@@ -1,29 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ProtocolAll; pub struct ProtocolAll;
impl Command for ProtocolAll { impl Command for ProtocolAll {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "protocol all"; const COMMAND: &'static str = "protocol all";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ProtocolAll, "")) Ok((Request::ProtocolAll, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,41 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct ProtocolAvailable; pub struct ProtocolAvailable;
pub type ProtocolAvailableResponse = Vec<String>;
impl Command for ProtocolAvailable { impl Command for ProtocolAvailable {
type Request = (); type Response = ();
type Response = ProtocolAvailableResponse;
const COMMAND: &'static str = "protocol available"; const COMMAND: &'static str = "protocol available";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ProtocolAvailable, "")) Ok((Request::ProtocolAvailable, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts_: Vec<_> = parts.into_vec()?; unimplemented!()
if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != "feature") {
return Err(ResponseParserError::UnexpectedProperty(k));
}
let list = parts_
.into_iter()
.map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
.collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)
} }
} }

View File

@@ -1,29 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ProtocolClear; pub struct ProtocolClear;
impl Command for ProtocolClear { impl Command for ProtocolClear {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "protocol clear"; const COMMAND: &'static str = "protocol clear";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ProtocolClear, "")) Ok((Request::ProtocolClear, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,34 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::Feature,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ProtocolDisable; pub struct ProtocolDisable;
pub type ProtocolDisableRequest = Vec<Feature>;
impl Command for ProtocolDisable { impl Command for ProtocolDisable {
type Request = ProtocolDisableRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "protocol disable"; const COMMAND: &'static str = "protocol disable";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request.join(" "))
}
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable(); let mut parts = parts.peekable();
if parts.peek().is_none() { if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF); return Err(RequestParserError::UnexpectedEOF);
} }
let features = parts.map(|s| s.to_string()).collect::<Vec<String>>(); // TODO: verify that the features are split by whitespace
let mut features = Vec::new();
for feature in parts {
features.push(feature.to_string());
}
Ok((Request::ProtocolDisable(features), "")) Ok((Request::ProtocolDisable(features), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,34 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::Feature,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ProtocolEnable; pub struct ProtocolEnable;
pub type ProtocolEnableRequest = Vec<Feature>;
impl Command for ProtocolEnable { impl Command for ProtocolEnable {
type Request = ProtocolEnableRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "protocol enable"; const COMMAND: &'static str = "protocol enable";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request.join(" "))
}
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable(); let mut parts = parts.peekable();
if parts.peek().is_none() { if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF); return Err(RequestParserError::UnexpectedEOF);
} }
let features = parts.map(|s| s.to_string()).collect::<Vec<String>>(); // TODO: verify that the features are split by whitespace
let mut features = Vec::new();
for feature in parts {
features.push(feature.to_string());
}
Ok((Request::ProtocolEnable(features), "")) Ok((Request::ProtocolEnable(features), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,7 +1,6 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct TagTypes; pub struct TagTypes;
@@ -9,31 +8,31 @@ pub struct TagTypes;
pub type TagTypesResponse = Vec<String>; pub type TagTypesResponse = Vec<String>;
impl Command for TagTypes { impl Command for TagTypes {
type Request = ();
type Response = TagTypesResponse; type Response = TagTypesResponse;
const COMMAND: &'static str = "tagtypes"; const COMMAND: &'static str = "tagtypes";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::TagTypes, "")) Ok((Request::TagTypes, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; let parts: Vec<_> = parts.into();
let mut tagtypes = Vec::with_capacity(parts.len()); let mut tagtypes = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() { for (key, value) in parts.into_iter() {
if key != "tagtype" { debug_assert_eq!(key, "tagtype");
return Err(ResponseParserError::UnexpectedProperty(key));
}
let tagtype = expect_property_type!(Some(value), "tagtype", Text).to_string(); let tagtype = match value {
GenericResponseValue::Text(name) => name.to_string(),
GenericResponseValue::Binary(_) => {
return Err(ResponseParserError::UnexpectedPropertyType(
"tagtype", "Binary",
))
}
};
tagtypes.push(tagtype); tagtypes.push(tagtype);
} }

View File

@@ -1,29 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct TagTypesAll; pub struct TagTypesAll;
impl Command for TagTypesAll { impl Command for TagTypesAll {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "tagtypes all"; const COMMAND: &'static str = "tagtypes all";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::TagTypesAll, "")) Ok((Request::TagTypesAll, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,44 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct TagTypesAvailable; pub struct TagTypesAvailable;
pub type TagTypesAvailableResponse = Vec<String>;
impl Command for TagTypesAvailable { impl Command for TagTypesAvailable {
type Request = (); type Response = ();
type Response = TagTypesAvailableResponse;
const COMMAND: &'static str = "tagtypes available"; const COMMAND: &'static str = "tagtypes available";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::TagTypesAvailable, "")) Ok((Request::TagTypesAvailable, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; unimplemented!()
let mut tagtypes = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() {
if key != "tagtype" {
return Err(ResponseParserError::UnexpectedProperty(key));
}
let tagtype = expect_property_type!(Some(value), "tagtype", Text).to_string();
tagtypes.push(tagtype);
}
Ok(tagtypes)
} }
} }

View File

@@ -1,29 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct TagTypesClear; pub struct TagTypesClear;
impl Command for TagTypesClear { impl Command for TagTypesClear {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "tagtypes clear"; const COMMAND: &'static str = "tagtypes clear";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::TagTypesClear, "")) Ok((Request::TagTypesClear, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,34 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::TagName,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct TagTypesDisable; pub struct TagTypesDisable;
pub type TagTypesDisableRequest = Vec<TagName>;
impl Command for TagTypesDisable { impl Command for TagTypesDisable {
type Request = TagTypesDisableRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "tagtypes disable"; const COMMAND: &'static str = "tagtypes disable";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request.join(" "))
}
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable(); let mut parts = parts.peekable();
if parts.peek().is_none() { if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF); return Err(RequestParserError::UnexpectedEOF);
} }
let tag_types = parts.map(|s| s.to_string()).collect::<Vec<String>>(); // TODO: verify that the tag types are split by whitespace
let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesDisable(tag_types), "")) Ok((Request::TagTypesDisable(tag_types), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,34 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::TagName,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct TagTypesEnable; pub struct TagTypesEnable;
pub type TagTypesEnableRequest = Vec<TagName>;
impl Command for TagTypesEnable { impl Command for TagTypesEnable {
type Request = TagTypesEnableRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "tagtypes enable"; const COMMAND: &'static str = "tagtypes enable";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request.join(" "))
}
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable(); let mut parts = parts.peekable();
if parts.peek().is_none() { if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF); return Err(RequestParserError::UnexpectedEOF);
} }
let tag_types = parts.map(|s| s.to_string()).collect::<Vec<String>>(); // TODO: verify that the tag types are split by whitespace
let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesEnable(tag_types), "")) Ok((Request::TagTypesEnable(tag_types), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,39 +1,34 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::TagName,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct TagTypesReset; pub struct TagTypesReset;
pub type TagTypesResetRequest = Vec<TagName>;
impl Command for TagTypesReset { impl Command for TagTypesReset {
type Request = TagTypesResetRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "tagtypes reset"; const COMMAND: &'static str = "tagtypes reset";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request.join(" "))
}
fn parse_request(parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mut parts = parts.peekable(); let mut parts = parts.peekable();
if parts.peek().is_none() { if parts.peek().is_none() {
return Err(RequestParserError::UnexpectedEOF); return Err(RequestParserError::UnexpectedEOF);
} }
// TODO: verify that the tag types are split by whitespace // TODO: verify that the tag types are split by whitespace
let tag_types = parts.map(|s| s.to_string()).collect::<Vec<String>>(); let mut tag_types = Vec::new();
for tag_type in parts {
tag_types.push(tag_type.to_string());
}
Ok((Request::TagTypesReset(tag_types), "")) Ok((Request::TagTypesReset(tag_types), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Next; pub struct Next;
impl Command for Next { impl Command for Next {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "next"; const COMMAND: &'static str = "next";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Next, "")) Ok((Request::Next, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,25 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct Pause; pub struct Pause;
impl Command for Pause { impl Command for Pause {
type Request = Option<bool>;
type Response = (); type Response = ();
const COMMAND: &'static str = "pause"; const COMMAND: &'static str = "pause";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request {
Some(true) => format!("{} 1", Self::COMMAND),
Some(false) => format!("{} 0", Self::COMMAND),
None => Self::COMMAND.to_string(),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let result = match parts.next() { let result = match parts.next() {
Some("0") => Ok((Request::Pause(Some(false)), "")), Some("0") => Ok((Request::Pause(Some(false)), "")),
Some("1") => Ok((Request::Pause(Some(true)), "")), Some("1") => Ok((Request::Pause(Some(true)), "")),
@@ -34,7 +24,7 @@ impl Command for Pause {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,18 @@
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::SongPosition, common::SongPosition,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Play; pub struct Play;
impl Command for Play { impl Command for Play {
type Request = SongPosition;
type Response = (); type Response = ();
const COMMAND: &'static str = "play"; const COMMAND: &'static str = "play";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songpos = match parts.next() { let songpos = match parts.next() {
Some(s) => s Some(s) => s
.parse::<SongPosition>() .parse::<SongPosition>()
@@ -31,7 +27,7 @@ impl Command for Play {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,18 @@
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::SongId, common::SongId,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct PlayId; pub struct PlayId;
impl Command for PlayId { impl Command for PlayId {
type Request = SongId;
type Response = (); type Response = ();
const COMMAND: &'static str = "playid"; const COMMAND: &'static str = "playid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songid = match parts.next() { let songid = match parts.next() {
Some(s) => s Some(s) => s
.parse::<SongId>() .parse::<SongId>()
@@ -31,7 +27,7 @@ impl Command for PlayId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Previous; pub struct Previous;
impl Command for Previous { impl Command for Previous {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "previous"; const COMMAND: &'static str = "previous";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Previous, "")) Ok((Request::Previous, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,30 +1,18 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::{SongPosition, TimeWithFractions}, common::{SongPosition, TimeWithFractions},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Seek; pub struct Seek;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SeekRequest {
pub songpos: SongPosition,
pub time: TimeWithFractions,
}
impl Command for Seek { impl Command for Seek {
type Request = SeekRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "seek"; const COMMAND: &'static str = "seek";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.songpos, request.time)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songpos = match parts.next() { let songpos = match parts.next() {
Some(s) => s Some(s) => s
.parse::<SongPosition>() .parse::<SongPosition>()
@@ -46,7 +34,7 @@ impl Command for Seek {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,39 +1,18 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{ commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}, },
common::{SeekMode, TimeWithFractions}, common::{SeekMode, TimeWithFractions},
request_tokenizer::RequestTokenizer,
}; };
pub struct SeekCur; pub struct SeekCur;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SeekCurRequest {
pub mode: SeekMode,
pub time: TimeWithFractions,
}
impl Command for SeekCur { impl Command for SeekCur {
type Request = SeekCurRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "seekcur"; const COMMAND: &'static str = "seekcur";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let time_raw = match parts.next() { let time_raw = match parts.next() {
Some(t) => t, Some(t) => t,
None => return Err(RequestParserError::UnexpectedEOF), None => return Err(RequestParserError::UnexpectedEOF),
@@ -67,7 +46,7 @@ impl Command for SeekCur {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,30 +1,18 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::{SongId, TimeWithFractions}, common::{SongId, TimeWithFractions},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct SeekId; pub struct SeekId;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SeekIdRequest {
pub songid: SongId,
pub time: TimeWithFractions,
}
impl Command for SeekId { impl Command for SeekId {
type Request = SeekIdRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "seekid"; const COMMAND: &'static str = "seekid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.songid, request.time)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songid = match parts.next() { let songid = match parts.next() {
Some(s) => s Some(s) => s
.parse::<SongId>() .parse::<SongId>()
@@ -46,7 +34,7 @@ impl Command for SeekId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Stop; pub struct Stop;
impl Command for Stop { impl Command for Stop {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "stop"; const COMMAND: &'static str = "stop";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Stop, "")) Ok((Request::Stop, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,56 +1,22 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct ListMounts; pub struct ListMounts;
impl Command for ListMounts { impl Command for ListMounts {
type Request = (); type Response = Vec<(String, String)>;
type Response = Vec<String>;
const COMMAND: &'static str = "listmounts"; const COMMAND: &'static str = "listmounts";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ListMounts, "")) Ok((Request::ListMounts, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; unimplemented!()
let mut result = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() {
if key != "mount" {
return Err(ResponseParserError::UnexpectedProperty(key));
}
let value = expect_property_type!(Some(value), "mount", Text).to_string();
result.push(value);
}
Ok(result)
}
}
#[cfg(test)]
mod tests {
use indoc::indoc;
use super::*;
#[test]
fn test_parse_response() {
let input = indoc! {"
mount:
mount: /mnt/music
OK
"};
let result = ListMounts::parse_raw_response(input);
assert_eq!(result, Ok(vec!["".to_string(), "/mnt/music".to_string(),]));
} }
} }

View File

@@ -1,51 +1,22 @@
use std::collections::HashMap;
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct ListNeighbors; pub struct ListNeighbors;
pub type ListNeighborsResponse = HashMap<String, String>;
impl Command for ListNeighbors { impl Command for ListNeighbors {
type Request = (); type Response = Vec<(String, String)>;
type Response = ListNeighborsResponse;
const COMMAND: &'static str = "listneighbors"; const COMMAND: &'static str = "listneighbors";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ListNeighbors, "")) Ok((Request::ListNeighbors, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; unimplemented!()
debug_assert!(parts.len() % 2 == 0);
let mut result = HashMap::with_capacity(parts.len() / 2);
for channel_message_pair in parts.chunks_exact(2) {
let (neigh_key, neigh_value) = channel_message_pair[0];
let (name_key, name_value) = channel_message_pair[1];
debug_assert!(neigh_key == "neighbor");
debug_assert!(name_key == "name");
let neighbor = expect_property_type!(Some(neigh_value), "neighbor", Text).to_string();
let name = expect_property_type!(Some(name_value), "name", Text).to_string();
result.insert(neighbor, name);
}
Ok(result)
} }
} }

View File

@@ -1,34 +1,21 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Mount; pub struct Mount;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MountRequest {
pub path: String,
pub uri: String,
}
impl Command for Mount { impl Command for Mount {
type Request = MountRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "mount"; const COMMAND: &'static str = "mount";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.path, request.uri) let path = parts
} .next()
.ok_or(RequestParserError::UnexpectedEOF)?
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { .to_string();
let path = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let path = path
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, path.to_string()))?;
let uri = parts let uri = parts
.next() .next()
@@ -42,7 +29,7 @@ impl Command for Mount {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Unmount; pub struct Unmount;
pub type UnmountRequest = String;
impl Command for Unmount { impl Command for Unmount {
type Request = UnmountRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "unmount"; const COMMAND: &'static str = "unmount";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request) let path = parts
} .next()
.ok_or(RequestParserError::UnexpectedEOF)?
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> { .to_string();
let path = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let path = path
.parse()
.map_err(|_| RequestParserError::SyntaxError(0, path.to_string()))?;
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
@@ -31,7 +24,7 @@ impl Command for Unmount {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,25 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{Offset, Uri}, get_and_parse_property, get_property, Command, Request, RequestParserError,
request_tokenizer::RequestTokenizer, RequestParserResult, ResponseAttributes, ResponseParserError,
response_tokenizer::{ResponseAttributes, get_and_parse_property, get_property}, },
common::Offset,
}; };
pub struct AlbumArt; 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 struct AlbumArtResponse {
pub size: usize, pub size: usize,
pub binary: Vec<u8>, pub binary: Vec<u8>,
} }
impl Command for AlbumArt { impl Command for AlbumArt {
type Request = AlbumArtRequest;
type Response = AlbumArtResponse; type Response = AlbumArtResponse;
const COMMAND: &'static str = "albumart"; const COMMAND: &'static str = "albumart";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.uri, request.offset)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() { let uri = match parts.next() {
Some(s) => s, Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF), None => return Err(RequestParserError::UnexpectedEOF),
@@ -52,8 +39,8 @@ impl Command for AlbumArt {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let size = get_and_parse_property!(parts, "size", Text); let size = get_and_parse_property!(parts, "size", Text);

View File

@@ -1,49 +1,26 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::GroupType, get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
filter::Filter, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::{ResponseAttributes, get_and_parse_property}, filter::parse_filter,
}; };
pub struct Count; pub struct Count;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CountRequest {
filter: Filter,
group: Option<GroupType>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct CountResponse { pub struct CountResponse {
pub songs: usize, pub songs: usize,
pub playtime: u64, pub playtime: u64,
} }
impl Command for Count { impl Command for Count {
type Request = CountRequest;
type Response = CountResponse; type Response = CountResponse;
const COMMAND: &'static str = "count"; const COMMAND: &'static str = "count";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
if let Some(group) = request.group {
cmd.push_str(&format!(" group {}", group));
}
cmd
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let group = if let Some("group") = parts.next() { let group = if let Some("group") = parts.next() {
let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
@@ -64,8 +41,8 @@ impl Command for Count {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let songs = get_and_parse_property!(parts, "songs", Text); let songs = get_and_parse_property!(parts, "songs", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text); let playtime = get_and_parse_property!(parts, "playtime", Text);

View File

@@ -1,56 +1,30 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{Sort, WindowRange}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
filter::Filter, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::ResponseAttributes, filter::parse_filter,
}; };
pub struct Find; pub struct Find;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FindRequest {
filter: Filter,
sort: Option<Sort>,
window: Option<WindowRange>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FindResponse {} pub struct FindResponse {}
impl Command for Find { impl Command for Find {
type Request = FindRequest;
type Response = FindResponse; type Response = FindResponse;
const COMMAND: &'static str = "find"; const COMMAND: &'static str = "find";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let mut sort_or_window = parts.next(); let mut sort_or_window = parts.next();
let mut sort = None; let mut sort = None;
if let Some("sort") = sort_or_window { if let Some("sort") = sort_or_window {
let s = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
sort = Some( sort = Some(
s.parse() parts
.map_err(|_| RequestParserError::SyntaxError(0, s.to_string()))?, .next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
); );
sort_or_window = parts.next(); sort_or_window = parts.next();
} }
@@ -71,7 +45,7 @@ impl Command for Find {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,57 +1,28 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{SongPosition, Sort, WindowRange}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
filter::Filter, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::ResponseAttributes, filter::parse_filter,
}; };
pub struct FindAdd; pub struct FindAdd;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct FindAddRequest {
filter: Filter,
sort: Option<Sort>,
window: Option<WindowRange>,
position: Option<SongPosition>,
}
impl Command for FindAdd { impl Command for FindAdd {
type Request = FindAddRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "findadd"; const COMMAND: &'static str = "findadd";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let mut sort_or_window_or_position = parts.next(); let mut sort_or_window_or_position = parts.next();
let mut sort = None; let mut sort = None;
if let Some("sort") = sort_or_window_or_position { if let Some("sort") = sort_or_window_or_position {
let s = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
sort = Some( sort = Some(
s.parse() parts
.map_err(|_| RequestParserError::SyntaxError(0, s.to_string()))?, .next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
); );
sort_or_window_or_position = parts.next(); sort_or_window_or_position = parts.next();
} }
@@ -82,7 +53,7 @@ impl Command for FindAdd {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,31 +1,21 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use crate::commands::{
get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
use crate::{ ResponseAttributes, ResponseParserError,
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError},
common::Uri,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, get_and_parse_property},
}; };
pub struct GetFingerprint; pub struct GetFingerprint;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct GetFingerprintResponse { pub struct GetFingerprintResponse {
pub chromaprint: String, pub chromaprint: String,
} }
impl Command for GetFingerprint { impl Command for GetFingerprint {
type Request = Uri;
type Response = GetFingerprintResponse; type Response = GetFingerprintResponse;
const COMMAND: &'static str = "getfingerprint"; const COMMAND: &'static str = "getfingerprint";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let uri = uri let uri = uri
.parse() .parse()
@@ -38,8 +28,8 @@ impl Command for GetFingerprint {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let chromaprint = get_and_parse_property!(parts, "chromaprint", Text); let chromaprint = get_and_parse_property!(parts, "chromaprint", Text);

View File

@@ -1,102 +1,54 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{GroupType, TagName, WindowRange}, expect_property_type, Command, Request, RequestParserError, RequestParserResult,
filter::Filter, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::{ResponseAttributes, expect_property_type}, filter::parse_filter,
}; };
pub struct List; pub struct List;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ListRequest {
tagname: TagName,
filter: Option<Filter>,
groups: Vec<GroupType>,
window: Option<WindowRange>,
}
pub type ListResponse = Vec<String>; pub type ListResponse = Vec<String>;
impl Command for List { impl Command for List {
type Request = ListRequest;
type Response = ListResponse; type Response = ListResponse;
const COMMAND: &'static str = "list"; const COMMAND: &'static str = "list";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = match &request.filter { let tagtype = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
Some(f) => format!("{} {} {}", Self::COMMAND, request.tagname, f), let tagtype = tagtype
None => format!("{} {}", Self::COMMAND, request.tagname),
};
for group in request.groups {
cmd.push_str(&format!(" group {}", group));
}
if let Some(window) = request.window {
cmd.push_str(&format!(" window {}", window));
}
cmd
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let tagname = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let tagname = tagname
.parse() .parse()
.map_err(|_| RequestParserError::SyntaxError(1, tagname.to_owned()))?; .map_err(|_| RequestParserError::SyntaxError(1, tagtype.to_owned()))?;
let mut filter = None; // TODO: This should be optional
let mut groups = Vec::new(); let filter = parse_filter(&mut parts)?;
let mut window = None;
let mut next = parts.next(); let group = if let Some("group") = parts.next() {
if let Some(f) = next
&& f != "group"
&& f != "window"
{
let parsed_filter =
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?;
filter = Some(parsed_filter);
next = parts.next();
}
while let Some(g) = next
&& g == "group"
{
let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let parsed_group = group Some(
.parse() group
.map_err(|_| RequestParserError::SyntaxError(1, group.to_owned()))?; .parse()
groups.push(parsed_group); .map_err(|_| RequestParserError::SyntaxError(1, group.to_owned()))?,
next = parts.next(); )
} } else {
None
if let Some(w) = next };
&& w == "window"
{
let window_str = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let parsed_window = window_str
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, window_str.to_owned()))?;
window = Some(parsed_window);
}
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::List(tagname, filter, groups, window), "")) Ok((Request::List(tagtype, filter, group), ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts_: Vec<_> = parts.into_vec()?;
debug_assert!({ debug_assert!({
let key = parts_.first().map(|(k, _)| k); let key = parts.0.first().map(|(k, _)| k);
parts_.iter().all(|(k, _)| k == key.unwrap()) parts.0.iter().all(|(k, _)| k == key.unwrap())
}); });
let list = parts_ let list = parts
.0
.into_iter() .into_iter()
.map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string())) .map(|(k, v)| Ok(expect_property_type!(Some(v), k, Text).to_string()))
.collect::<Result<Vec<_>, ResponseParserError>>()?; .collect::<Result<Vec<_>, ResponseParserError>>()?;

View File

@@ -1,8 +1,6 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::Uri, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ListAll; pub struct ListAll;
@@ -11,19 +9,10 @@ pub struct ListAll;
pub type ListAllResponse = Vec<String>; pub type ListAllResponse = Vec<String>;
impl Command for ListAll { impl Command for ListAll {
type Request = Option<Uri>;
type Response = ListAllResponse; type Response = ListAllResponse;
const COMMAND: &'static str = "listall"; const COMMAND: &'static str = "listall";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
if let Some(uri) = request {
format!("{} {}", Self::COMMAND, uri)
} else {
Self::COMMAND.to_string()
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts let uri = parts
.next() .next()
.map(|s| { .map(|s| {
@@ -39,7 +28,7 @@ impl Command for ListAll {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,8 +1,6 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::Uri, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ListAllInfo; pub struct ListAllInfo;
@@ -12,19 +10,10 @@ pub struct ListAllInfo;
pub type ListAllInfoResponse = Vec<String>; pub type ListAllInfoResponse = Vec<String>;
impl Command for ListAllInfo { impl Command for ListAllInfo {
type Request = Option<Uri>;
type Response = ListAllInfoResponse; type Response = ListAllInfoResponse;
const COMMAND: &'static str = "listallinfo"; const COMMAND: &'static str = "listallinfo";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
if let Some(uri) = request {
format!("{} {}", Self::COMMAND, uri)
} else {
Self::COMMAND.to_string()
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts let uri = parts
.next() .next()
.map(|s| { .map(|s| {
@@ -40,7 +29,7 @@ impl Command for ListAllInfo {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,8 +1,6 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::Uri, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ListFiles; pub struct ListFiles;
@@ -11,19 +9,10 @@ pub struct ListFiles;
pub type ListFilesResponse = Vec<String>; pub type ListFilesResponse = Vec<String>;
impl Command for ListFiles { impl Command for ListFiles {
type Request = Option<Uri>;
type Response = ListFilesResponse; type Response = ListFilesResponse;
const COMMAND: &'static str = "listfiles"; const COMMAND: &'static str = "listfiles";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
if let Some(uri) = request {
format!("{} {}", Self::COMMAND, uri)
} else {
Self::COMMAND.to_string()
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts let uri = parts
.next() .next()
.map(|s| { .map(|s| {
@@ -39,7 +28,7 @@ impl Command for ListFiles {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,35 +1,18 @@
use serde::{Deserialize, Serialize}; use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
use crate::{ ResponseParserError,
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError},
common::Uri,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct LsInfo; pub struct LsInfo;
pub type LsInfoResponse = Vec<LsInfoResponseEntry>; // TODO: fix this type
pub type LsInfoResponse = Vec<String>;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct LsInfoResponseEntry {
playlist: String,
last_modified: Option<String>,
}
impl Command for LsInfo { impl Command for LsInfo {
type Request = Option<Uri>;
type Response = LsInfoResponse; type Response = LsInfoResponse;
const COMMAND: &'static str = "lsinfo"; const COMMAND: &'static str = "lsinfo";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request {
Some(uri) => format!("{} {}", Self::COMMAND, uri),
None => Self::COMMAND.to_string(),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts let uri = parts
.next() .next()
.map(|s| { .map(|s| {
@@ -45,31 +28,7 @@ impl Command for LsInfo {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; unimplemented!()
let mut playlists = Vec::new();
for (key, value) in parts {
match key {
"playlist" => {
playlists.push(LsInfoResponseEntry {
playlist: expect_property_type!(Some(value), "playlist", Text).to_string(),
last_modified: None,
});
}
"Last-Modified" => {
if let Some(last) = playlists.last_mut() {
last.last_modified = Some(
expect_property_type!(Some(value), "Last-Modified", Text).to_string(),
);
} else {
return Err(ResponseParserError::UnexpectedProperty("Last-Modified"));
}
}
_ => return Err(ResponseParserError::UnexpectedProperty(key)),
}
}
Ok(playlists)
} }
} }

View File

@@ -1,10 +1,8 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, GenericResponseValue, Request, RequestParserError, RequestParserResult,
common::Uri, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::{GenericResponseValue, ResponseAttributes},
}; };
pub struct ReadComments; pub struct ReadComments;
@@ -12,15 +10,10 @@ pub struct ReadComments;
pub type ReadCommentsResponse = HashMap<String, String>; pub type ReadCommentsResponse = HashMap<String, String>;
impl Command for ReadComments { impl Command for ReadComments {
type Request = Uri;
type Response = ReadCommentsResponse; type Response = ReadCommentsResponse;
const COMMAND: &'static str = "readcomments"; const COMMAND: &'static str = "readcomments";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let uri = uri let uri = uri
.parse() .parse()
@@ -33,8 +26,8 @@ impl Command for ReadComments {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let comments = parts let comments = parts
.iter() .iter()

View File

@@ -1,25 +1,15 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{Offset, Uri}, get_and_parse_property, get_optional_property, get_property, Command, Request,
request_tokenizer::RequestTokenizer, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
response_tokenizer::{
ResponseAttributes, get_and_parse_property, get_optional_property, get_property,
}, },
common::Offset,
}; };
pub struct ReadPicture; 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 struct ReadPictureResponse {
pub size: usize, pub size: usize,
pub binary: Vec<u8>, pub binary: Vec<u8>,
@@ -27,15 +17,10 @@ pub struct ReadPictureResponse {
} }
impl Command for ReadPicture { impl Command for ReadPicture {
type Request = ReadPictureRequest;
type Response = Option<ReadPictureResponse>; type Response = Option<ReadPictureResponse>;
const COMMAND: &'static str = "readpicture"; const COMMAND: &'static str = "readpicture";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.uri, request.offset)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() { let uri = match parts.next() {
Some(s) => s, Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF), None => return Err(RequestParserError::UnexpectedEOF),
@@ -55,8 +40,8 @@ impl Command for ReadPicture {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
if parts.is_empty() { if parts.is_empty() {
return Ok(None); return Ok(None);

View File

@@ -1,34 +1,21 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use crate::commands::{
get_and_parse_property, Command, Request, RequestParserResult, ResponseAttributes,
use crate::{ ResponseParserError,
commands::{Command, Request, RequestParserResult, ResponseParserError},
common::Uri,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, get_and_parse_property},
}; };
pub struct Rescan; pub struct Rescan;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RescanResponse { pub struct RescanResponse {
pub updating_db: usize, pub updating_db: usize,
} }
impl Command for Rescan { impl Command for Rescan {
type Request = Option<Uri>;
type Response = RescanResponse; type Response = RescanResponse;
const COMMAND: &'static str = "rescan"; const COMMAND: &'static str = "rescan";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request {
Some(uri) => format!("{} {}", Self::COMMAND, uri),
None => Self::COMMAND.to_string(),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts.next().map(|s| s.to_string()); let uri = parts.next().map(|s| s.to_string());
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
@@ -38,8 +25,8 @@ impl Command for Rescan {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let updating_db = get_and_parse_property!(parts, "updating_db", Text); let updating_db = get_and_parse_property!(parts, "updating_db", Text);

View File

@@ -1,56 +1,30 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{Sort, WindowRange}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
filter::Filter, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::ResponseAttributes, filter::parse_filter,
}; };
pub struct Search; pub struct Search;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchRequest {
filter: Filter,
sort: Option<Sort>,
window: Option<WindowRange>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchResponse {} pub struct SearchResponse {}
impl Command for Search { impl Command for Search {
type Request = SearchRequest;
type Response = SearchResponse; type Response = SearchResponse;
const COMMAND: &'static str = "search"; const COMMAND: &'static str = "search";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let mut sort_or_window = parts.next(); let mut sort_or_window = parts.next();
let mut sort = None; let mut sort = None;
if let Some("sort") = sort_or_window { if let Some("sort") = sort_or_window {
let s = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
sort = Some( sort = Some(
s.parse() parts
.map_err(|_| RequestParserError::SyntaxError(0, s.to_string()))?, .next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
); );
sort_or_window = parts.next(); sort_or_window = parts.next();
} }
@@ -71,7 +45,7 @@ impl Command for Search {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,59 +1,28 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{ commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}, },
common::{SongPosition, Sort, WindowRange}, filter::parse_filter,
filter::Filter,
request_tokenizer::RequestTokenizer,
}; };
pub struct SearchAdd; pub struct SearchAdd;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchAddRequest {
filter: Filter,
sort: Option<Sort>,
window: Option<WindowRange>,
position: Option<SongPosition>,
}
impl Command for SearchAdd { impl Command for SearchAdd {
type Request = SearchAddRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "searchadd"; const COMMAND: &'static str = "searchadd";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let mut sort_or_window_or_position = parts.next(); let mut sort_or_window_or_position = parts.next();
let mut sort = None; let mut sort = None;
if let Some("sort") = sort_or_window_or_position { if let Some("sort") = sort_or_window_or_position {
let s = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
sort = Some( sort = Some(
s.parse() parts
.map_err(|_| RequestParserError::SyntaxError(0, s.to_string()))?, .next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
); );
sort_or_window_or_position = parts.next(); sort_or_window_or_position = parts.next();
} }
@@ -84,7 +53,7 @@ impl Command for SearchAdd {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,70 +1,33 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{ commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError, ResponseParserError,
}, },
common::{PlaylistName, SongPosition, Sort, WindowRange}, filter::parse_filter,
filter::Filter,
request_tokenizer::RequestTokenizer,
}; };
pub struct SearchAddPl; pub struct SearchAddPl;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchAddPlRequest {
playlist_name: PlaylistName,
filter: Filter,
sort: Option<Sort>,
window: Option<WindowRange>,
position: Option<SongPosition>,
}
impl Command for SearchAddPl { impl Command for SearchAddPl {
type Request = SearchAddPlRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "searchaddpl"; const COMMAND: &'static str = "searchaddpl";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
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: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let playlist_name = parts let playlist_name = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
.to_string(); .to_string();
let filter = match parts.next() { let filter = parse_filter(&mut parts)?;
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let mut sort_or_window_or_position = parts.next(); let mut sort_or_window_or_position = parts.next();
let mut sort = None; let mut sort = None;
if let Some("sort") = sort_or_window_or_position { if let Some("sort") = sort_or_window_or_position {
let s = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
sort = Some( sort = Some(
s.parse() parts
.map_err(|_| RequestParserError::SyntaxError(0, s.to_string()))?, .next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
); );
sort_or_window_or_position = parts.next(); sort_or_window_or_position = parts.next();
} }
@@ -98,7 +61,7 @@ impl Command for SearchAddPl {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,49 +1,26 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::GroupType, get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
filter::Filter, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer, },
response_tokenizer::{ResponseAttributes, get_and_parse_property}, filter::parse_filter,
}; };
pub struct SearchCount; pub struct SearchCount;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchCountRequest {
filter: Filter,
group: Option<GroupType>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct SearchCountResponse { pub struct SearchCountResponse {
pub songs: usize, pub songs: usize,
pub playtime: u64, pub playtime: u64,
} }
impl Command for SearchCount { impl Command for SearchCount {
type Request = SearchCountRequest;
type Response = SearchCountResponse; type Response = SearchCountResponse;
const COMMAND: &'static str = "searchcount"; const COMMAND: &'static str = "searchcount";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let mut cmd = format!("{} {}", Self::COMMAND, request.filter); let filter = parse_filter(&mut parts)?;
if let Some(group) = request.group {
cmd.push_str(&format!(" group {}", group));
}
cmd
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let filter = match parts.next() {
Some(f) => {
Filter::parse(f).map_err(|_| RequestParserError::SyntaxError(1, f.to_owned()))?
}
None => return Err(RequestParserError::UnexpectedEOF),
};
let group = if let Some("group") = parts.next() { let group = if let Some("group") = parts.next() {
let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
@@ -63,8 +40,8 @@ impl Command for SearchCount {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let songs = get_and_parse_property!(parts, "songs", Text); let songs = get_and_parse_property!(parts, "songs", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text); let playtime = get_and_parse_property!(parts, "playtime", Text);

View File

@@ -1,34 +1,21 @@
use std::collections::HashMap; use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use crate::commands::{
get_and_parse_property, Command, Request, RequestParserResult, ResponseAttributes,
use crate::{ ResponseParserError,
commands::{Command, Request, RequestParserResult, ResponseParserError},
common::Uri,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, get_and_parse_property},
}; };
pub struct Update; pub struct Update;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct UpdateResponse { pub struct UpdateResponse {
updating_db: usize, updating_db: usize,
} }
impl Command for Update { impl Command for Update {
type Request = Option<Uri>;
type Response = UpdateResponse; type Response = UpdateResponse;
const COMMAND: &'static str = "update"; const COMMAND: &'static str = "update";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request {
Some(uri) => format!("{} {}", Self::COMMAND, uri),
None => Self::COMMAND.to_string(),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = parts.next().map(|s| s.to_string()); let uri = parts.next().map(|s| s.to_string());
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
@@ -38,8 +25,8 @@ impl Command for Update {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let updating_db = get_and_parse_property!(parts, "updating_db", Text); let updating_db = get_and_parse_property!(parts, "updating_db", Text);

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::PartitionName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct DelPartition; pub struct DelPartition;
impl Command for DelPartition { impl Command for DelPartition {
type Request = PartitionName;
type Response = (); type Response = ();
const COMMAND: &'static str = "delpartition"; const COMMAND: &'static str = "delpartition";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let partition = parts let partition = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
@@ -29,7 +22,7 @@ impl Command for DelPartition {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,29 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, expect_property_type, Command, Request, RequestParserResult, ResponseAttributes,
common::PartitionName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, expect_property_type},
}; };
pub struct ListPartitions; pub struct ListPartitions;
pub type ListPartitionsResponse = Vec<PartitionName>; pub type ListPartitionsResponse = Vec<String>;
impl Command for ListPartitions { impl Command for ListPartitions {
type Request = ();
type Response = ListPartitionsResponse; type Response = ListPartitionsResponse;
const COMMAND: &'static str = "listpartitions"; const COMMAND: &'static str = "listpartitions";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ListPartitions, "")) Ok((Request::ListPartitions, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into_vec()?; let parts: Vec<_> = parts.into();
let mut partitions = Vec::with_capacity(parts.len()); let mut partitions = Vec::with_capacity(parts.len());
for (key, value) in parts.into_iter() { for (key, value) in parts.into_iter() {
if key != "partition" { debug_assert_eq!(key, "partition");
return Err(ResponseParserError::UnexpectedProperty(key));
}
let partition = expect_property_type!(Some(value), "partition", Text).to_string(); let partition = expect_property_type!(Some(value), "partition", Text).to_string();
partitions.push(partition); partitions.push(partition);
} }

View File

@@ -1,21 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct MoveOutput; pub struct MoveOutput;
impl Command for MoveOutput { impl Command for MoveOutput {
type Request = String;
type Response = (); type Response = ();
const COMMAND: &'static str = "moveoutput"; const COMMAND: &'static str = "moveoutput";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let output_name = parts let output_name = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
@@ -28,7 +22,7 @@ impl Command for MoveOutput {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::PartitionName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct NewPartition; pub struct NewPartition;
impl Command for NewPartition { impl Command for NewPartition {
type Request = PartitionName;
type Response = (); type Response = ();
const COMMAND: &'static str = "newpartition"; const COMMAND: &'static str = "newpartition";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let partition = parts let partition = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
@@ -29,7 +22,7 @@ impl Command for NewPartition {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::PartitionName, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Partition; pub struct Partition;
impl Command for Partition { impl Command for Partition {
type Request = PartitionName;
type Response = (); type Response = ();
const COMMAND: &'static str = "partition"; const COMMAND: &'static str = "partition";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let partition = parts let partition = parts
.next() .next()
.ok_or(RequestParserError::UnexpectedEOF)? .ok_or(RequestParserError::UnexpectedEOF)?
@@ -29,7 +22,7 @@ impl Command for Partition {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,24 +1,17 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::BoolOrOneshot, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Consume; pub struct Consume;
impl Command for Consume { impl Command for Consume {
type Request = BoolOrOneshot;
type Response = (); type Response = ();
const COMMAND: &'static str = "consume"; const COMMAND: &'static str = "consume";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let state = match parts.next() { let state = match parts.next() {
Some(s) => crate::common::BoolOrOneshot::from_str(s) Some(s) => crate::common::BoolOrOneshot::from_str(s)
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?,
@@ -32,7 +25,7 @@ impl Command for Consume {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,18 @@
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::Seconds, common::Seconds,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Crossfade; pub struct Crossfade;
impl Command for Crossfade { impl Command for Crossfade {
type Request = Seconds;
type Response = (); type Response = ();
const COMMAND: &'static str = "crossfade"; const COMMAND: &'static str = "crossfade";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let seconds = match parts.next() { let seconds = match parts.next() {
Some(s) => s Some(s) => s
.parse::<Seconds>() .parse::<Seconds>()
@@ -31,7 +27,7 @@ impl Command for Crossfade {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,32 +1,28 @@
use std::collections::HashMap; use std::collections::HashMap;
use crate::{ use crate::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, commands::{
get_and_parse_property, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::VolumeValue, common::VolumeValue,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, get_and_parse_property},
}; };
pub struct GetVol; pub struct GetVol;
impl Command for GetVol { impl Command for GetVol {
type Request = ();
type Response = VolumeValue; type Response = VolumeValue;
const COMMAND: &'static str = "getvol"; const COMMAND: &'static str = "getvol";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::GetVol, "")) Ok((Request::GetVol, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
assert_eq!(parts.len(), 1); assert_eq!(parts.len(), 1);
let volume = get_and_parse_property!(parts, "volume", Text); let volume = get_and_parse_property!(parts, "volume", Text);
Ok(volume) Ok(volume)

View File

@@ -1,21 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct MixRampDb; pub struct MixRampDb;
impl Command for MixRampDb { impl Command for MixRampDb {
type Request = f32;
type Response = (); type Response = ();
const COMMAND: &'static str = "mixrampdb"; const COMMAND: &'static str = "mixrampdb";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let db = match parts.next() { let db = match parts.next() {
Some(s) => s Some(s) => s
.parse::<f32>() .parse::<f32>()
@@ -30,7 +24,7 @@ impl Command for MixRampDb {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,18 @@
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::Seconds, common::Seconds,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct MixRampDelay; pub struct MixRampDelay;
impl Command for MixRampDelay { impl Command for MixRampDelay {
type Request = Seconds;
type Response = (); type Response = ();
const COMMAND: &'static str = "mixrampdelay"; const COMMAND: &'static str = "mixrampdelay";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let seconds = match parts.next() { let seconds = match parts.next() {
Some(s) => s Some(s) => s
.parse::<Seconds>() .parse::<Seconds>()
@@ -31,7 +27,7 @@ impl Command for MixRampDelay {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct Random; pub struct Random;
impl Command for Random { impl Command for Random {
type Request = bool;
type Response = (); type Response = ();
const COMMAND: &'static str = "random"; const COMMAND: &'static str = "random";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let state = if request { "1" } else { "0" };
format!("{} {}", Self::COMMAND, state)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let state = match parts.next() { let state = match parts.next() {
Some("0") => false, Some("0") => false,
Some("1") => true, Some("1") => true,
@@ -31,7 +24,7 @@ impl Command for Random {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct Repeat; pub struct Repeat;
impl Command for Repeat { impl Command for Repeat {
type Request = bool;
type Response = (); type Response = ();
const COMMAND: &'static str = "repeat"; const COMMAND: &'static str = "repeat";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let state = if request { "1" } else { "0" };
format!("{} {}", Self::COMMAND, state)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let state = match parts.next() { let state = match parts.next() {
Some("0") => false, Some("0") => false,
Some("1") => true, Some("1") => true,
@@ -31,7 +24,7 @@ impl Command for Repeat {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,24 +1,20 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::ReplayGainModeMode, common::ReplayGainModeMode,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ReplayGainMode; pub struct ReplayGainMode;
impl Command for ReplayGainMode { impl Command for ReplayGainMode {
type Request = ReplayGainModeMode;
type Response = (); type Response = ();
const COMMAND: &'static str = "replay_gain_mode"; const COMMAND: &'static str = "replay_gain_mode";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let mode = match parts.next() { let mode = match parts.next() {
Some(s) => ReplayGainModeMode::from_str(s) Some(s) => ReplayGainModeMode::from_str(s)
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?,
@@ -32,7 +28,7 @@ impl Command for ReplayGainMode {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -3,10 +3,11 @@ use std::{collections::HashMap, str::FromStr};
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, commands::{
get_property, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::ReplayGainModeMode, common::ReplayGainModeMode,
request_tokenizer::RequestTokenizer,
response_tokenizer::{ResponseAttributes, get_property},
}; };
pub struct ReplayGainStatus; pub struct ReplayGainStatus;
@@ -17,23 +18,18 @@ pub struct ReplayGainStatusResponse {
} }
impl Command for ReplayGainStatus { impl Command for ReplayGainStatus {
type Request = ();
type Response = ReplayGainStatusResponse; type Response = ReplayGainStatusResponse;
const COMMAND: &'static str = "replay_gain_status"; const COMMAND: &'static str = "replay_gain_status";
fn serialize_request(&self, _: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ReplayGainStatus, "")) Ok((Request::ReplayGainStatus, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let replay_gain_mode = get_property!(parts, "replay_gain_mode", Text); let replay_gain_mode = get_property!(parts, "replay_gain_mode", Text);
Ok(ReplayGainStatusResponse { Ok(ReplayGainStatusResponse {

View File

@@ -1,24 +1,20 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::VolumeValue, common::VolumeValue,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct SetVol; pub struct SetVol;
impl Command for SetVol { impl Command for SetVol {
type Request = VolumeValue;
type Response = (); type Response = ();
const COMMAND: &'static str = "setvol"; const COMMAND: &'static str = "setvol";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let volume = match parts.next() { let volume = match parts.next() {
Some(s) => VolumeValue::from_str(s) Some(s) => VolumeValue::from_str(s)
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?,
@@ -32,7 +28,7 @@ impl Command for SetVol {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,24 +1,17 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
common::BoolOrOneshot, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Single; pub struct Single;
impl Command for Single { impl Command for Single {
type Request = BoolOrOneshot;
type Response = (); type Response = ();
const COMMAND: &'static str = "single"; const COMMAND: &'static str = "single";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let state = match parts.next() { let state = match parts.next() {
Some(s) => crate::common::BoolOrOneshot::from_str(s) Some(s) => crate::common::BoolOrOneshot::from_str(s)
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?,
@@ -32,7 +25,7 @@ impl Command for Single {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,24 +1,20 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
common::VolumeValue, common::VolumeValue,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Volume; pub struct Volume;
impl Command for Volume { impl Command for Volume {
type Request = VolumeValue;
type Response = (); type Response = ();
const COMMAND: &'static str = "volume"; const COMMAND: &'static str = "volume";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let change = match parts.next() { let change = match parts.next() {
Some(s) => VolumeValue::from_str(s) Some(s) => VolumeValue::from_str(s)
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?, .map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))?,
@@ -32,7 +28,7 @@ impl Command for Volume {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,22 +1,15 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
/// Clears the current error message in status (this is also accomplished by any command that starts playback) /// Clears the current error message in status (this is also accomplished by any command that starts playback)
pub struct ClearError; pub struct ClearError;
impl Command for ClearError { impl Command for ClearError {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "clearerror"; const COMMAND: &'static str = "clearerror";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::ClearError, "")) Ok((Request::ClearError, ""))
@@ -24,7 +17,7 @@ impl Command for ClearError {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,9 +1,7 @@
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
/// Displays the song info of the current song (same song that is identified in status) /// Displays the song info of the current song (same song that is identified in status)
@@ -13,15 +11,10 @@ pub struct CurrentSong;
pub struct CurrentSongResponse {} pub struct CurrentSongResponse {}
impl Command for CurrentSong { impl Command for CurrentSong {
type Request = ();
type Response = CurrentSongResponse; type Response = CurrentSongResponse;
const COMMAND: &'static str = "currentsong"; const COMMAND: &'static str = "currentsong";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::CurrentSong, "")) Ok((Request::CurrentSong, ""))
@@ -29,7 +22,7 @@ impl Command for CurrentSong {
fn parse_response( fn parse_response(
_parts: ResponseAttributes<'_>, _parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
unimplemented!() unimplemented!()
} }
} }

View File

@@ -1,36 +1,18 @@
use std::str::FromStr; use std::str::{FromStr, SplitWhitespace};
use crate::{ use crate::common::SubSystem;
commands::{Command, Request, RequestParserResult, ResponseParserError},
common::SubSystem, use crate::commands::{
request_tokenizer::RequestTokenizer, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
response_tokenizer::ResponseAttributes,
}; };
pub struct Idle; pub struct Idle;
pub type IdleRequest = Option<Vec<SubSystem>>;
impl Command for Idle { impl Command for Idle {
type Request = IdleRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "idle"; const COMMAND: &'static str = "idle";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request {
Some(subsystems) => {
let subsystems_str = subsystems
.iter()
.map(|subsystem| subsystem.to_string())
.collect::<Vec<_>>()
.join(",");
format!("{} {}", Self::COMMAND, subsystems_str)
}
None => Self::COMMAND.to_string(),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let result = parts let result = parts
.next() .next()
.map_or(Ok((Request::Idle(None), "")), |subsystems| { .map_or(Ok((Request::Idle(None), "")), |subsystems| {
@@ -48,7 +30,7 @@ impl Command for Idle {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -2,12 +2,9 @@ use std::collections::HashMap;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, get_and_parse_optional_property, get_and_parse_property, Command, Request, RequestParserResult,
request_tokenizer::RequestTokenizer, ResponseAttributes, ResponseParserError,
response_tokenizer::{
ResponseAttributes, get_and_parse_optional_property, get_and_parse_property,
},
}; };
pub struct Stats; pub struct Stats;
@@ -24,15 +21,10 @@ pub struct StatsResponse {
} }
impl Command for Stats { impl Command for Stats {
type Request = ();
type Response = StatsResponse; type Response = StatsResponse;
const COMMAND: &'static str = "stats"; const COMMAND: &'static str = "stats";
fn serialize_request(&self, _: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Stats, "")) Ok((Request::Stats, ""))
@@ -40,8 +32,8 @@ impl Command for Stats {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<_, _> = parts.into();
let uptime = get_and_parse_property!(parts, "uptime", Text); let uptime = get_and_parse_property!(parts, "uptime", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text); let playtime = get_and_parse_property!(parts, "playtime", Text);

View File

@@ -3,14 +3,12 @@ use std::str::FromStr;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::{ use crate::common::{Audio, BoolOrOneshot, SongId, SongPosition};
commands::{Command, Request, RequestParserResult, ResponseParserError},
common::{Audio, BoolOrOneshot, SongId, SongPosition}, use crate::commands::{
request_tokenizer::RequestTokenizer, get_and_parse_optional_property, get_and_parse_property, get_optional_property, get_property,
response_tokenizer::{ Command, GenericResponseValue, Request, RequestParserResult, ResponseAttributes,
GenericResponseValue, ResponseAttributes, get_and_parse_optional_property, ResponseParserError,
get_and_parse_property, get_optional_property, get_property,
},
}; };
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -65,8 +63,8 @@ pub struct StatusResponse {
#[inline] #[inline]
fn parse_status_response( fn parse_status_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<StatusResponse, ResponseParserError<'_>> { ) -> Result<StatusResponse, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?; let parts: HashMap<&str, GenericResponseValue> = parts.into();
let partition = get_property!(parts, "partition", Text).to_string(); let partition = get_property!(parts, "partition", Text).to_string();
let volume = match get_property!(parts, "volume", Text) { let volume = match get_property!(parts, "volume", Text) {
@@ -161,15 +159,10 @@ fn parse_status_response(
pub struct Status; pub struct Status;
impl Command for Status { impl Command for Status {
type Request = ();
type Response = StatusResponse; type Response = StatusResponse;
const COMMAND: &'static str = "status"; const COMMAND: &'static str = "status";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Status, "")) Ok((Request::Status, ""))
@@ -177,7 +170,7 @@ impl Command for Status {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
parse_status_response(parts) parse_status_response(parts)
} }
} }

View File

@@ -1,39 +1,18 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{SongPosition, Uri}, Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
request_tokenizer::RequestTokenizer, ResponseParserError,
response_tokenizer::ResponseAttributes, },
common::SongPosition,
}; };
pub struct Add; pub struct Add;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddRequest {
uri: Uri,
position: Option<SongPosition>,
}
impl AddRequest {
pub fn new(uri: Uri, position: Option<SongPosition>) -> Self {
Self { uri, position }
}
}
impl Command for Add { impl Command for Add {
type Request = AddRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "add"; const COMMAND: &'static str = "add";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request.position {
Some(position) => format!("{} {} {}", Self::COMMAND, request.uri, position),
None => format!("{} {}", Self::COMMAND, request.uri),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() { let uri = match parts.next() {
Some(s) => s, Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF), None => return Err(RequestParserError::UnexpectedEOF),
@@ -54,7 +33,7 @@ impl Command for Add {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,38 +1,22 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{Command, Request, RequestParserError, RequestParserResult, ResponseParserError}, commands::{
common::{SongId, SongPosition, Uri}, get_next_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
request_tokenizer::RequestTokenizer, ResponseAttributes, ResponseParserError,
response_tokenizer::{ResponseAttributes, get_next_and_parse_property}, },
common::{SongId, SongPosition},
}; };
pub struct AddId; pub struct AddId;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddIdRequest {
pub uri: Uri,
pub position: Option<SongPosition>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct AddIdResponse { pub struct AddIdResponse {
pub id: SongId, pub id: SongId,
} }
impl Command for AddId { impl Command for AddId {
type Request = AddIdRequest;
type Response = AddIdResponse; type Response = AddIdResponse;
const COMMAND: &'static str = "addid"; const COMMAND: &'static str = "addid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
match request.position {
Some(pos) => format!("{} {} {}", Self::COMMAND, request.uri, pos),
None => format!("{} {}", Self::COMMAND, request.uri),
}
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() { let uri = match parts.next() {
Some(s) => s, Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF), None => return Err(RequestParserError::UnexpectedEOF),
@@ -53,13 +37,11 @@ impl Command for AddId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
let parts: Vec<_> = parts.into(); let parts: Vec<_> = parts.into();
let mut iter = parts.into_iter(); let mut iter = parts.into_iter();
let (key, id) = get_next_and_parse_property!(iter, Text); let (key, id) = get_next_and_parse_property!(iter, Text);
if key != "Id" { debug_assert!(key == "Id");
return Err(ResponseParserError::UnexpectedProperty(key));
}
Ok(AddIdResponse { id }) Ok(AddIdResponse { id })
} }
} }

View File

@@ -1,39 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
Request,
commands::{ commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
}, },
common::{SongId, TagName, TagValue}, Request,
request_tokenizer::RequestTokenizer,
}; };
pub struct AddTagId; 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 { impl Command for AddTagId {
type Request = AddTagIdRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "addtagid"; const COMMAND: &'static str = "addtagid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!(
"{} {} {} {}",
Self::COMMAND,
request.songid,
request.tag_name,
request.tag_value
)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let songid = songid let songid = songid
.parse() .parse()
@@ -56,7 +34,7 @@ impl Command for AddTagId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,28 +1,21 @@
use crate::{ use crate::commands::{
commands::{Command, Request, RequestParserResult, ResponseParserError}, Command, Request, RequestParserResult, ResponseAttributes, ResponseParserError,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Clear; pub struct Clear;
impl Command for Clear { impl Command for Clear {
type Request = ();
type Response = (); type Response = ();
const COMMAND: &'static str = "clear"; const COMMAND: &'static str = "clear";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Clear, "")) Ok((Request::Clear, ""))
} }
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,31 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::{SongId, TagName},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct ClearTagId; pub struct ClearTagId;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ClearTagIdRequest {
pub songid: SongId,
pub tag_name: TagName,
}
impl Command for ClearTagId { impl Command for ClearTagId {
type Request = ClearTagIdRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "cleartagid"; const COMMAND: &'static str = "cleartagid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.songid, request.tag_name)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let songid = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let songid = songid let songid = songid
.parse() .parse()
@@ -43,7 +29,7 @@ impl Command for ClearTagId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,25 +1,20 @@
use std::str::FromStr; use std::str::FromStr;
use crate::{ use crate::{
Request, commands::{
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError}, Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
common::OneOrRange, common::OneOrRange,
request_tokenizer::RequestTokenizer, Request,
response_tokenizer::ResponseAttributes,
}; };
pub struct Delete; pub struct Delete;
impl Command for Delete { impl Command for Delete {
type Request = OneOrRange;
type Response = (); type Response = ();
const COMMAND: &'static str = "delete"; const COMMAND: &'static str = "delete";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let pos_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let pos_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let one_or_range = OneOrRange::from_str(pos_or_range) let one_or_range = OneOrRange::from_str(pos_or_range)
.map_err(|_| RequestParserError::SyntaxError(0, pos_or_range.to_string()))?; .map_err(|_| RequestParserError::SyntaxError(0, pos_or_range.to_string()))?;
@@ -31,7 +26,7 @@ impl Command for Delete {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,23 +1,17 @@
use crate::{ use crate::{
commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
Request, Request,
commands::{Command, RequestParserError, RequestParserResult, ResponseParserError},
common::SongId,
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct DeleteId; pub struct DeleteId;
impl Command for DeleteId { impl Command for DeleteId {
type Request = SongId;
type Response = (); type Response = ();
const COMMAND: &'static str = "deleteid"; const COMMAND: &'static str = "deleteid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {}", Self::COMMAND, request)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let id = id let id = id
.parse() .parse()
@@ -30,7 +24,7 @@ impl Command for DeleteId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,32 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
Request,
commands::{ commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
}, },
common::{AbsouluteRelativeSongPosition, OneOrRange}, Request,
request_tokenizer::RequestTokenizer,
}; };
pub struct Move; pub struct Move;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MoveRequest {
pub from_or_range: OneOrRange,
pub to: AbsouluteRelativeSongPosition,
}
impl Command for Move { impl Command for Move {
type Request = MoveRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "move"; const COMMAND: &'static str = "move";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.from_or_range, request.to)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let from_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let from_or_range = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let from_or_range = from_or_range let from_or_range = from_or_range
.parse() .parse()
@@ -44,7 +29,7 @@ impl Command for Move {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,32 +1,17 @@
use serde::{Deserialize, Serialize};
use crate::{ use crate::{
Request,
commands::{ commands::{
Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError, Command, RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
}, },
common::{AbsouluteRelativeSongPosition, SongId}, Request,
request_tokenizer::RequestTokenizer,
}; };
pub struct MoveId; pub struct MoveId;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct MoveIdRequest {
pub id: SongId,
pub to: AbsouluteRelativeSongPosition,
}
impl Command for MoveId { impl Command for MoveId {
type Request = MoveIdRequest;
type Response = (); type Response = ();
const COMMAND: &'static str = "moveid"; const COMMAND: &'static str = "moveid";
fn serialize_request(&self, request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
format!("{} {} {}", Self::COMMAND, request.id, request.to)
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?; let id = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let id = id let id = id
.parse() .parse()
@@ -44,7 +29,7 @@ impl Command for MoveId {
fn parse_response( fn parse_response(
parts: ResponseAttributes<'_>, parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
debug_assert!(parts.is_empty()); debug_assert!(parts.is_empty());
Ok(()) Ok(())
} }

View File

@@ -1,29 +1,25 @@
use crate::{ use crate::{
commands::{Command, RequestParserResult, ResponseAttributes, ResponseParserError},
Request, Request,
commands::{Command, RequestParserResult, ResponseParserError},
request_tokenizer::RequestTokenizer,
response_tokenizer::ResponseAttributes,
}; };
pub struct Playlist; pub struct Playlist;
pub type PlaylistResponse = Vec<String>;
impl Command for Playlist { impl Command for Playlist {
type Request = (); type Response = PlaylistResponse;
type Response = ();
const COMMAND: &'static str = "playlist"; const COMMAND: &'static str = "playlist";
fn serialize_request(&self, _request: Self::Request) -> String { fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
Self::COMMAND.to_string()
}
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
debug_assert!(parts.next().is_none()); debug_assert!(parts.next().is_none());
Ok((Request::Playlist, "")) Ok((Request::Playlist, ""))
} }
fn parse_response( fn parse_response(
_parts: ResponseAttributes<'_>, _parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError<'_>> { ) -> Result<Self::Response, ResponseParserError> {
// TODO: 1: https, 2: https, etc.
unimplemented!() unimplemented!()
} }
} }

Some files were not shown because too many files have changed in this diff Show More