Implement some more commands
This commit is contained in:
parent
cea3d3da04
commit
9cb92741a4
@ -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::*;
|
||||
|
37
src/commands/music_database.rs
Normal file
37
src/commands/music_database.rs
Normal 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;
|
51
src/commands/music_database/albumart.rs
Normal file
51
src/commands/music_database/albumart.rs
Normal 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 })
|
||||
}
|
||||
}
|
52
src/commands/music_database/count.rs
Normal file
52
src/commands/music_database/count.rs
Normal 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 })
|
||||
}
|
||||
}
|
51
src/commands/music_database/find.rs
Normal file
51
src/commands/music_database/find.rs
Normal 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!()
|
||||
}
|
||||
}
|
61
src/commands/music_database/findadd.rs
Normal file
61
src/commands/music_database/findadd.rs
Normal 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!()
|
||||
}
|
||||
}
|
38
src/commands/music_database/getfingerprint.rs
Normal file
38
src/commands/music_database/getfingerprint.rs
Normal 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 })
|
||||
}
|
||||
}
|
63
src/commands/music_database/list.rs
Normal file
63
src/commands/music_database/list.rs
Normal 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)
|
||||
}
|
||||
}
|
32
src/commands/music_database/listall.rs
Normal file
32
src/commands/music_database/listall.rs
Normal 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!()
|
||||
}
|
||||
}
|
33
src/commands/music_database/listallinfo.rs
Normal file
33
src/commands/music_database/listallinfo.rs
Normal 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!()
|
||||
}
|
||||
}
|
32
src/commands/music_database/listfiles.rs
Normal file
32
src/commands/music_database/listfiles.rs
Normal 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!()
|
||||
}
|
||||
}
|
32
src/commands/music_database/lsinfo.rs
Normal file
32
src/commands/music_database/lsinfo.rs
Normal 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!()
|
||||
}
|
||||
}
|
42
src/commands/music_database/readcomments.rs
Normal file
42
src/commands/music_database/readcomments.rs
Normal 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)
|
||||
}
|
||||
}
|
62
src/commands/music_database/readpicture.rs
Normal file
62
src/commands/music_database/readpicture.rs
Normal 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,
|
||||
}))
|
||||
}
|
||||
}
|
35
src/commands/music_database/rescan.rs
Normal file
35
src/commands/music_database/rescan.rs
Normal 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 })
|
||||
}
|
||||
}
|
51
src/commands/music_database/search.rs
Normal file
51
src/commands/music_database/search.rs
Normal 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!()
|
||||
}
|
||||
}
|
61
src/commands/music_database/searchadd.rs
Normal file
61
src/commands/music_database/searchadd.rs
Normal 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!()
|
||||
}
|
||||
}
|
69
src/commands/music_database/searchaddpl.rs
Normal file
69
src/commands/music_database/searchaddpl.rs
Normal 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!()
|
||||
}
|
||||
}
|
52
src/commands/music_database/searchcount.rs
Normal file
52
src/commands/music_database/searchcount.rs
Normal 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 })
|
||||
}
|
||||
}
|
35
src/commands/music_database/update.rs
Normal file
35
src/commands/music_database/update.rs
Normal 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 })
|
||||
}
|
||||
}
|
@ -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),
|
||||
|
Loading…
Reference in New Issue
Block a user