Implement some more commands
Some checks failed
Build and test / check (push) Failing after 13s
Build and test / test (push) Failing after 14s
Build and test / build (push) Successful in 57s
Build and test / docs (push) Successful in 1m12s

This commit is contained in:
Oystein Kristoffer Tveit 2024-12-13 18:20:03 +01:00
parent cea3d3da04
commit 9cb92741a4
Signed by: oysteikt
GPG Key ID: 9F2F7D8250F35146
21 changed files with 934 additions and 2 deletions

View File

@ -10,6 +10,7 @@ mod client_to_client;
mod connection_settings;
mod controlling_playback;
mod mounts_and_neighbors;
mod music_database;
mod partition_commands;
mod playback_options;
mod querying_mpd_status;
@ -22,6 +23,7 @@ pub use client_to_client::*;
pub use connection_settings::*;
pub use controlling_playback::*;
pub use mounts_and_neighbors::*;
pub use music_database::*;
pub use partition_commands::*;
pub use playback_options::*;
pub use querying_mpd_status::*;

View File

@ -0,0 +1,37 @@
pub mod albumart;
pub mod count;
pub mod find;
pub mod findadd;
pub mod getfingerprint;
pub mod list;
pub mod listall;
pub mod listallinfo;
pub mod listfiles;
pub mod lsinfo;
pub mod readcomments;
pub mod readpicture;
pub mod rescan;
pub mod search;
pub mod searchadd;
pub mod searchaddpl;
pub mod searchcount;
pub mod update;
pub use albumart::AlbumArt;
pub use count::Count;
pub use find::Find;
pub use findadd::FindAdd;
pub use getfingerprint::GetFingerprint;
pub use list::List;
pub use listall::ListAll;
pub use listallinfo::ListAllInfo;
pub use listfiles::ListFiles;
pub use lsinfo::LsInfo;
pub use readcomments::ReadComments;
pub use readpicture::ReadPicture;
pub use rescan::Rescan;
pub use search::Search;
pub use searchadd::SearchAdd;
pub use searchaddpl::SearchAddPl;
pub use searchcount::SearchCount;
pub use update::Update;

View File

@ -0,0 +1,51 @@
use std::collections::HashMap;
use crate::{
commands::{
get_and_parse_property, get_property, Command, Request, RequestParserError,
RequestParserResult, ResponseAttributes, ResponseParserError,
},
common::Offset,
};
pub struct AlbumArt;
pub struct AlbumArtResponse {
pub size: usize,
pub binary: Vec<u8>,
}
impl Command for AlbumArt {
type Response = AlbumArtResponse;
const COMMAND: &'static str = "albumart";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() {
Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF),
};
let offset = match parts.next() {
Some(s) => s
.parse::<Offset>()
.map_err(|_| RequestParserError::SyntaxError(1, s.to_owned()))?,
None => return Err(RequestParserError::UnexpectedEOF),
};
debug_assert!(parts.next().is_none());
Ok((Request::AlbumArt(uri.to_string(), offset), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let size = get_and_parse_property!(parts, "size", Text);
let binary = get_property!(parts, "binary", Binary).into();
Ok(AlbumArtResponse { size, binary })
}
}

View File

@ -0,0 +1,52 @@
use std::collections::HashMap;
use crate::{
commands::{
get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
},
filter::parse_filter,
};
pub struct Count;
pub struct CountResponse {
pub songs: usize,
pub playtime: u64,
}
impl Command for Count {
type Response = CountResponse;
const COMMAND: &'static str = "count";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let group = if let Some("group") = parts.next() {
Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "group".to_owned()))?,
)
} else {
None
};
debug_assert!(parts.next().is_none());
Ok((Request::Count(filter, group), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let songs = get_and_parse_property!(parts, "songs", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text);
Ok(CountResponse { songs, playtime })
}
}

View File

@ -0,0 +1,51 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
filter::parse_filter,
};
pub struct Find;
pub struct FindResponse {}
impl Command for Find {
type Response = FindResponse;
const COMMAND: &'static str = "find";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let mut sort_or_window = parts.next();
let mut sort = None;
if let Some("sort") = sort_or_window {
sort = Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
);
sort_or_window = parts.next();
}
let mut window = None;
if let Some("window") = sort_or_window {
let w = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
window = Some(
w.parse()
.map_err(|_| RequestParserError::SyntaxError(0, w.to_string()))?,
);
}
debug_assert!(parts.next().is_none());
Ok((Request::Find(filter, sort, window), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,61 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
filter::parse_filter,
};
pub struct FindAdd;
pub struct FindAddResponse {}
impl Command for FindAdd {
type Response = FindAddResponse;
const COMMAND: &'static str = "findadd";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let mut sort_or_window_or_position = parts.next();
let mut sort = None;
if let Some("sort") = sort_or_window_or_position {
sort = Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
);
sort_or_window_or_position = parts.next();
}
let mut window = None;
if let Some("window") = sort_or_window_or_position {
let w = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
window = Some(
w.parse()
.map_err(|_| RequestParserError::SyntaxError(0, w.to_string()))?,
);
sort_or_window_or_position = parts.next();
}
let mut position = None;
if let Some("position") = sort_or_window_or_position {
let p = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
position = Some(
p.parse()
.map_err(|_| RequestParserError::SyntaxError(0, p.to_string()))?,
);
}
debug_assert!(parts.next().is_none());
Ok((Request::FindAdd(filter, sort, window, position), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,38 @@
use std::collections::HashMap;
use crate::commands::{
get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
};
pub struct GetFingerprint;
pub struct GetFingerprintResponse {
pub chromaprint: String,
}
impl Command for GetFingerprint {
type Response = GetFingerprintResponse;
const COMMAND: &'static str = "getfingerprint";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let uri = uri
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))?;
debug_assert!(parts.next().is_none());
Ok((Request::GetFingerprint(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let chromaprint = get_and_parse_property!(parts, "chromaprint", Text);
Ok(GetFingerprintResponse { chromaprint })
}
}

View File

@ -0,0 +1,63 @@
use crate::{
commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
},
filter::parse_filter,
};
pub struct List;
pub type ListResponse = Vec<String>;
impl Command for List {
type Response = ListResponse;
const COMMAND: &'static str = "list";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let tagtype = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let tagtype = tagtype
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "tagtype".to_owned()))?;
// TODO: This should be optional
let filter = parse_filter(&mut parts)?;
let group = if let Some("group") = parts.next() {
let group = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
Some(
group
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "group".to_owned()))?,
)
} else {
None
};
debug_assert!(parts.next().is_none());
Ok((Request::List(tagtype, filter, group), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
debug_assert!({
let key = parts.0.first().map(|(k, _)| k);
parts.0.iter().all(|(k, _)| k == key.unwrap())
});
let list = parts
.0
.iter()
.map(|(_, v)| match v {
GenericResponseValue::Text(value) => Ok(value.to_string()),
GenericResponseValue::Binary(_) => Err(
ResponseParserError::UnexpectedPropertyType("handler", "Binary"),
),
})
.collect::<Result<Vec<_>, ResponseParserError>>()?;
Ok(list)
}
}

View File

@ -0,0 +1,32 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct ListAll;
// TODO: This is supposed to be a tree-like structure, with directories containing files and playlists
pub type ListAllResponse = Vec<String>;
impl Command for ListAll {
type Response = ListAllResponse;
const COMMAND: &'static str = "listall";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))
})
.transpose()?;
Ok((Request::ListAll(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,33 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct ListAllInfo;
// TODO: This is supposed to be a tree-like structure, with directories containing files and playlists
// in addition to the metadata of each entry
pub type ListAllInfoResponse = Vec<String>;
impl Command for ListAllInfo {
type Response = ListAllInfoResponse;
const COMMAND: &'static str = "listallinfo";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))
})
.transpose()?;
Ok((Request::ListAllInfo(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,32 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct ListFiles;
// TODO: fix this type
pub type ListFilesResponse = Vec<String>;
impl Command for ListFiles {
type Response = ListFilesResponse;
const COMMAND: &'static str = "listfiles";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))
})
.transpose()?;
Ok((Request::ListFiles(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,32 @@
use crate::commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct LsInfo;
// TODO: fix this type
pub type LsInfoResponse = Vec<String>;
impl Command for LsInfo {
type Response = LsInfoResponse;
const COMMAND: &'static str = "lsinfo";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts
.next()
.map(|s| {
s.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))
})
.transpose()?;
Ok((Request::LsInfo(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,42 @@
use std::collections::HashMap;
use crate::commands::{
Command, GenericResponseValue, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
};
pub struct ReadComments;
pub type ReadCommentsResponse = HashMap<String, String>;
impl Command for ReadComments {
type Response = ReadCommentsResponse;
const COMMAND: &'static str = "readcomments";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
let uri = uri
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "uri".to_owned()))?;
debug_assert!(parts.next().is_none());
Ok((Request::ReadComments(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
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)),
})
.collect::<Result<HashMap<_, _>, ResponseParserError>>()?;
Ok(comments)
}
}

View File

@ -0,0 +1,62 @@
use std::collections::HashMap;
use crate::{
commands::{
get_and_parse_property, get_optional_property, get_property, Command, Request,
RequestParserError, RequestParserResult, ResponseAttributes, ResponseParserError,
},
common::Offset,
};
pub struct ReadPicture;
pub struct ReadPictureResponse {
pub size: usize,
pub binary: Vec<u8>,
pub mimetype: Option<String>,
}
impl Command for ReadPicture {
type Response = Option<ReadPictureResponse>;
const COMMAND: &'static str = "readpicture";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = match parts.next() {
Some(s) => s,
None => return Err(RequestParserError::UnexpectedEOF),
};
let offset = match parts.next() {
Some(s) => s
.parse::<Offset>()
.map_err(|_| RequestParserError::SyntaxError(1, s.to_owned()))?,
None => return Err(RequestParserError::UnexpectedEOF),
};
debug_assert!(parts.next().is_none());
Ok((Request::ReadPicture(uri.to_string(), offset), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
if parts.is_empty() {
return Ok(None);
}
let size = get_and_parse_property!(parts, "size", Text);
let binary = get_property!(parts, "binary", Binary).into();
let mimetype = get_optional_property!(parts, "mimetype", Text).map(|s| s.to_string());
Ok(Some(ReadPictureResponse {
size,
binary,
mimetype,
}))
}
}

View File

@ -0,0 +1,35 @@
use std::collections::HashMap;
use crate::commands::{
get_and_parse_property, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Rescan;
pub struct RescanResponse {
pub updating_db: usize,
}
impl Command for Rescan {
type Response = RescanResponse;
const COMMAND: &'static str = "rescan";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts.next().map(|s| s.to_string());
debug_assert!(parts.next().is_none());
Ok((Request::Rescan(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let updating_db = get_and_parse_property!(parts, "updating_db", Text);
Ok(RescanResponse { updating_db })
}
}

View File

@ -0,0 +1,51 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
filter::parse_filter,
};
pub struct Search;
pub struct SearchResponse {}
impl Command for Search {
type Response = SearchResponse;
const COMMAND: &'static str = "search";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let mut sort_or_window = parts.next();
let mut sort = None;
if let Some("sort") = sort_or_window {
sort = Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
);
sort_or_window = parts.next();
}
let mut window = None;
if let Some("window") = sort_or_window {
let w = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
window = Some(
w.parse()
.map_err(|_| RequestParserError::SyntaxError(0, w.to_string()))?,
);
}
debug_assert!(parts.next().is_none());
Ok((Request::Search(filter, sort, window), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,61 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
filter::parse_filter,
};
pub struct SearchAdd;
pub struct SearchAddResponse {}
impl Command for SearchAdd {
type Response = SearchAddResponse;
const COMMAND: &'static str = "searchadd";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let mut sort_or_window_or_position = parts.next();
let mut sort = None;
if let Some("sort") = sort_or_window_or_position {
sort = Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
);
sort_or_window_or_position = parts.next();
}
let mut window = None;
if let Some("window") = sort_or_window_or_position {
let w = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
window = Some(
w.parse()
.map_err(|_| RequestParserError::SyntaxError(0, w.to_string()))?,
);
sort_or_window_or_position = parts.next();
}
let mut position = None;
if let Some("position") = sort_or_window_or_position {
let p = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
position = Some(
p.parse()
.map_err(|_| RequestParserError::SyntaxError(0, p.to_string()))?,
);
}
debug_assert!(parts.next().is_none());
Ok((Request::SearchAdd(filter, sort, window, position), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,69 @@
use crate::{
commands::{
Command, Request, RequestParserError, RequestParserResult, ResponseAttributes,
ResponseParserError,
},
filter::parse_filter,
};
pub struct SearchAddPl;
pub struct SearchAddPlResponse {}
impl Command for SearchAddPl {
type Response = SearchAddPlResponse;
const COMMAND: &'static str = "searchaddpl";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let playlist_name = parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string();
let filter = parse_filter(&mut parts)?;
let mut sort_or_window_or_position = parts.next();
let mut sort = None;
if let Some("sort") = sort_or_window_or_position {
sort = Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.to_string(),
);
sort_or_window_or_position = parts.next();
}
let mut window = None;
if let Some("window") = sort_or_window_or_position {
let w = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
window = Some(
w.parse()
.map_err(|_| RequestParserError::SyntaxError(0, w.to_string()))?,
);
sort_or_window_or_position = parts.next();
}
let mut position = None;
if let Some("position") = sort_or_window_or_position {
let p = parts.next().ok_or(RequestParserError::UnexpectedEOF)?;
position = Some(
p.parse()
.map_err(|_| RequestParserError::SyntaxError(0, p.to_string()))?,
);
}
debug_assert!(parts.next().is_none());
Ok((
Request::SearchAddPl(playlist_name, filter, sort, window, position),
"",
))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
unimplemented!()
}
}

View File

@ -0,0 +1,52 @@
use std::collections::HashMap;
use crate::{
commands::{
get_and_parse_property, Command, Request, RequestParserError, RequestParserResult,
ResponseAttributes, ResponseParserError,
},
filter::parse_filter,
};
pub struct SearchCount;
pub struct SearchCountResponse {
pub songs: usize,
pub playtime: u64,
}
impl Command for SearchCount {
type Response = SearchCountResponse;
const COMMAND: &'static str = "searchcount";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let filter = parse_filter(&mut parts)?;
let group = if let Some("group") = parts.next() {
Some(
parts
.next()
.ok_or(RequestParserError::UnexpectedEOF)?
.parse()
.map_err(|_| RequestParserError::SyntaxError(1, "group".to_owned()))?,
)
} else {
None
};
debug_assert!(parts.next().is_none());
Ok((Request::SearchCount(filter, group), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let songs = get_and_parse_property!(parts, "songs", Text);
let playtime = get_and_parse_property!(parts, "playtime", Text);
Ok(SearchCountResponse { songs, playtime })
}
}

View File

@ -0,0 +1,35 @@
use std::collections::HashMap;
use crate::commands::{
get_and_parse_property, Command, Request, RequestParserResult, ResponseAttributes,
ResponseParserError,
};
pub struct Update;
pub struct UpdateResponse {
updating_db: usize,
}
impl Command for Update {
type Response = UpdateResponse;
const COMMAND: &'static str = "update";
fn parse_request(mut parts: std::str::SplitWhitespace<'_>) -> RequestParserResult<'_> {
let uri = parts.next().map(|s| s.to_string());
debug_assert!(parts.next().is_none());
Ok((Request::Update(uri), ""))
}
fn parse_response(
parts: ResponseAttributes<'_>,
) -> Result<Self::Response, ResponseParserError> {
let parts: HashMap<_, _> = parts.into();
let updating_db = get_and_parse_property!(parts, "updating_db", Text);
Ok(UpdateResponse { updating_db })
}
}

View File

@ -103,12 +103,12 @@ pub enum Request {
Option<WindowRange>,
Option<SongPosition>,
),
List(Tag, Filter, Option<GroupType>),
List(TagName, Filter, Option<GroupType>),
#[deprecated]
ListAll(Option<Uri>),
#[deprecated]
ListAllInfo(Option<Uri>),
ListFiles(Uri),
ListFiles(Option<Uri>),
LsInfo(Option<Uri>),
ReadComments(Uri),
ReadPicture(Uri, Offset),
@ -120,6 +120,7 @@ pub enum Request {
Option<SongPosition>,
),
SearchAddPl(
PlaylistName,
Filter,
Option<Sort>,
Option<WindowRange>,
@ -294,6 +295,28 @@ pub enum GroupType {
Any,
}
impl FromStr for GroupType {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"artist" => Ok(Self::Artist),
"album" => Ok(Self::Album),
"albumartist" => Ok(Self::AlbumArtist),
"date" => Ok(Self::Date),
"genre" => Ok(Self::Genre),
"track" => Ok(Self::Track),
"composer" => Ok(Self::Composer),
"performer" => Ok(Self::Performer),
"conductor" => Ok(Self::Conductor),
"comment" => Ok(Self::Comment),
"disc" => Ok(Self::Disc),
"filename" => Ok(Self::Filename),
"any" => Ok(Self::Any),
_ => Err(()),
}
}
}
// trait RequestCommand {
// // The command name used within the protocol
// const COMMAND: &'static str;
@ -426,6 +449,24 @@ impl Request {
Save::COMMAND => Save::parse_request(parts),
/* music database */
AlbumArt::COMMAND => AlbumArt::parse_request(parts),
Count::COMMAND => Count::parse_request(parts),
GetFingerprint::COMMAND => GetFingerprint::parse_request(parts),
Find::COMMAND => Find::parse_request(parts),
FindAdd::COMMAND => FindAdd::parse_request(parts),
List::COMMAND => List::parse_request(parts),
ListAll::COMMAND => ListAll::parse_request(parts),
ListAllInfo::COMMAND => ListAllInfo::parse_request(parts),
ListFiles::COMMAND => ListFiles::parse_request(parts),
LsInfo::COMMAND => LsInfo::parse_request(parts),
ReadComments::COMMAND => ReadComments::parse_request(parts),
ReadPicture::COMMAND => ReadPicture::parse_request(parts),
Search::COMMAND => Search::parse_request(parts),
SearchAdd::COMMAND => SearchAdd::parse_request(parts),
SearchAddPl::COMMAND => SearchAddPl::parse_request(parts),
SearchCount::COMMAND => SearchCount::parse_request(parts),
Update::COMMAND => Update::parse_request(parts),
Rescan::COMMAND => Rescan::parse_request(parts),
/* mounts and neighbors */
Mount::COMMAND => Mount::parse_request(parts),