This commit contains a rewrite of the response tokenizer, which introduces lazy parsing of the response, handling of binary data, some tests, as well as just generally more robustness against errors.
144 lines
4.5 KiB
Rust
144 lines
4.5 KiB
Rust
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::{
|
|
commands::{Command, Request, RequestParserResult, ResponseParserError},
|
|
request_tokenizer::RequestTokenizer,
|
|
response_tokenizer::{ResponseAttributes, expect_property_type},
|
|
};
|
|
|
|
pub struct Decoders;
|
|
|
|
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
pub struct Decoder {
|
|
pub plugin: String,
|
|
pub suffixes: Vec<String>,
|
|
pub mime_types: Vec<String>,
|
|
}
|
|
|
|
pub type DecodersResponse = Vec<Decoder>;
|
|
|
|
impl Command for Decoders {
|
|
type Request = ();
|
|
type Response = DecodersResponse;
|
|
const COMMAND: &'static str = "decoders";
|
|
|
|
fn serialize_request(&self, _request: Self::Request) -> String {
|
|
Self::COMMAND.to_string()
|
|
}
|
|
|
|
fn parse_request(mut parts: RequestTokenizer<'_>) -> RequestParserResult<'_> {
|
|
debug_assert!(parts.next().is_none());
|
|
Ok((Request::Decoders, ""))
|
|
}
|
|
|
|
fn parse_response(
|
|
parts: ResponseAttributes<'_>,
|
|
) -> Result<Self::Response, ResponseParserError<'_>> {
|
|
let mut result = Vec::new();
|
|
let mut current_decoder: Option<Decoder> = None;
|
|
for (key, value) in parts.into_vec()?.into_iter() {
|
|
match key {
|
|
"plugin" => {
|
|
if let Some(decoder) = current_decoder.take() {
|
|
result.push(decoder);
|
|
}
|
|
|
|
let plugin_name = expect_property_type!(Some(value), key, Text).to_string();
|
|
|
|
current_decoder = Some(Decoder {
|
|
plugin: plugin_name,
|
|
suffixes: Vec::new(),
|
|
mime_types: Vec::new(),
|
|
});
|
|
}
|
|
"suffix" => {
|
|
current_decoder
|
|
.as_mut()
|
|
.ok_or(ResponseParserError::SyntaxError(0, key))?
|
|
.suffixes
|
|
.push(expect_property_type!(Some(value), key, Text).to_string());
|
|
}
|
|
"mime_type" => {
|
|
current_decoder
|
|
.as_mut()
|
|
.ok_or(ResponseParserError::SyntaxError(0, key))?
|
|
.mime_types
|
|
.push(expect_property_type!(Some(value), key, Text).to_string());
|
|
}
|
|
k => {
|
|
return Err(ResponseParserError::UnexpectedProperty(k));
|
|
}
|
|
}
|
|
}
|
|
|
|
if let Some(decoder) = current_decoder.take() {
|
|
result.push(decoder);
|
|
}
|
|
|
|
Ok(result)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use indoc::indoc;
|
|
|
|
use super::*;
|
|
|
|
#[test]
|
|
fn test_parse_response() {
|
|
let input = indoc! {"
|
|
plugin: audiofile
|
|
suffix: wav
|
|
suffix: au
|
|
suffix: aiff
|
|
suffix: aif
|
|
mime_type: audio/wav
|
|
mime_type: audio/aiff
|
|
mime_type: audio/x-wav
|
|
mime_type: audio/x-aiff
|
|
plugin: pcm
|
|
mime_type: audio/L16
|
|
mime_type: audio/L24
|
|
mime_type: audio/x-mpd-float
|
|
mime_type: audio/x-mpd-cdda-pcm
|
|
mime_type: audio/x-mpd-cdda-pcm-reverse
|
|
mime_type: audio/x-mpd-alsa-pcm
|
|
OK
|
|
"};
|
|
let result = Decoders::parse_raw_response(input);
|
|
assert_eq!(
|
|
result,
|
|
Ok(vec![
|
|
Decoder {
|
|
plugin: "audiofile".to_string(),
|
|
suffixes: vec![
|
|
"wav".to_string(),
|
|
"au".to_string(),
|
|
"aiff".to_string(),
|
|
"aif".to_string()
|
|
],
|
|
mime_types: vec![
|
|
"audio/wav".to_string(),
|
|
"audio/aiff".to_string(),
|
|
"audio/x-wav".to_string(),
|
|
"audio/x-aiff".to_string()
|
|
],
|
|
},
|
|
Decoder {
|
|
plugin: "pcm".to_string(),
|
|
suffixes: vec![],
|
|
mime_types: vec![
|
|
"audio/L16".to_string(),
|
|
"audio/L24".to_string(),
|
|
"audio/x-mpd-float".to_string(),
|
|
"audio/x-mpd-cdda-pcm".to_string(),
|
|
"audio/x-mpd-cdda-pcm-reverse".to_string(),
|
|
"audio/x-mpd-alsa-pcm".to_string(),
|
|
],
|
|
},
|
|
])
|
|
);
|
|
}
|
|
}
|