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 connection_settings;
|
||||||
mod controlling_playback;
|
mod controlling_playback;
|
||||||
mod mounts_and_neighbors;
|
mod mounts_and_neighbors;
|
||||||
|
mod music_database;
|
||||||
mod partition_commands;
|
mod partition_commands;
|
||||||
mod playback_options;
|
mod playback_options;
|
||||||
mod querying_mpd_status;
|
mod querying_mpd_status;
|
||||||
@ -22,6 +23,7 @@ pub use client_to_client::*;
|
|||||||
pub use connection_settings::*;
|
pub use connection_settings::*;
|
||||||
pub use controlling_playback::*;
|
pub use controlling_playback::*;
|
||||||
pub use mounts_and_neighbors::*;
|
pub use mounts_and_neighbors::*;
|
||||||
|
pub use music_database::*;
|
||||||
pub use partition_commands::*;
|
pub use partition_commands::*;
|
||||||
pub use playback_options::*;
|
pub use playback_options::*;
|
||||||
pub use querying_mpd_status::*;
|
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<WindowRange>,
|
||||||
Option<SongPosition>,
|
Option<SongPosition>,
|
||||||
),
|
),
|
||||||
List(Tag, Filter, Option<GroupType>),
|
List(TagName, Filter, Option<GroupType>),
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
ListAll(Option<Uri>),
|
ListAll(Option<Uri>),
|
||||||
#[deprecated]
|
#[deprecated]
|
||||||
ListAllInfo(Option<Uri>),
|
ListAllInfo(Option<Uri>),
|
||||||
ListFiles(Uri),
|
ListFiles(Option<Uri>),
|
||||||
LsInfo(Option<Uri>),
|
LsInfo(Option<Uri>),
|
||||||
ReadComments(Uri),
|
ReadComments(Uri),
|
||||||
ReadPicture(Uri, Offset),
|
ReadPicture(Uri, Offset),
|
||||||
@ -120,6 +120,7 @@ pub enum Request {
|
|||||||
Option<SongPosition>,
|
Option<SongPosition>,
|
||||||
),
|
),
|
||||||
SearchAddPl(
|
SearchAddPl(
|
||||||
|
PlaylistName,
|
||||||
Filter,
|
Filter,
|
||||||
Option<Sort>,
|
Option<Sort>,
|
||||||
Option<WindowRange>,
|
Option<WindowRange>,
|
||||||
@ -294,6 +295,28 @@ pub enum GroupType {
|
|||||||
Any,
|
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 {
|
// trait RequestCommand {
|
||||||
// // The command name used within the protocol
|
// // The command name used within the protocol
|
||||||
// const COMMAND: &'static str;
|
// const COMMAND: &'static str;
|
||||||
@ -426,6 +449,24 @@ impl Request {
|
|||||||
Save::COMMAND => Save::parse_request(parts),
|
Save::COMMAND => Save::parse_request(parts),
|
||||||
|
|
||||||
/* music database */
|
/* 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 */
|
/* mounts and neighbors */
|
||||||
Mount::COMMAND => Mount::parse_request(parts),
|
Mount::COMMAND => Mount::parse_request(parts),
|
||||||
|
Loading…
Reference in New Issue
Block a user