use crate::convert::FromIter;
use crate::error::{Error, ParseError};
use std::fmt;
use std::str::FromStr;
use std::time::Duration;
#[derive(Debug, Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Default)]
pub struct Id(pub u32);
impl fmt::Display for Id {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.fmt(f)
}
}
#[cfg(feature = "serde")]
impl<'de> serde::Deserialize<'de> for Id {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where D: serde::Deserializer<'de> {
Ok(Id(u32::deserialize(deserializer)?))
}
}
#[cfg(feature = "serde")]
impl serde::Serialize for Id {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: serde::Serializer {
serializer.serialize_u32(self.0)
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct QueuePlace {
pub id: Id,
pub pos: u32,
pub prio: u8,
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Range(pub Duration, pub Option<Duration>);
impl Default for Range {
fn default() -> Range {
Range(Duration::from_secs(0), None)
}
}
impl fmt::Display for Range {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
self.0.as_secs().fmt(f)?;
f.write_str(":")?;
if let Some(v) = self.1 {
v.as_secs().fmt(f)?;
}
Ok(())
}
}
impl FromStr for Range {
type Err = ParseError;
fn from_str(s: &str) -> Result<Range, ParseError> {
let mut splits = s.split('-').flat_map(|v| v.parse().into_iter());
match (splits.next(), splits.next()) {
(Some(s), Some(e)) => Ok(Range(Duration::from_secs(s), Some(Duration::from_secs(e)))),
(None, Some(e)) => Ok(Range(Duration::from_secs(0), Some(Duration::from_secs(e)))),
(Some(s), None) => Ok(Range(Duration::from_secs(s), None)),
(None, None) => Ok(Range(Duration::from_secs(0), None)),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Song {
pub file: String,
pub name: Option<String>,
pub title: Option<String>,
pub last_mod: Option<String>,
pub artist: Option<String>,
pub duration: Option<Duration>,
pub place: Option<QueuePlace>,
pub range: Option<Range>,
pub tags: Vec<(String, String)>,
}
impl FromIter for Song {
fn from_iter<I: Iterator<Item = Result<(String, String), Error>>>(iter: I) -> Result<Song, Error> {
let mut result = Song::default();
for res in iter {
let line = res?;
match &*line.0 {
"file" => result.file = line.1.to_owned(),
"Title" => result.title = Some(line.1.to_owned()),
"Last-Modified" => result.last_mod = Some(line.1.to_owned()),
"Artist" => result.artist = Some(line.1.to_owned()),
"Name" => result.name = Some(line.1.to_owned()),
"Time" => result.duration = Some(Duration::from_secs(line.1.parse()?)),
"Range" => result.range = Some(line.1.parse()?),
"Id" => match result.place {
None => result.place = Some(QueuePlace { id: Id(line.1.parse()?), pos: 0, prio: 0 }),
Some(ref mut place) => place.id = Id(line.1.parse()?),
},
"Pos" => match result.place {
None => result.place = Some(QueuePlace { pos: line.1.parse()?, id: Id(0), prio: 0 }),
Some(ref mut place) => place.pos = line.1.parse()?,
},
"Prio" => match result.place {
None => result.place = Some(QueuePlace { prio: line.1.parse()?, id: Id(0), pos: 0 }),
Some(ref mut place) => place.prio = line.1.parse()?,
},
_ => {
result.tags.push((line.0, line.1));
}
}
}
Ok(result)
}
}