use chrono::prelude::*;
use helpers::*;
use serde::Deserialize;
use std::{fmt, time::Duration};
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
#[serde(default)]
pub struct Track {
pub file: String,
#[serde(rename = "artistsort")]
pub artist_sort: Option<String>,
#[serde(rename = "albumartist")]
pub album_artist: Option<String>,
#[serde(rename = "albumsort")]
pub album_sort: Option<String>,
#[serde(rename = "albumartistsort")]
pub album_artist_sort: Option<String>,
#[serde(deserialize_with = "de_string_or_vec")]
#[serde(rename = "performer")]
pub performers: Vec<String>,
pub genre: Option<String>,
pub title: Option<String>,
#[serde(deserialize_with = "de_position")]
pub track: Option<Position>,
pub album: Option<String>,
pub artist: Option<String>,
pub pos: u32,
pub id: u32,
#[serde(rename = "last-modified")]
pub last_modified: Option<DateTime<FixedOffset>>,
#[serde(rename = "originaldate")]
pub original_date: Option<u16>,
pub format: Option<String>,
#[serde(deserialize_with = "de_time_float")]
pub duration: Option<Duration>,
pub label: Option<String>,
pub date: Option<u16>,
#[serde(deserialize_with = "de_position")]
pub disc: Option<Position>,
pub musicbraiz_trackid: Option<String>,
pub musicbrainz_albumid: Option<String>,
pub musicbrainz_albumartistid: Option<String>,
pub musicbrainz_artistid: Option<String>,
pub musicbrainz_releasetrackid: Option<String>,
pub musicbrainz_trackid: Option<String>,
pub composer: Option<String>,
}
#[derive(Deserialize, PartialEq, Debug)]
pub struct UnitResponse {}
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
pub struct Position {
pub item_position: u16,
pub total_items: Option<u16>,
}
impl fmt::Display for Position {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.total_items {
Some(n) => write!(f, "{}/{}", self.item_position, n),
None => write!(f, "{}", self.item_position),
}
}
}
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
#[serde(default)]
pub struct Status {
pub volume: Option<u8>,
#[serde(deserialize_with = "de_bint")]
pub repeat: bool,
#[serde(deserialize_with = "de_bint")]
pub random: bool,
pub single: u8,
#[serde(deserialize_with = "de_bint")]
pub consume: bool,
pub playlist: u32,
pub playlistlength: u32,
#[serde(deserialize_with = "de_state")]
pub state: State,
pub song: Option<u32>,
pub songid: Option<u32>,
pub nextsong: Option<u32>,
pub nextsongid: Option<u32>,
#[serde(deserialize_with = "de_time_float")]
pub elapsed: Option<Duration>,
#[serde(deserialize_with = "de_time_float")]
pub duration: Option<Duration>,
pub bitrate: Option<u16>,
pub xfade: Option<u8>,
pub mixrampdb: f32,
pub mixrampdelay: Option<u8>,
pub audio: Option<String>,
pub updating_db: Option<u32>,
pub error: Option<String>,
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub struct File {
#[serde(alias = "directory", rename = "file")]
pub name: String,
#[serde(rename = "last-modified")]
pub last_modified: DateTime<FixedOffset>,
#[serde(default)]
pub size: usize,
}
impl File {
pub fn is_directory(&self) -> bool {
self.size == 0
}
}
#[derive(Deserialize, Clone, Debug, PartialEq)]
pub enum State {
Stop,
Play,
Pause,
}
impl Default for State {
fn default() -> Self {
Self::Stop
}
}
#[derive(Deserialize, Clone, Debug, Default, PartialEq)]
#[serde(default)]
pub struct Stats {
pub artists: u32,
pub albums: u32,
pub songs: u32,
#[serde(deserialize_with = "de_time_int")]
pub uptime: Duration,
#[serde(deserialize_with = "de_time_int")]
pub db_playtime: Duration,
pub db_update: u32,
#[serde(deserialize_with = "de_time_int")]
pub playtime: Duration,
}
mod helpers {
use super::*;
use crate::SEPARATOR;
use serde::{de, Deserialize};
use std::{str::FromStr, time::Duration};
pub fn de_time_int<'de, D>(deserializer: D) -> Result<Duration, D::Error>
where
D: de::Deserializer<'de>,
{
u64::deserialize(deserializer).map(Duration::from_secs)
}
pub fn de_time_float<'de, D>(deserializer: D) -> Result<Option<Duration>, D::Error>
where
D: de::Deserializer<'de>,
{
f64::deserialize(deserializer).map(Duration::from_secs_f64).map(Some)
}
pub fn de_state<'de, D>(deserializer: D) -> Result<State, D::Error>
where
D: de::Deserializer<'de>,
{
match String::deserialize(deserializer)?.as_ref() {
"play" => Ok(State::Play),
"pause" => Ok(State::Pause),
"stop" => Ok(State::Stop),
s => Err(de::Error::invalid_value(
de::Unexpected::Str(s),
&"expected one of play, pause, or stop",
)),
}
}
pub fn de_string_or_vec<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
where
D: de::Deserializer<'de>,
{
String::deserialize(deserializer).map(|s| s.split(SEPARATOR).map(std::string::String::from).collect())
}
pub fn de_bint<'de, D>(deserializer: D) -> Result<bool, D::Error>
where
D: de::Deserializer<'de>,
{
match u8::deserialize(deserializer)? {
0 => Ok(false),
1 => Ok(true),
n => Err(de::Error::invalid_value(de::Unexpected::Unsigned(n as u64), &"zero or one")),
}
}
pub fn de_position<'de, D>(deserializer: D) -> Result<Option<Position>, D::Error>
where
D: de::Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
let mut ints = s.split(SEPARATOR).filter_map(|s| u16::from_str(s).ok());
if let Some(n) = ints.next() {
return Ok(Some(Position {
item_position: n,
total_items: ints.next(),
}));
}
Ok(None)
}
}