diff --git a/src/lib.rs b/src/lib.rs index bc0728d..a50e2cb 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,7 +3,7 @@ use std::collections::HashMap; mod error; use error::{Error, MpdResult}; mod structs; -pub use structs::{Position, Track}; +pub use structs::{Position, Status, Track}; /// some unprintable character to separate repeated keys const SEPARATOR: char = '\x02'; @@ -38,6 +38,7 @@ pub fn deserialize_response<'a, I: Iterator, T: de::DeserializeO mod tests { use super::*; use chrono::DateTime; + use std::time::Duration; #[test] fn track_de_test() { @@ -69,7 +70,7 @@ OK"; item_position: 1, total_items: None, }), - duration: std::time::Duration::from_secs_f64(512.380), + duration: Some(Duration::from_secs_f64(512.380)), pos: 1367, id: 1368, ..Track::default() @@ -129,7 +130,7 @@ OK"#; item_position: 6, total_items: Some(6), }), - duration: std::time::Duration::from_secs_f64(682.840), + duration: Some(Duration::from_secs_f64(682.840)), pos: 3439, id: 3440, performers: vec!["Royal Philharmonic Orchestra".to_string(), "Jane Glover".to_string()], @@ -142,4 +143,47 @@ OK"#; } ); } + #[test] + fn de_status_test() { + let input_str = "volume: 74 +repeat: 0 +random: 1 +single: 0 +consume: 0 +playlist: 6 +playlistlength: 5364 +mixrampdb: 0.000000 +state: play +song: 3833 +songid: 3834 +time: 70:164 +elapsed: 69.642 +bitrate: 702 +duration: 163.760 +audio: 44100:16:2 +nextsong: 4036 +nextsongid: 4037 +OK"; + let s: Status = deserialize_response(input_str.lines()).unwrap(); + assert_eq!( + s, + Status { + volume: Some(74), + random: true, + playlist: 6, + playlistlength: 5364, + mixrampdb: 0.0, + state: String::from("play"), + song: Some(3833), + songid: Some(3834), + elapsed: Some(Duration::from_secs_f64(69.642)), + bitrate: Some(702), + duration: Some(Duration::from_secs_f64(163.760)), + audio: Some(String::from("44100:16:2")), + nextsong: Some(4036), + nextsongid: Some(4037), + ..Status::default() + } + ); + } } diff --git a/src/structs.rs b/src/structs.rs index 090e64a..1eb5306 100644 --- a/src/structs.rs +++ b/src/structs.rs @@ -3,6 +3,7 @@ use helpers::*; use serde::Deserialize; use std::time::Duration; +/// All information about a track. This is returned by the `currentsong` or `queue` commands. #[derive(Deserialize, Clone, Debug, Default, PartialEq)] #[serde(default)] pub struct Track { @@ -30,11 +31,9 @@ pub struct Track { pub last_modified: Option>, #[serde(rename = "originaldate")] pub original_date: Option, - // probably not needed. it’s just the duration but as an int - // pub time: u16, pub format: Option, #[serde(deserialize_with = "de_time_float")] - pub duration: Duration, + pub duration: Option, pub label: Option, pub date: Option, #[serde(deserialize_with = "de_position")] @@ -58,6 +57,52 @@ pub struct Position { pub total_items: Option, } +/// Current status as returned by `status`. +/// +/// Regarding optional `volume`: +/// The volume is None if mpd is running without local audio output, +/// i.e. on a server with no pulse or alsa output configured. +/// Output via httpd might be used instead +/// but will result in empty `volume` and `audio` fields. +#[derive(Deserialize, Clone, Debug, Default, PartialEq)] +#[serde(default)] +pub struct Status { + pub volume: Option, + #[serde(deserialize_with = "de_bint")] + pub repeat: bool, + #[serde(deserialize_with = "de_bint")] + pub random: bool, + // TODO: make enum + pub single: u8, + #[serde(deserialize_with = "de_bint")] + pub consume: bool, + // an empty playlist still has an ID + pub playlist: u32, + // mpd returns 0 if there is no current playlist + pub playlistlength: u32, + // play, stop, pause. TODO: make an enum + pub state: String, + pub song: Option, + pub songid: Option, + pub nextsong: Option, + pub nextsongid: Option, + #[serde(deserialize_with = "de_time_float")] + pub elapsed: Option, + #[serde(deserialize_with = "de_time_float")] + pub duration: Option, + pub bitrate: Option, + pub xfade: Option, + // 0 if unset + pub mixrampdb: f32, + pub mixrampdelay: Option, + // “audio: The format emitted by the decoder plugin during playback, format: samplerate:bits:channels. + // See Global Audio Format for a detailed explanation.” + // TODO: make struct + pub audio: Option, + pub updating_db: Option, + pub error: Option, +} + /// Deserialization helpers to handle the quirks of mpd’s output. mod helpers { use super::*; @@ -74,9 +119,9 @@ mod helpers { /// Deserialize time from a float that represents the seconds. /// mpd uses floats for the current status (e.g. time elapsed in song). - pub fn de_time_float<'de, D>(deserializer: D) -> Result + pub fn de_time_float<'de, D>(deserializer: D) -> Result, D::Error> where D: de::Deserializer<'de> { - f64::deserialize(deserializer).map(Duration::from_secs_f64) + f64::deserialize(deserializer).map(Duration::from_secs_f64).map(Some) } pub fn de_string_or_vec<'de, D>(deserializer: D) -> Result, D::Error>