Compare commits
1 Commits
main
...
queue-resp
| Author | SHA1 | Date | |
|---|---|---|---|
|
aa56ba5a7f
|
@@ -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
4
.gitignore
vendored
@@ -1,5 +1,3 @@
|
|||||||
/target
|
/target
|
||||||
result
|
result
|
||||||
result-*
|
result-*
|
||||||
|
|
||||||
Cargo.lock
|
|
||||||
95
Cargo.lock
generated
Normal file
95
Cargo.lock
generated
Normal 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"
|
||||||
13
Cargo.toml
13
Cargo.toml
@@ -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"
|
|
||||||
|
|||||||
8
build.rs
8
build.rs
@@ -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
12
flake.lock
generated
@@ -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": {
|
||||||
|
|||||||
@@ -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";
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
style_edition = "2024"
|
|
||||||
507
src/commands.rs
507
src/commands.rs
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
|
||||||
},
|
|
||||||
},
|
|
||||||
])
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),
|
|
||||||
},
|
|
||||||
])
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(),]));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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>>()?;
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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!()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
Reference in New Issue
Block a user