make ResponseParserError self-contained, impl thiserror
This commit is contained in:
@@ -16,6 +16,7 @@ chrono = { version = "0.4.42", features = ["serde"] }
|
||||
lalrpop-util = { version = "0.22.2", features = ["lexer"] }
|
||||
paste = "1.0.15"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
thiserror = "2.0.17"
|
||||
|
||||
[dev-dependencies]
|
||||
indoc = "2.0.7"
|
||||
|
||||
@@ -109,10 +109,10 @@ where
|
||||
|
||||
/// Parses the response from its tokenized parts.
|
||||
/// See also [`parse_raw`].
|
||||
fn parse(parts: ResponseAttributes<'a>) -> Result<Self, ResponseParserError<'a>>;
|
||||
fn parse(parts: ResponseAttributes<'a>) -> Result<Self, ResponseParserError>;
|
||||
|
||||
/// Parses the response from its raw byte representation.
|
||||
fn parse_raw(raw: &'a [u8]) -> Result<Self, ResponseParserError<'a>> {
|
||||
fn parse_raw(raw: &'a [u8]) -> Result<Self, ResponseParserError> {
|
||||
Self::parse(ResponseAttributes::new_from_bytes(raw))
|
||||
}
|
||||
}
|
||||
@@ -154,11 +154,11 @@ pub trait Command<'req, 'res> {
|
||||
/// Parse the response from its tokenized parts. See also [`parse_raw_response`].
|
||||
fn parse_response(
|
||||
parts: ResponseAttributes<'res>,
|
||||
) -> Result<Self::Response, ResponseParserError<'res>> {
|
||||
) -> Result<Self::Response, ResponseParserError> {
|
||||
Self::Response::parse(parts)
|
||||
}
|
||||
/// Parse the raw response string into a response.
|
||||
fn parse_raw_response(raw: &'res [u8]) -> Result<Self::Response, ResponseParserError<'res>> {
|
||||
fn parse_raw_response(raw: &'res [u8]) -> Result<Self::Response, ResponseParserError> {
|
||||
Self::Response::parse_raw(raw)
|
||||
}
|
||||
}
|
||||
@@ -224,7 +224,7 @@ macro_rules! empty_command_response {
|
||||
|
||||
fn parse(
|
||||
_parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError<'_>> {
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
debug_assert!(_parts.is_empty());
|
||||
Ok(paste::paste! { [<$name Response>] })
|
||||
}
|
||||
@@ -319,8 +319,9 @@ macro_rules! single_optional_item_command_request {
|
||||
let item = parts
|
||||
.next()
|
||||
.map(|s| {
|
||||
s.parse()
|
||||
.map_err(|_| RequestParserError::SyntaxError(0, s.to_owned()))
|
||||
s.parse().map_err(|_| {
|
||||
crate::commands::RequestParserError::SyntaxError(0, s.to_owned())
|
||||
})
|
||||
})
|
||||
.transpose()?;
|
||||
|
||||
@@ -350,13 +351,13 @@ macro_rules! single_item_command_response {
|
||||
|
||||
fn parse(
|
||||
parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError<'_>> {
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
let map = parts.into_map()?;
|
||||
|
||||
debug_assert!(map.len() == 1, "Expected only one property in response");
|
||||
|
||||
let item_token = map.get($item_name).ok_or(
|
||||
crate::commands::ResponseParserError::MissingProperty($item_name),
|
||||
crate::commands::ResponseParserError::MissingProperty($item_name.to_string()),
|
||||
)?;
|
||||
let item_ = crate::response_tokenizer::expect_property_type!(
|
||||
Some(item_token),
|
||||
@@ -364,7 +365,10 @@ macro_rules! single_item_command_response {
|
||||
Text
|
||||
);
|
||||
let item = item_.parse::<$item_type>().map_err(|_| {
|
||||
crate::commands::ResponseParserError::InvalidProperty($item_name, item_)
|
||||
crate::commands::ResponseParserError::InvalidProperty(
|
||||
$item_name.to_string(),
|
||||
item_.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(paste::paste! { [<$name Response>] ( item ) })
|
||||
@@ -391,12 +395,12 @@ macro_rules! multi_item_command_response {
|
||||
|
||||
fn parse(
|
||||
parts: crate::commands::ResponseAttributes<'_>,
|
||||
) -> Result<Self, crate::commands::ResponseParserError<'_>> {
|
||||
) -> Result<Self, crate::commands::ResponseParserError> {
|
||||
// TODO: use lazy vec
|
||||
let parts_: Vec<_> = parts.into_vec()?;
|
||||
|
||||
if let Some((k, _)) = parts_.iter().find(|(k, _)| *k != $item_name) {
|
||||
return Err(ResponseParserError::UnexpectedProperty(k));
|
||||
return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
|
||||
}
|
||||
|
||||
let mut items = Vec::with_capacity(parts_.len());
|
||||
@@ -406,8 +410,8 @@ macro_rules! multi_item_command_response {
|
||||
let unwrapped_value = expect_property_type!(Some(value.1), $item_name, Text);
|
||||
let parsed_value = unwrapped_value.parse::<$item_type>().map_err(|_| {
|
||||
crate::commands::ResponseParserError::InvalidProperty(
|
||||
$item_name,
|
||||
unwrapped_value,
|
||||
$item_name.to_string(),
|
||||
unwrapped_value.to_string(),
|
||||
)
|
||||
})?;
|
||||
|
||||
@@ -426,6 +430,7 @@ pub(crate) use multi_item_command_response;
|
||||
pub(crate) use single_item_command_request;
|
||||
pub(crate) use single_item_command_response;
|
||||
pub(crate) use single_optional_item_command_request;
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum RequestParserError {
|
||||
@@ -437,32 +442,34 @@ pub enum RequestParserError {
|
||||
MissingNewline,
|
||||
}
|
||||
|
||||
pub type ResponseParserResult<'a, T> = Result<T, ResponseParserError<'a>>;
|
||||
pub type ResponseParserResult<'a, T> = Result<T, ResponseParserError>;
|
||||
|
||||
// TODO: should these be renamed to fit the mpd docs?
|
||||
// "Attribute" instead of "Property"?
|
||||
#[derive(Debug, Clone, PartialEq)]
|
||||
pub enum ResponseParserError<'a> {
|
||||
/// A property was expected to be present in the response, but was not found.
|
||||
MissingProperty(&'a str),
|
||||
#[derive(Error, Debug, Clone, PartialEq)]
|
||||
pub enum ResponseParserError {
|
||||
#[error("A property was expected to be present in the response, but was not found: {0}")]
|
||||
MissingProperty(String),
|
||||
|
||||
// TODO: change name to UnexpectedPropertyEncoding
|
||||
/// An expected property was found in the response, but its encoding was not as expected. (e.g. binary instead of text)
|
||||
UnexpectedPropertyType(&'a str, &'a str),
|
||||
#[error(
|
||||
"An expected property was found in the response, but its encoding was not as expected: {0}: {1}"
|
||||
)]
|
||||
UnexpectedPropertyType(String, String),
|
||||
|
||||
/// A property was found in the response that was not expected.
|
||||
UnexpectedProperty(&'a str),
|
||||
#[error("A property was found in the response that was not expected: {0}")]
|
||||
UnexpectedProperty(String),
|
||||
|
||||
/// A property was found multiple times in the response, but was only expected once.
|
||||
DuplicateProperty(&'a str),
|
||||
#[error("A property was found multiple times in the response, but was only expected once: {0}")]
|
||||
DuplicateProperty(String),
|
||||
|
||||
/// The property value is parsable, but the value is invalid or nonsensical.
|
||||
InvalidProperty(&'a str, &'a str),
|
||||
#[error("The property value is parsable, but the value is invalid or nonsensical: {0}: {1}")]
|
||||
InvalidProperty(String, String),
|
||||
|
||||
/// Could not parse the response due to a syntax error.
|
||||
SyntaxError(u64, &'a str),
|
||||
#[error("Could not parse the response due to a syntax error at position {0}: {1}")]
|
||||
SyntaxError(u64, String),
|
||||
|
||||
/// Response ended early, while more properties were expected.
|
||||
#[error("Response ended early, while more properties were expected")]
|
||||
UnexpectedEOF,
|
||||
// MissingNewline,
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl CommandResponse<'_> for OutputsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut outputs = Vec::new();
|
||||
|
||||
let mut id: Option<AudioOutputId> = None;
|
||||
@@ -61,7 +61,7 @@ impl CommandResponse<'_> for OutputsResponse {
|
||||
let id_s = expect_property_type!(Some(v), k, Text);
|
||||
id = Some(
|
||||
id_s.parse()
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, id_s))?,
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, id_s.to_string()))?,
|
||||
);
|
||||
}
|
||||
"outputname" => {
|
||||
@@ -74,7 +74,7 @@ impl CommandResponse<'_> for OutputsResponse {
|
||||
let val_s = expect_property_type!(Some(v), k, Text);
|
||||
let val: u64 = val_s
|
||||
.parse::<u64>()
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, val_s))?;
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, val_s.to_string()))?;
|
||||
enabled = Some(val != 0);
|
||||
}
|
||||
"attribute" => {
|
||||
@@ -82,16 +82,16 @@ impl CommandResponse<'_> for OutputsResponse {
|
||||
let mut parts = value.splitn(2, '=');
|
||||
let attr_key = parts
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, value))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, value.to_string()))?
|
||||
.to_string();
|
||||
let attr_value = parts
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, value))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, value.to_string()))?
|
||||
.to_string();
|
||||
attributes.insert(attr_key, attr_value);
|
||||
}
|
||||
_ => {
|
||||
return Err(ResponseParserError::UnexpectedProperty(k));
|
||||
return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ impl<'a> CommandResponse<'a> for ChannelsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
let mut channel_names = Vec::with_capacity(parts.len());
|
||||
for (key, value) in parts {
|
||||
@@ -32,7 +32,7 @@ impl<'a> CommandResponse<'a> for ChannelsResponse {
|
||||
let channel_name = expect_property_type!(Some(value), "channels", Text);
|
||||
let channel_name = channel_name
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, channel_name))?;
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, channel_name.to_string()))?;
|
||||
channel_names.push(channel_name);
|
||||
}
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ impl CommandResponse<'_> for ReadMessagesResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!(parts.len() % 2 == 0);
|
||||
|
||||
@@ -44,7 +44,7 @@ impl CommandResponse<'_> for ReadMessagesResponse {
|
||||
let channel = expect_property_type!(Some(cvalue), "channel", Text);
|
||||
let channel = channel
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, channel))?;
|
||||
.map_err(|_| ResponseParserError::SyntaxError(0, channel.to_string()))?;
|
||||
|
||||
let message = expect_property_type!(Some(mvalue), "message", Text).to_string();
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
commands::{Command, empty_command_response, single_item_command_request},
|
||||
commands::{Command, empty_command_response, single_optional_item_command_request},
|
||||
types::SongPosition,
|
||||
};
|
||||
|
||||
pub struct Play;
|
||||
|
||||
single_item_command_request!(Play, "play", SongPosition);
|
||||
single_optional_item_command_request!(Play, "play", SongPosition);
|
||||
|
||||
empty_command_response!(Play);
|
||||
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
use crate::{
|
||||
commands::{Command, empty_command_response, single_item_command_request},
|
||||
commands::{Command, empty_command_response, single_optional_item_command_request},
|
||||
types::SongId,
|
||||
};
|
||||
|
||||
pub struct PlayId;
|
||||
|
||||
single_item_command_request!(PlayId, "playid", SongId);
|
||||
single_optional_item_command_request!(PlayId, "playid", SongId);
|
||||
|
||||
empty_command_response!(PlayId);
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ impl CommandResponse<'_> for ListNeighborsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!(parts.len() % 2 == 0);
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ impl CommandResponse<'_> for AlbumArtResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let size = get_and_parse_property!(parts, "size", Text);
|
||||
|
||||
@@ -81,7 +81,7 @@ impl CommandResponse<'_> for CountResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let songs = get_and_parse_property!(parts, "songs", Text);
|
||||
|
||||
@@ -96,19 +96,19 @@ impl CommandResponse<'_> for FindResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
.map(|i| match i {
|
||||
DbSelectionPrintResponse::Song(db_song_info) => Ok(db_song_info),
|
||||
DbSelectionPrintResponse::Directory(_db_directory_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("directory"))
|
||||
}
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("playlist"))
|
||||
}
|
||||
DbSelectionPrintResponse::Directory(_db_directory_info) => Err(
|
||||
ResponseParserError::UnexpectedProperty("directory".to_string()),
|
||||
),
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => Err(
|
||||
ResponseParserError::UnexpectedProperty("playlist".to_string()),
|
||||
),
|
||||
})
|
||||
.collect::<Result<Vec<DbSongInfo>, ResponseParserError<'_>>>()
|
||||
.collect::<Result<Vec<DbSongInfo>, ResponseParserError>>()
|
||||
.map(FindResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ impl CommandResponse<'_> for GetFingerprintResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let chromaprint = get_and_parse_property!(parts, "chromaprint", Text);
|
||||
|
||||
@@ -117,7 +117,7 @@ impl CommandResponse<'_> for ListResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts_: Vec<_> = parts.into_vec()?;
|
||||
debug_assert!({
|
||||
let key = parts_.first().map(|(k, _)| k);
|
||||
|
||||
@@ -26,7 +26,7 @@ impl CommandResponse<'_> for ListAllResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
Ok(ListAllResponse(result))
|
||||
|
||||
@@ -26,7 +26,7 @@ impl CommandResponse<'_> for ListAllInfoResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
Ok(ListAllInfoResponse(result))
|
||||
|
||||
@@ -25,19 +25,19 @@ impl CommandResponse<'_> for ListFilesResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
.map(|i| match i {
|
||||
DbSelectionPrintResponse::Directory(db_directory_info) => Ok(db_directory_info),
|
||||
DbSelectionPrintResponse::Song(_db_song_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("file"))
|
||||
}
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("playlist"))
|
||||
Err(ResponseParserError::UnexpectedProperty("file".to_string()))
|
||||
}
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => Err(
|
||||
ResponseParserError::UnexpectedProperty("playlist".to_string()),
|
||||
),
|
||||
})
|
||||
.collect::<Result<Vec<_>, ResponseParserError<'_>>>()
|
||||
.collect::<Result<Vec<_>, ResponseParserError>>()
|
||||
.map(ListFilesResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ impl CommandResponse<'_> for LsInfoResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let result = DbSelectionPrintResponse::parse(parts)?;
|
||||
|
||||
Ok(LsInfoResponse(result))
|
||||
|
||||
@@ -24,14 +24,16 @@ impl CommandResponse<'_> for ReadCommentsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let comments = parts
|
||||
.iter()
|
||||
.map(|(k, v)| match v {
|
||||
GenericResponseValue::Text(s) => Ok((k.to_string(), s.to_string())),
|
||||
GenericResponseValue::Binary(_) => Err(ResponseParserError::SyntaxError(1, k)),
|
||||
GenericResponseValue::Binary(_) => {
|
||||
Err(ResponseParserError::SyntaxError(1, k.to_string()))
|
||||
}
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, ResponseParserError>>()?;
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ impl CommandResponse<'_> for ReadPictureResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
// TODO: is empty response possible?
|
||||
|
||||
@@ -29,7 +29,7 @@ impl CommandResponse<'_> for RescanResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let updating_db = get_and_parse_property!(parts, "updating_db", Text);
|
||||
|
||||
@@ -96,19 +96,19 @@ impl CommandResponse<'_> for SearchResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
DbSelectionPrintResponse::parse(parts)?
|
||||
.into_iter()
|
||||
.map(|i| match i {
|
||||
DbSelectionPrintResponse::Song(db_song_info) => Ok(db_song_info),
|
||||
DbSelectionPrintResponse::Directory(_db_directory_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("directory"))
|
||||
}
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => {
|
||||
Err(ResponseParserError::UnexpectedProperty("playlist"))
|
||||
}
|
||||
DbSelectionPrintResponse::Directory(_db_directory_info) => Err(
|
||||
ResponseParserError::UnexpectedProperty("directory".to_string()),
|
||||
),
|
||||
DbSelectionPrintResponse::Playlist(_db_playlist_info) => Err(
|
||||
ResponseParserError::UnexpectedProperty("playlist".to_string()),
|
||||
),
|
||||
})
|
||||
.collect::<Result<Vec<DbSongInfo>, ResponseParserError<'_>>>()
|
||||
.collect::<Result<Vec<DbSongInfo>, ResponseParserError>>()
|
||||
.map(SearchResponse)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ impl CommandResponse<'_> for SearchCountResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let songs = get_and_parse_property!(parts, "songs", Text);
|
||||
|
||||
@@ -29,7 +29,7 @@ impl CommandResponse<'_> for UpdateResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let updating_db = get_and_parse_property!(parts, "updating_db", Text);
|
||||
|
||||
@@ -26,13 +26,16 @@ impl CommandResponse<'_> for ReplayGainStatusResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
let replay_gain_mode = get_property!(parts, "replay_gain_mode", Text);
|
||||
|
||||
Ok(ReplayGainStatusResponse {
|
||||
replay_gain_mode: ReplayGainModeMode::from_str(replay_gain_mode).map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("replay_gain_mode", replay_gain_mode)
|
||||
ResponseParserError::InvalidProperty(
|
||||
"replay_gain_mode".to_string(),
|
||||
replay_gain_mode.to_string(),
|
||||
)
|
||||
})?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ impl CommandResponse<'_> for CurrentSongResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let position: SongPosition = get_and_parse_property!(parts, "Pos", Text);
|
||||
|
||||
@@ -33,7 +33,7 @@ impl CommandResponse<'_> for StatsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let uptime = get_and_parse_property!(parts, "uptime", Text);
|
||||
|
||||
@@ -74,29 +74,33 @@ impl CommandResponse<'_> for StatusResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
let partition = get_property!(parts, "partition", Text).to_string();
|
||||
|
||||
let volume = match get_property!(parts, "volume", Text) {
|
||||
"-1" => None,
|
||||
volume => Some(
|
||||
volume
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::InvalidProperty("volume", volume))?,
|
||||
),
|
||||
volume => Some(volume.parse().map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("volume".to_string(), volume.to_string())
|
||||
})?),
|
||||
};
|
||||
|
||||
let repeat = match get_property!(parts, "repeat", Text) {
|
||||
"0" => Ok(false),
|
||||
"1" => Ok(true),
|
||||
repeat => Err(ResponseParserError::InvalidProperty("repeat", repeat)),
|
||||
repeat => Err(ResponseParserError::InvalidProperty(
|
||||
"repeat".to_string(),
|
||||
repeat.to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let random = match get_property!(parts, "random", Text) {
|
||||
"0" => Ok(false),
|
||||
"1" => Ok(true),
|
||||
random => Err(ResponseParserError::InvalidProperty("random", random)),
|
||||
random => Err(ResponseParserError::InvalidProperty(
|
||||
"random".to_string(),
|
||||
random.to_string(),
|
||||
)),
|
||||
}?;
|
||||
|
||||
let single = get_and_parse_property!(parts, "single", Text);
|
||||
@@ -116,14 +120,18 @@ impl CommandResponse<'_> for StatusResponse {
|
||||
let mut parts = time.split(':');
|
||||
let elapsed = parts
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, time))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, time.to_string()))?
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::InvalidProperty("time", time))?;
|
||||
.map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("time".to_string(), time.to_string())
|
||||
})?;
|
||||
let duration = parts
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, time))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, time.to_string()))?
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::InvalidProperty("time", time))?;
|
||||
.map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("time".to_string(), time.to_string())
|
||||
})?;
|
||||
Some((elapsed, duration))
|
||||
}
|
||||
None => None,
|
||||
|
||||
@@ -73,12 +73,12 @@ impl CommandResponse<'_> for AddIdResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into();
|
||||
let mut iter = parts.into_iter();
|
||||
let (key, id) = get_next_and_parse_property!(iter, Text);
|
||||
if key != "Id" {
|
||||
return Err(ResponseParserError::UnexpectedProperty(key));
|
||||
return Err(ResponseParserError::UnexpectedProperty(key.to_string()));
|
||||
}
|
||||
Ok(AddIdResponse { id })
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ impl CommandResponse<'_> for PlaylistResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -105,7 +105,7 @@ impl CommandResponse<'_> for PlaylistFindResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ impl CommandResponse<'_> for PlaylistIdResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ impl CommandResponse<'_> for PlaylistInfoResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(_parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ impl CommandResponse<'_> for PlaylistSearchResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ impl CommandResponse<'_> for PlChangesResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ impl CommandResponse<'_> for PlChangesPosIdResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ impl CommandResponse<'_> for ConfigResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let music_directory = get_property!(parts, "music_directory", Text).to_string();
|
||||
|
||||
@@ -28,7 +28,7 @@ impl CommandResponse<'_> for DecodersResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut result = Vec::new();
|
||||
let mut current_decoder: Option<Decoder> = None;
|
||||
for (key, value) in parts.into_vec()?.into_iter() {
|
||||
@@ -49,19 +49,19 @@ impl CommandResponse<'_> for DecodersResponse {
|
||||
"suffix" => {
|
||||
current_decoder
|
||||
.as_mut()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, key))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, key.to_string()))?
|
||||
.suffixes
|
||||
.push(expect_property_type!(Some(value), key, Text).to_string());
|
||||
}
|
||||
"mime_type" => {
|
||||
current_decoder
|
||||
.as_mut()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, key))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, key.to_string()))?
|
||||
.mime_types
|
||||
.push(expect_property_type!(Some(value), key, Text).to_string());
|
||||
}
|
||||
k => {
|
||||
return Err(ResponseParserError::UnexpectedProperty(k));
|
||||
return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ impl CommandResponse<'_> for StickerFindResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
let mut stickers = Vec::with_capacity(parts.len() / 2);
|
||||
|
||||
@@ -144,7 +144,9 @@ impl CommandResponse<'_> for StickerFindResponse {
|
||||
.ok_or(ResponseParserError::UnexpectedEOF)?;
|
||||
|
||||
if sticker.0 != "sticker" {
|
||||
return Err(ResponseParserError::UnexpectedProperty(sticker.0));
|
||||
return Err(ResponseParserError::UnexpectedProperty(
|
||||
sticker.0.to_string(),
|
||||
));
|
||||
}
|
||||
// TODO: check that this is a valid sticker type
|
||||
// debug_assert!(uri.0 == "");
|
||||
@@ -157,11 +159,11 @@ impl CommandResponse<'_> for StickerFindResponse {
|
||||
let mut split = sticker.split("=");
|
||||
let name = split
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, sticker))?
|
||||
.ok_or(ResponseParserError::SyntaxError(0, sticker.to_string()))?
|
||||
.to_string();
|
||||
let value = split
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(1, sticker))?
|
||||
.ok_or(ResponseParserError::SyntaxError(1, sticker.to_string()))?
|
||||
.to_string();
|
||||
|
||||
stickers.push(StickerFindResponseEntry { uri, name, value });
|
||||
|
||||
@@ -66,19 +66,22 @@ impl CommandResponse<'_> for StickerListResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
if let Some((k, _)) = parts.iter().find(|(k, _)| *k != "sticker") {
|
||||
return Err(ResponseParserError::UnexpectedProperty(k));
|
||||
return Err(ResponseParserError::UnexpectedProperty(k.to_string()));
|
||||
}
|
||||
|
||||
let result = parts
|
||||
.iter()
|
||||
.map(|(_, v)| match v {
|
||||
GenericResponseValue::Text(value) => Ok(value),
|
||||
GenericResponseValue::Binary(_) => Err(
|
||||
ResponseParserError::UnexpectedPropertyType("sticker", "Binary"),
|
||||
),
|
||||
GenericResponseValue::Binary(_) => {
|
||||
Err(ResponseParserError::UnexpectedPropertyType(
|
||||
"sticker".to_string(),
|
||||
"Binary".to_string(),
|
||||
))
|
||||
}
|
||||
})
|
||||
.collect::<Result<Vec<_>, ResponseParserError>>()?;
|
||||
|
||||
@@ -88,8 +91,12 @@ impl CommandResponse<'_> for StickerListResponse {
|
||||
// TODO: This assumes the first = is the only one.
|
||||
// See: https://github.com/MusicPlayerDaemon/MPD/issues/2166
|
||||
let mut split = v.split('=');
|
||||
let key = split.next().ok_or(ResponseParserError::SyntaxError(0, v))?;
|
||||
let value = split.next().ok_or(ResponseParserError::SyntaxError(1, v))?;
|
||||
let key = split
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(0, v.to_string()))?;
|
||||
let value = split
|
||||
.next()
|
||||
.ok_or(ResponseParserError::SyntaxError(1, v.to_string()))?;
|
||||
Ok((key.to_string(), value.to_string()))
|
||||
})
|
||||
.collect::<Result<HashMap<_, _>, ResponseParserError>>()
|
||||
|
||||
@@ -66,7 +66,7 @@ impl CommandResponse<'_> for StickerNamesTypesResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: Vec<_> = parts.into_vec()?;
|
||||
let mut result = HashMap::new();
|
||||
|
||||
@@ -76,10 +76,14 @@ impl CommandResponse<'_> for StickerNamesTypesResponse {
|
||||
let (type_key, type_value) = name_type_pair[1];
|
||||
|
||||
if name_key != "name" {
|
||||
return Err(ResponseParserError::UnexpectedProperty(name_key));
|
||||
return Err(ResponseParserError::UnexpectedProperty(
|
||||
name_key.to_string(),
|
||||
));
|
||||
}
|
||||
if type_key != "type" {
|
||||
return Err(ResponseParserError::UnexpectedProperty(type_key));
|
||||
return Err(ResponseParserError::UnexpectedProperty(
|
||||
type_key.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
let name = expect_property_type!(Some(name_value), "name", Text).to_string();
|
||||
|
||||
@@ -70,7 +70,7 @@ impl CommandResponse<'_> for ListPlaylistInfoResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ impl CommandResponse<'_> for ListPlaylistsResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let mut parts = parts.into_lazy_vec().into_iter().peekable();
|
||||
// TODO: count instances of 'playlist' to preallocate
|
||||
let mut result = Vec::new();
|
||||
|
||||
@@ -27,7 +27,7 @@ impl CommandResponse<'_> for PlaylistLengthResponse {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
|
||||
let songs = get_and_parse_property!(parts, "songs", Text);
|
||||
|
||||
@@ -36,8 +36,8 @@ pub enum Request {
|
||||
// -- Playback Control Commands -- //
|
||||
Next,
|
||||
Pause(Option<bool>),
|
||||
Play(SongPosition),
|
||||
PlayId(SongId),
|
||||
Play(Option<SongPosition>),
|
||||
PlayId(Option<SongId>),
|
||||
Previous,
|
||||
Seek(SongPosition, TimeWithFractions),
|
||||
SeekId(SongId, TimeWithFractions),
|
||||
|
||||
@@ -33,29 +33,27 @@ impl<'a> ResponseAttributes<'a> {
|
||||
|
||||
pub fn into_map(
|
||||
self,
|
||||
) -> Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError<'a>> {
|
||||
) -> Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn into_vec(
|
||||
self,
|
||||
) -> Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError<'a>> {
|
||||
pub fn into_vec(self) -> Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn into_lazy_vec(
|
||||
self,
|
||||
) -> Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError<'a>>> {
|
||||
) -> Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>> {
|
||||
self.into()
|
||||
}
|
||||
|
||||
pub fn verify_all_keys_equal(&self, expected_key: &str) -> Result<(), ResponseParserError<'a>> {
|
||||
pub fn verify_all_keys_equal(&self, expected_key: &str) -> Result<(), ResponseParserError> {
|
||||
let mut copy = self.clone();
|
||||
copy.cursor = 0;
|
||||
for item in copy {
|
||||
let (key, _) = item?;
|
||||
if key != expected_key {
|
||||
return Err(ResponseParserError::UnexpectedProperty(key));
|
||||
return Err(ResponseParserError::UnexpectedProperty(key.to_string()));
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -67,7 +65,7 @@ impl<'a> ResponseAttributes<'a> {
|
||||
}
|
||||
|
||||
impl<'a> Iterator for ResponseAttributes<'a> {
|
||||
type Item = Result<(&'a str, GenericResponseValue<'a>), ResponseParserError<'a>>;
|
||||
type Item = Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>;
|
||||
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
if self.cursor >= self.bytestring.len() {
|
||||
@@ -121,7 +119,7 @@ impl<'a> Iterator for ResponseAttributes<'a> {
|
||||
// TODO: throw more specific error
|
||||
return Some(Err(ResponseParserError::SyntaxError(
|
||||
0,
|
||||
"Invalid byte count",
|
||||
"Invalid byte count".to_string(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
@@ -131,7 +129,7 @@ impl<'a> Iterator for ResponseAttributes<'a> {
|
||||
// TODO: throw more specific error
|
||||
return Some(Err(ResponseParserError::SyntaxError(
|
||||
0,
|
||||
"Invalid byte count",
|
||||
"Invalid byte count".to_string(),
|
||||
)));
|
||||
}
|
||||
};
|
||||
@@ -170,14 +168,14 @@ impl<'a> Iterator for ResponseAttributes<'a> {
|
||||
}
|
||||
|
||||
impl<'a> From<ResponseAttributes<'a>>
|
||||
for Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError<'a>>
|
||||
for Result<HashMap<&'a str, GenericResponseValue<'a>>, ResponseParserError>
|
||||
{
|
||||
fn from(val: ResponseAttributes<'a>) -> Self {
|
||||
let mut map = HashMap::new();
|
||||
for item in val {
|
||||
let (k, v) = item?;
|
||||
if map.contains_key(k) {
|
||||
return Err(ResponseParserError::DuplicateProperty(k));
|
||||
return Err(ResponseParserError::DuplicateProperty(k.to_string()));
|
||||
}
|
||||
map.insert(k, v);
|
||||
}
|
||||
@@ -186,7 +184,7 @@ impl<'a> From<ResponseAttributes<'a>>
|
||||
}
|
||||
|
||||
impl<'a> From<ResponseAttributes<'a>>
|
||||
for Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError<'a>>>
|
||||
for Vec<Result<(&'a str, GenericResponseValue<'a>), ResponseParserError>>
|
||||
{
|
||||
fn from(val: ResponseAttributes<'a>) -> Self {
|
||||
val.collect()
|
||||
@@ -194,7 +192,7 @@ impl<'a> From<ResponseAttributes<'a>>
|
||||
}
|
||||
|
||||
impl<'a> From<ResponseAttributes<'a>>
|
||||
for Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError<'a>>
|
||||
for Result<Vec<(&'a str, GenericResponseValue<'a>)>, ResponseParserError>
|
||||
{
|
||||
fn from(val: ResponseAttributes<'a>) -> Self {
|
||||
val.collect()
|
||||
@@ -220,8 +218,8 @@ macro_rules! _expect_property_type {
|
||||
};
|
||||
return Err(
|
||||
crate::commands::ResponseParserError::UnexpectedPropertyType(
|
||||
$name,
|
||||
actual_type,
|
||||
$name.to_string(),
|
||||
actual_type.to_string(),
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -235,7 +233,10 @@ macro_rules! _parse_optional_property_type {
|
||||
$property
|
||||
.map(|value| {
|
||||
value.parse().map_err(|_| {
|
||||
crate::commands::ResponseParserError::InvalidProperty($name, value)
|
||||
crate::commands::ResponseParserError::InvalidProperty(
|
||||
$name.to_string(),
|
||||
value.to_string(),
|
||||
)
|
||||
})
|
||||
})
|
||||
.transpose()?
|
||||
@@ -246,7 +247,11 @@ macro_rules! _unwrap_optional_property_type {
|
||||
($name:expr, $property:expr) => {
|
||||
match $property {
|
||||
Some(value) => value,
|
||||
None => return Err(crate::commands::ResponseParserError::MissingProperty($name)),
|
||||
None => {
|
||||
return Err(crate::commands::ResponseParserError::MissingProperty(
|
||||
$name.to_string(),
|
||||
))
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -402,7 +407,7 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
#[cfg(debug_assertions)]
|
||||
fn test_valid_hashmap_uniqueness_assert() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_valid_hashmap_uniqueness_assert() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"a: 1
|
||||
A: 2
|
||||
@@ -431,7 +436,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_single_attribute() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_single_attribute() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name: Sticker1
|
||||
OK"
|
||||
@@ -447,7 +452,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_multiple_attributes() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_multiple_attributes() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name: Sticker1
|
||||
type: emoji
|
||||
@@ -467,7 +472,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_empty_response() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_empty_response() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"OK"
|
||||
};
|
||||
@@ -478,7 +483,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_unexpected_eof() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_unexpected_eof() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name: Sticker1
|
||||
type: emoji"
|
||||
@@ -490,7 +495,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_repeated_attribute() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_repeated_attribute() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name: Sticker1
|
||||
name: Sticker2
|
||||
@@ -505,7 +510,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_empty_line() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_empty_line() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name: Sticker1
|
||||
|
||||
@@ -524,7 +529,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_no_value() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_no_value() -> Result<(), ResponseParserError> {
|
||||
let raw_response = indoc! {
|
||||
"name:
|
||||
type: emoji
|
||||
@@ -539,7 +544,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_response_attributes_binary_data() -> Result<(), ResponseParserError<'static>> {
|
||||
fn test_response_attributes_binary_data() -> Result<(), ResponseParserError> {
|
||||
let bytestring: &[u8] = b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09";
|
||||
let raw_response = {
|
||||
let mut response = format!("binary: {}\n", bytestring.len()).into_bytes();
|
||||
|
||||
@@ -21,18 +21,18 @@ impl DbDirectoryInfo {
|
||||
self.last_modified.is_some()
|
||||
}
|
||||
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
Self::parse_map(parts)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_map<'a>(
|
||||
parts: HashMap<&str, GenericResponseValue<'a>>,
|
||||
) -> Result<Self, ResponseParserError<'a>> {
|
||||
) -> Result<Self, ResponseParserError> {
|
||||
let directory = get_property!(parts, "directory", Text);
|
||||
let directory = directory
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::InvalidProperty("directory", directory))?;
|
||||
let directory = directory.parse().map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("directory".to_string(), directory.to_string())
|
||||
})?;
|
||||
|
||||
let last_modified =
|
||||
get_optional_property!(parts, "Last-Modified", Text).map(|s| s.to_owned());
|
||||
|
||||
@@ -21,18 +21,18 @@ impl DbPlaylistInfo {
|
||||
self.last_modified.is_some()
|
||||
}
|
||||
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
Self::parse_map(parts)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_map<'a>(
|
||||
parts: HashMap<&str, GenericResponseValue<'a>>,
|
||||
) -> Result<Self, ResponseParserError<'a>> {
|
||||
) -> Result<Self, ResponseParserError> {
|
||||
let playlist = get_property!(parts, "playlist", Text);
|
||||
let playlist = playlist
|
||||
.parse()
|
||||
.map_err(|_| ResponseParserError::InvalidProperty("playlist", playlist))?;
|
||||
let playlist = playlist.parse().map_err(|_| {
|
||||
ResponseParserError::InvalidProperty("playlist".to_string(), playlist.to_string())
|
||||
})?;
|
||||
let last_modified =
|
||||
get_optional_property!(parts, "Last-Modified", Text).map(|s| s.to_owned());
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ impl DbSelectionPrintResponse {
|
||||
|
||||
pub fn parse(
|
||||
parts: ResponseAttributes<'_>,
|
||||
) -> Result<Vec<DbSelectionPrintResponse>, ResponseParserError<'_>> {
|
||||
) -> Result<Vec<DbSelectionPrintResponse>, ResponseParserError> {
|
||||
debug_assert!(!parts.is_empty());
|
||||
let vec: Vec<_> = parts.into_vec()?;
|
||||
|
||||
@@ -48,9 +48,9 @@ impl DbSelectionPrintResponse {
|
||||
"playlist" => {
|
||||
DbPlaylistInfo::parse_map(attrs).map(DbSelectionPrintResponse::Playlist)
|
||||
}
|
||||
p => Err(ResponseParserError::UnexpectedProperty(p)),
|
||||
p => Err(ResponseParserError::UnexpectedProperty(p.to_string())),
|
||||
})
|
||||
.collect::<Result<Vec<DbSelectionPrintResponse>, ResponseParserError<'_>>>()
|
||||
.collect::<Result<Vec<DbSelectionPrintResponse>, ResponseParserError>>()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -47,17 +47,17 @@ impl DbSongInfo {
|
||||
|| self.playlist.is_some()
|
||||
}
|
||||
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError<'_>> {
|
||||
pub fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
|
||||
let parts: HashMap<_, _> = parts.into_map()?;
|
||||
Self::parse_map(parts)
|
||||
}
|
||||
|
||||
pub(crate) fn parse_map<'a>(
|
||||
mut parts: HashMap<&'a str, GenericResponseValue<'a>>,
|
||||
) -> Result<Self, ResponseParserError<'a>> {
|
||||
) -> Result<Self, ResponseParserError> {
|
||||
let file: PathBuf = match remove_and_parse_next_optional_property!(parts, "file", Text) {
|
||||
Some(f) => f,
|
||||
None => return Err(ResponseParserError::MissingProperty("file")),
|
||||
None => return Err(ResponseParserError::MissingProperty("file".to_string())),
|
||||
};
|
||||
|
||||
if parts.is_empty() {
|
||||
@@ -88,7 +88,7 @@ impl DbSongInfo {
|
||||
let value = expect_property_type!(Some(value), key, Text).to_string();
|
||||
Ok(Tag::new(key.to_string(), value))
|
||||
})
|
||||
.collect::<Result<Vec<Tag>, ResponseParserError<'_>>>()?;
|
||||
.collect::<Result<Vec<Tag>, ResponseParserError>>()?;
|
||||
tags.sort_unstable();
|
||||
|
||||
Ok(DbSongInfo {
|
||||
|
||||
Reference in New Issue
Block a user