2020-06-20 22:21:21 +02:00
use helpers ::* ;
use serde ::Deserialize ;
use std ::time ::Duration ;
2020-06-20 22:50:33 +02:00
#[ derive(Deserialize, Clone, Debug, Default, PartialEq) ]
2020-06-20 23:29:09 +02:00
#[ serde(default) ]
2020-06-20 22:21:21 +02:00
pub struct Track {
2020-06-20 23:27:00 +02:00
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 ,
// TODO: use proper time here
#[ serde(rename = " last-modified " ) ]
pub last_modified : Option < String > ,
#[ serde(rename = " originaldate " ) ]
pub original_date : Option < u16 > ,
// probably not needed. it’s just the duration but as an int
// pub time: u16,
pub format : Option < String > ,
#[ serde(deserialize_with = " de_time_float " ) ]
pub duration : 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 > ,
}
/// The position of an item in a list with an optional total length.
/// Can be used for e.g. track number,
/// where `Position { 3, 12 }` represents track 3 of a CD with 12 tracks,
/// or disc numbers in a multi-disc set.
#[ derive(Deserialize, Clone, Debug, Default, PartialEq) ]
pub struct Position {
pub item_position : u16 ,
pub total_items : Option < u16 > ,
2020-06-20 22:21:21 +02:00
}
/// Deserialization helpers to handle the quirks of mpd’s output.
mod helpers {
2020-06-20 23:27:00 +02:00
use super ::* ;
use crate ::SEPARATOR ;
use serde ::{ de , Deserialize } ;
use std ::{ str ::FromStr , time ::Duration } ;
2020-06-20 22:21:21 +02:00
2020-06-20 23:27:00 +02:00
/// Deserialize time from an integer that represents the seconds.
/// mpd uses int for the database stats (e.g. total time played).
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 )
}
2020-06-20 22:21:21 +02:00
2020-06-20 23:27:00 +02:00
/// 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 < Duration , D ::Error >
where D : de ::Deserializer < ' de > {
f64 ::deserialize ( deserializer ) . map ( Duration ::from_secs_f64 )
}
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 ( ) )
}
2020-06-20 22:21:21 +02:00
2020-06-20 23:27:00 +02:00
/// mpd uses bints (0 or 1) to represent booleans,
/// so we need a special parser for those.
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 " ) ) ,
2020-06-20 22:21:21 +02:00
}
2020-06-20 23:27:00 +02:00
}
2020-06-20 22:21:21 +02:00
2020-06-20 23:27:00 +02:00
/// Deserialize a position with an optional total length.
/// The input string here is either a number or two numbers separated by `SEPARATOR`.
pub fn de_position < ' de , D > ( deserializer : D ) -> Result < Option < Position > , D ::Error >
where D : de ::Deserializer < ' de > {
2020-06-20 23:53:32 +02:00
let s = String ::deserialize ( deserializer ) ? ;
2020-06-21 00:00:24 +02:00
let mut ints = s . split ( SEPARATOR ) . filter_map ( | s | u16 ::from_str ( s ) . ok ( ) ) ;
if let Some ( n ) = ints . next ( ) {
2020-06-20 23:53:32 +02:00
return Ok ( Some ( Position {
item_position : n ,
2020-06-21 00:00:24 +02:00
total_items : ints . next ( ) ,
2020-06-20 23:53:32 +02:00
} ) ) ;
2020-06-20 22:21:21 +02:00
}
2020-06-20 23:27:00 +02:00
Ok ( None )
}
2020-06-20 22:21:21 +02:00
}