Lines
64.29 %
Functions
37.5 %
use std::collections::HashMap;
use std::str::FromStr;
use serde::{Deserialize, Serialize};
use crate::{
commands::{CommandResponse, ResponseParserError, empty_command_request},
response_tokenizer::{
ResponseAttributes, get_and_parse_optional_property, get_and_parse_property,
get_optional_property, get_property,
},
types::{Audio, BoolOrOneshot, SongId, SongPosition},
};
empty_command_request!(Status, "status");
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum StatusResponseState {
Play,
Stop,
Pause,
}
impl FromStr for StatusResponseState {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"play" => Ok(StatusResponseState::Play),
"stop" => Ok(StatusResponseState::Stop),
"pause" => Ok(StatusResponseState::Pause),
_ => Err(()),
pub struct StatusResponse {
pub partition: String,
// Note: the Option<>::None here is serialized as -1
pub volume: Option<u8>,
pub repeat: bool,
pub random: bool,
pub single: BoolOrOneshot,
pub consume: BoolOrOneshot,
pub playlist: u32,
pub playlist_length: u64,
pub state: StatusResponseState,
pub song: Option<SongPosition>,
pub song_id: Option<SongId>,
pub next_song: Option<SongPosition>,
pub next_song_id: Option<SongId>,
pub time: Option<(u64, u64)>,
pub elapsed: Option<f64>,
pub duration: Option<f64>,
pub bitrate: Option<u32>,
pub xfade: Option<u32>,
pub mixrampdb: Option<f64>,
pub mixrampdelay: Option<f64>,
pub audio: Option<Audio>,
pub updating_db: Option<u64>,
pub error: Option<String>,
pub last_loaded_playlist: Option<String>,
impl StatusResponse {
pub fn new(
partition: String,
volume: Option<u8>,
repeat: bool,
random: bool,
single: BoolOrOneshot,
consume: BoolOrOneshot,
playlist: u32,
playlist_length: u64,
state: StatusResponseState,
song: Option<SongPosition>,
song_id: Option<SongId>,
next_song: Option<SongPosition>,
next_song_id: Option<SongId>,
time: Option<(u64, u64)>,
elapsed: Option<f64>,
duration: Option<f64>,
bitrate: Option<u32>,
xfade: Option<u32>,
mixrampdb: Option<f64>,
mixrampdelay: Option<f64>,
audio: Option<Audio>,
updating_db: Option<u64>,
error: Option<String>,
last_loaded_playlist: Option<String>,
) -> Self {
Self {
partition,
volume,
repeat,
random,
single,
consume,
playlist,
playlist_length,
state,
song,
song_id,
next_song,
next_song_id,
time,
elapsed,
duration,
bitrate,
xfade,
mixrampdb,
mixrampdelay,
audio,
updating_db,
error,
last_loaded_playlist,
impl CommandResponse for StatusResponse {
type Request = StatusRequest;
fn parse(parts: ResponseAttributes<'_>) -> Result<Self, ResponseParserError> {
let parts: HashMap<_, _> = parts.into_map()?;
let partition = get_property!(parts, "partition", Text).to_string();
let volume = match get_property!(parts, "volume", Text) {
"-1" => None,
volume => Some(volume.parse().map_err(|_| {
ResponseParserError::InvalidProperty("volume".to_string(), volume.to_string())
})?),
let repeat = match get_property!(parts, "repeat", Text) {
"0" => Ok(false),
"1" => Ok(true),
repeat => Err(ResponseParserError::InvalidProperty(
"repeat".to_string(),
repeat.to_string(),
)),
}?;
let random = match get_property!(parts, "random", Text) {
random => Err(ResponseParserError::InvalidProperty(
"random".to_string(),
random.to_string(),
let single = get_and_parse_property!(parts, "single", Text);
let consume = get_and_parse_property!(parts, "consume", Text);
let playlist: u32 = get_and_parse_property!(parts, "playlist", Text);
let playlist_length: u64 = get_and_parse_property!(parts, "playlistlength", Text);
let state: StatusResponseState = get_and_parse_property!(parts, "state", Text);
let song: Option<SongPosition> = get_and_parse_optional_property!(parts, "song", Text);
let song_id: Option<SongId> = get_and_parse_optional_property!(parts, "songid", Text);
let next_song: Option<SongPosition> =
get_and_parse_optional_property!(parts, "nextsong", Text);
let next_song_id: Option<SongId> =
get_and_parse_optional_property!(parts, "nextsongid", Text);
let time = match get_optional_property!(parts, "time", Text) {
Some(time) => {
let mut parts = time.split(':');
let elapsed = parts
.next()
.ok_or(ResponseParserError::SyntaxError(0, time.to_string()))?
.parse()
.map_err(|_| {
ResponseParserError::InvalidProperty("time".to_string(), time.to_string())
})?;
let duration = parts
Some((elapsed, duration))
None => None,
let elapsed = get_and_parse_optional_property!(parts, "elapsed", Text);
let duration = get_and_parse_optional_property!(parts, "duration", Text);
let bitrate = get_and_parse_optional_property!(parts, "bitrate", Text);
let xfade = get_and_parse_optional_property!(parts, "xfade", Text);
let mixrampdb = get_and_parse_optional_property!(parts, "mixrampdb", Text);
let mixrampdelay = get_and_parse_optional_property!(parts, "mixrampdelay", Text);
let audio = get_and_parse_optional_property!(parts, "audio", Text);
let updating_db = get_and_parse_optional_property!(parts, "updating_db", Text);
let error = get_and_parse_optional_property!(parts, "error", Text);
let last_loaded_playlist =
get_and_parse_optional_property!(parts, "last_loaded_playlist", Text);
Ok(StatusResponse {
})
fn serialize(&self) -> Vec<u8> {
unimplemented!("response serialization is not yet implemented for StatusResponse")
#[cfg(test)]
mod tests {
use super::*;
use indoc::indoc;
use pretty_assertions::assert_eq;
#[test]
fn test_parse_status_response() {
let contents = indoc! { r#"
volume: 66
repeat: 1
random: 1
single: 0
consume: 0
partition: default
playlist: 2
playlistlength: 78
mixrampdb: 0
state: play
song: 0
songid: 1
time: 225:263
elapsed: 225.376
bitrate: 127
duration: 262.525
audio: 44100:f:2
nextsong: 44
nextsongid: 45
OK
"# };
assert_eq!(
StatusResponse::parse_raw(contents.as_bytes()),
partition: "default".into(),
volume: Some(66),
repeat: true,
random: true,
single: BoolOrOneshot::False,
consume: BoolOrOneshot::False,
playlist: 2,
playlist_length: 78,
state: StatusResponseState::Play,
song: Some(0),
song_id: Some(1),
next_song: Some(44),
next_song_id: Some(45),
time: Some((225, 263)),
elapsed: Some(225.376),
duration: Some(262.525),
bitrate: Some(127),
xfade: None,
mixrampdb: Some(0.0),
mixrampdelay: None,
audio: Some(Audio {
sample_rate: 44100,
bits: 16,
channels: 2,
}),
updating_db: None,
error: None,
last_loaded_playlist: None,
);