2020-06-13 09:18:47 +02:00
use serde ::de ;
use serde ::de ::value ::MapDeserializer ;
use serde ::forward_to_deserialize_any ;
use serde ::Deserialize ;
2020-06-20 17:14:07 +02:00
use std ::fmt ;
use std ::time ::Duration ;
2020-06-13 09:18:47 +02:00
mod error ;
use error ::MpdResult ;
2020-06-13 09:44:26 +02:00
struct MPDeserializer < ' de , Iter : Iterator < Item = ( & ' de str , & ' de str ) > > {
2020-06-13 09:18:47 +02:00
inner : MapDeserializer < ' de , Iter , error ::Error > ,
}
2020-06-20 17:14:07 +02:00
/// Deserialize time from an integer that represents the seconds.
/// mpd uses int for the database stats (e.g. total time played).
fn de_time_int < ' de , D > ( deserializer : D ) -> Result < Duration , D ::Error >
where
D : de ::Deserializer < ' de > ,
{
u64 ::deserialize ( deserializer ) . map ( Duration ::from_secs )
}
/// Deserialize time from a float that represents the seconds.
/// mpd uses floats for the current status (e.g. time elapsed in song).
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 )
}
/// mpd uses bints (0 or 1) to represent booleans,
/// so we need a special parser for those.
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 " ,
) ) ,
}
}
#[ derive(Deserialize, Clone, Debug) ]
pub struct Track {
pub file : String ,
pub artist_sort : Option < String > ,
pub album_artist : Option < String > ,
pub album_sort : Option < String > ,
pub album_artist_sort : Option < String > ,
#[ serde(deserialize_with = " string_or_vec " ) ]
#[ serde(default) ]
#[ serde(rename = " performer " ) ]
pub performers : Vec < String > ,
pub genre : Option < String > ,
pub title : Option < String > ,
pub track : Option < u32 > ,
pub album : Option < String > ,
pub artist : Option < String > ,
pub pos : Option < u32 > ,
pub id : Option < u32 > ,
// TODO: use proper time here
pub last_modified : Option < String > ,
pub original_date : Option < String > ,
pub time : Option < String > ,
pub format : Option < String > ,
#[ serde(deserialize_with = " de_time_float " ) ]
pub duration : Option < Duration > ,
pub label : Option < String > ,
pub date : Option < String > ,
pub disc : Option < u32 > ,
pub musicbraiz_trackid : Option < String > ,
pub musicbrainz_albumid : Option < String > ,
pub musicbrainz_albumartistid : Option < String > ,
pub musicbrainz_artistid : Option < String > ,
pub musicbraiz_releasetrackid : Option < String > ,
pub composer : Option < String > ,
}
// some unprintable character
const SEPARATOR : char = '\x02' ;
2020-06-13 09:44:26 +02:00
impl < ' de , Iter : Iterator < Item = ( & ' de str , & ' de str ) > > de ::Deserializer < ' de >
2020-06-13 09:18:47 +02:00
for MPDeserializer < ' de , Iter >
{
type Error = error ::Error ;
fn deserialize_any < V > ( self , visitor : V ) -> MpdResult < V ::Value >
where
V : de ::Visitor < ' de > ,
{
self . deserialize_map ( visitor )
}
fn deserialize_map < V > ( self , visitor : V ) -> MpdResult < V ::Value >
where
V : de ::Visitor < ' de > ,
{
visitor . visit_map ( self . inner )
}
forward_to_deserialize_any! {
bool char str string bytes byte_buf unit unit_struct seq
tuple tuple_struct struct identifier ignored_any
i8 i16 i32 i64 i128 u8 u16 u32 u64 u128 f32 f64 option newtype_struct enum
}
}
2020-06-20 17:14:07 +02:00
fn string_or_vec < ' de , D > ( deserializer : D ) -> Result < Vec < String > , D ::Error >
where
D : de ::Deserializer < ' de > ,
{
struct StringOrVec ( ) ;
impl < ' de > de ::Visitor < ' de > for StringOrVec {
type Value = Vec < String > ;
fn expecting ( & self , formatter : & mut fmt ::Formatter ) -> fmt ::Result {
formatter . write_str ( " string or list of strings " )
}
fn visit_str < E : de ::Error > ( self , value : & str ) -> Result < Self ::Value , E > {
Ok ( value
. split ( SEPARATOR )
. map ( std ::string ::String ::from )
. collect ( ) )
}
}
deserializer . deserialize_any ( StringOrVec ( ) )
2020-06-13 09:18:47 +02:00
}
2020-06-20 17:14:07 +02:00
fn deserialize_response < ' a , I : Iterator < Item = & ' a str > > ( input : I ) -> Result < Track , error ::Error > {
let mut map : std ::collections ::HashMap < String , String > = std ::collections ::HashMap ::new ( ) ;
for line in input {
if let Some ( _ ) = line . strip_prefix ( " OK " ) {
break ;
} else if let Some ( message ) = line . strip_prefix ( " ACK " ) {
return Err ( error ::Error {
message : message . trim ( ) . to_string ( ) ,
} ) ;
}
let mut fields = line . splitn ( 2 , " : " ) ;
match ( fields . next ( ) , fields . next ( ) ) {
( Some ( k ) , Some ( v ) ) = > {
if let Some ( existing ) = map . get_mut ( k ) {
existing . push ( SEPARATOR ) ;
existing . push_str ( v ) ;
} else {
map . insert ( k . to_string ( ) , v . to_string ( ) ) ;
}
2020-06-13 09:18:47 +02:00
}
2020-06-20 17:14:07 +02:00
_ = > panic! ( " invalid response line: {:?} " , line ) ,
2020-06-13 09:18:47 +02:00
}
}
2020-06-20 17:14:07 +02:00
return Ok ( envy ::from_iter ( map ) . unwrap ( ) ) ;
// let deser = MPDeserializer {
// inner: MapDeserializer::new(map.into_iter()),
// };
// Song::deserialize(deser)
}
fn main ( ) {
let input_str = " file: Youtube Rip/VVVVVV Medley.flac
Last - Modified : 2018 - 03 - 07 T13 :18 :01 Z
Album : VVVVVV OST / PPPPPP
Artist : FamilyJules7x
Composer : Magnus Pålsson
Date : 2014
Genre : Video Game Music
Title : VVVVVV Medley
Time : 433
duration : 432.727
Pos : 1548
Id : 1549 " ;
let s = deserialize_response ( input_str . lines ( ) ) ;
dbg! ( s ) ;
2020-06-13 09:18:47 +02:00
}