mpd 0.0.3

A client library for MPD (music player daemon), like libmpdclient but in Rust

use std::time::duration::Duration;
use std::old_io::{standard_error, IoErrorKind, Stream};
use std::iter::FromIterator;
use std::collections::BTreeMap;
use time::{Timespec, strptime};
use rustc_serialize::{Encoder, Encodable};

use error::MpdResult;
use client::{MpdPair, MpdClient};
use utils::{FieldCutIter, ForceEncodable};

#[derive(Debug, Copy, RustcEncodable)]
pub struct MpdQueuePlace {
    pub id: usize,
    pub pos: usize,
    pub prio: u8
}

#[derive(Debug, RustcEncodable)]
pub struct MpdSong {
    pub file: String,
    pub last_mod: Timespec,
    pub duration: Duration,
    pub place: Option<MpdQueuePlace>,
    pub range: (Duration, Option<Duration>),
    pub tags: BTreeMap<String, String>,
}

impl MpdSong {
    pub fn seek<S: Stream>(&self, cli: &mut MpdClient<S>, time: Duration) -> MpdResult<()> {
        cli.exec_arg2("seekid", self.place.unwrap().id, time.num_milliseconds() as f32 / 1000f32)
    }
}

impl FromIterator<MpdResult<MpdPair>> for MpdResult<MpdSong> {
    fn from_iter<T: Iterator<Item=MpdResult<MpdPair>>>(iterator: T) -> MpdResult<MpdSong> {
        let mut song = MpdSong {
            file: "".to_string(),
            last_mod: Timespec::new(0, 0),
            duration: Duration::zero(),
            place: None,
            range: (Duration::zero(), None),
            tags: BTreeMap::new(),
        };
        let mut place = MpdQueuePlace {
            id: 0,
            pos: 0,
            prio: 0
        };

        let mut iter = iterator;

        for field in iter {
            let MpdPair(key, value) = try!(field);
            match &*key {
                "file" => song.file = value,
                "Last-Modified" => song.last_mod = try!(strptime(&*value, "%Y-%m-%dT%H:%M:%S%Z").map_err(|_| standard_error(IoErrorKind::InvalidInput))).to_timespec(),
                "Time" => song.duration = Duration::seconds(value.parse().unwrap_or(0)),
                "Range" => {
                    let mut splits = value.split('-').flat_map(|v| v.parse::<i64>().into_iter());
                    match (splits.next(), splits.next()) {
                        (Some(s), Some(e)) => song.range = (Duration::seconds(s), Some(Duration::seconds(e))),
                        (None, Some(e)) => song.range = (Duration::zero(), Some(Duration::seconds(e))),
                        (Some(s), None) => song.range = (Duration::seconds(s), None),
                        (None, None) => (),
                    }
                },
                "Id" => place.id = value.parse().unwrap_or(0),
                "Pos" => place.pos = value.parse().unwrap_or(0),
                "Prio" => place.prio = value.parse().unwrap_or(0),
                _ => { song.tags.insert(key, value); }
            }
        }

        if place.id > 0 {
            song.place = Some(place);
        }

        Ok(song)
    }
}

mpd_collectable!(MpdSong, "file");