<commit_before>use std::io::{self, Buffer, Reader, Writer, IoResult, BufferedStream, standard_error};
use std::mem;
use std::collections::BTreeMap;
use std::rand::{thread_rng, Rng};
use std::u16;
use std::num::{Int, FromPrimitive, ToPrimitive};
use std::slice::SliceConcatExt;
use url::Url;

use nonce::Nonce;
use message::{WSMessage, WSHeader, WS_MASK, WS_LEN, WS_LEN16, WS_LEN64, WS_OPTERM};
use stream::NetworkStream;

pub struct WebSocket<S = NetworkStream> {
    stream: Option<BufferedStream<S>>,
    pub url: Url,
    hostname: String,
    use_ssl: bool,
    version: uint,
    extensions: Option<Vec<String>>,
    protocols: Option<Vec<String>>
}

impl WebSocket {
    pub fn with_options(url: Url, version: uint, protocols: Option<&[&str]>, extensions: Option<&[&str]>) -> WebSocket {
        let use_ssl = url.scheme[] == "wss";

        let port = match url.port() {
            Some(p) => p,
            None if use_ssl => 443,
            _ => 80
        };

        WebSocket {
            stream: None,
            hostname: format!("{}:{}", url.serialize_host().unwrap(), port),
            url: url,
            use_ssl: use_ssl,
            version: version,
            extensions: extensions.map(|v| v.iter().map(|v| v.to_string()).collect()),
            protocols: protocols.map(|v| v.iter().map(|v| v.to_string()).collect())
        }
    }

    #[inline] pub fn new(url: Url) -> WebSocket {
        WebSocket::with_options(url, 1, None, None)
    }

    fn try_connect(&mut self) -> IoResult<()> {
        self.stream = Some(BufferedStream::new(try!(NetworkStream::connect(self.hostname[], self.use_ssl))));
        Ok(())
    }

    fn write_request(&mut self, nonce: &str) -> IoResult<()> {
        let s = match self.stream { Some(ref mut s) => s, None => return Err(standard_error(io::IoErrorKind::NotConnected)) };

        try!(s.write(format!("GET {} HTTP/1.1\r\n", self.url.serialize_path().unwrap_or("/".to_string())).as_bytes()));
        try!(s.write(format!("Host: {}\r\n", self.url.host().unwrap()).as_bytes()));
        try!(s.write(format!("Origin: {}\r\n", self.url.serialize_no_fragment()).as_bytes()));
        try!(s.write(format!("Sec-WebSocket-Key: {}\r\n", nonce).as_bytes()));

        try!(s.write(b"Upgrade: websocket\r\n"));
        try!(s.write(b"Connection: Upgrade\r\n"));
        try!(s.write(format!("Sec-WebSocket-Version: {}\r\n", self.version).as_bytes()));
        if let Some(ref protos) = self.protocols {
            try!(s.write(format!("Sec-WebSocket-Protocol: {}\r\n", protos.connect(", ")).as_bytes()));
        }
        if let Some(ref exts) = self.extensions {
            try!(s.write(format!("Sec-WebSocket-Extensions: {}\r\n", exts.connect(", ")).as_bytes()));
        }
        try!(s.write(b"\r\n"));

        s.flush()
    }

    fn read_response(&mut self, nonce: &str) -> IoResult<()> {
        let spaces: &[_] = &[' ', '\t', '\r', '\n'];
        let s = match self.stream { Some(ref mut s) => s, None => return Err(standard_error(io::IoErrorKind::NotConnected)) };
        let status = try!(s.read_line())[].splitn(2, ' ').nth(1).and_then(|s| s.parse::<uint>());

        match status {
            Some(101) => (),
            _ => return Err(standard_error(io::InvalidInput))
        }

        let headers = s.lines().map(|r| r.unwrap_or("\r\n".to_string())) .take_while(|l| l[] != "\r\n")
            .map(|s| s[].splitn(1, ':').map(|s| s.trim_matches(spaces).to_string()).collect::<Vec<String>>())
            .map(|p| (p[0].to_string(), p[1].to_string()))
            .collect::<BTreeMap<String, String>>();

        try!(s.flush());

        let response = headers.get("Sec-WebSocket-Accept");
        match response {
            Some(r) if nonce == r[] => (),
            _ => return Err(standard_error(io::InvalidInput))
        }

        Ok(())
    }

    pub fn connect(&mut self) -> IoResult<()> {
        let mut nonce = Nonce::new();

        try!(self.try_connect());
        try!(self.write_request(nonce[]));

        nonce = nonce.encode();
        try!(self.read_response(nonce[]));

        Ok(())
    }

    fn read_header(&mut self) -> IoResult<WSHeader> {
        // XXX: this is a bug, WSHeader should accept u16
        Ok(WSHeader::from_bits_truncate(try!(self.read_be_u16())))
    }

    fn read_length(&mut self, header: &WSHeader) -> IoResult<uint> {
        let wslen = *header & WS_LEN;
        if wslen == WS_LEN16 { self.read_be_u16().map(|v| v as uint) }
        else if wslen == WS_LEN64 { self.read_be_u64().map(|v| v as uint) }
        else { Ok(wslen.bits() as uint) }
    }

    pub fn read_message(&mut self) -> IoResult<WSMessage> {
        let header = try!(self.read_header());
        let mut len = try!(self.read_length(&header));

        let mask = if header.contains(WS_MASK) {
            Some(try!(self.read_be_u32()))
        } else {
            None
        };

        // If this is the terminating frame (close command),
        // first two bytes of data MUST BE u16 status code
        let mut status = if header.contains(WS_OPTERM) {
            // compensate length of status code
            len = len - 2;
            Some(try!(self.read_be_u16()))
        } else {
            None
        };

        let mut data = try!(self.read_exact(len));

        // If we have mask, decrypt data
        if let Some(mut m) = mask {
            // decrypt status if present
            if let Some(s) = status {
                status = Some(s ^ (m & 0xffff) as u16);
                // compensate the usage of two mask bytes
                m = m.rotate_right(16);
            }
            data = WebSocket::mask_data(data[], m);
        }

        Ok(WSMessage { header: header, data: data, status: status.and_then(FromPrimitive::from_u16) })
    }

    fn mask_data(data: &[u8], mask: u32) -> Vec<u8> {
        data.iter().enumerate().map(|(i, b)| *b ^ (mask >> ((i % 4) << 3) & 0xff) as u8).collect::<Vec<u8>>()
    }

    pub fn send_message(&mut self, msg: &WSMessage) -> IoResult<()> {
        let mut len = msg.data.len();
        let mut hdr = msg.header - WS_LEN;

        // If we have status set, the data length is increased by status size
        if msg.status.is_some() {
            len = len + 2;
        }

        // Encode and send length along with header
        if len < WS_LEN16.bits() as uint {
            hdr = hdr | WSHeader::from_bits_truncate(len as u16 & WS_LEN.bits());
            try!(self.write_be_u16(hdr.bits()));

        } else if len < u16::MAX as uint {
            hdr = hdr | WS_LEN16;
            try!(self.write_be_u16(hdr.bits()));
            try!(self.write_be_u16(len as u16));

        } else {
            hdr = hdr | WS_LEN64;
            try!(self.write_be_u16(hdr.bits()));
            try!(self.write_be_u64(len as u64));
        }

        // If user required masking, encrypt all data
        if hdr.contains(WS_MASK) {
            // Generate and send random mask
            let mut mask = thread_rng().gen::<u32>();
            try!(self.write_be_u32(mask));

            // Encrypt status code if present
            if let Some(status) = msg.status {
                try!(self.write_be_u16(status.to_u16().unwrap() ^ (mask & 0xffff) as u16));
                // compensate for mask already used for status encryption
                mask = mask.rotate_right(16);
            }

            try!(self.write(WebSocket::mask_data(msg.data[], mask)[]));
        } else {
            // Send status code if present
            if let Some(status) = msg.status {
                try!(self.write_be_u16(status.to_u16().unwrap()));
            }
            try!(self.write(msg.data[]));
        }

        self.flush()
    }

    pub fn iter(&mut self) -> WSMessages {
        WSMessages { sock: self }
    }
}

impl Reader for WebSocket {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match self.stream {
            Some(ref mut s) => s.read(buf),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }
}

impl Writer for WebSocket {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.stream {
            Some(ref mut s) => s.write(buf),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.stream {
            Some(ref mut s) => s.flush(),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }
}

impl Buffer for WebSocket {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        match self.stream {
            Some(ref mut s) => s.fill_buf(),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }

    fn consume(&mut self, amt: uint) {
        match self.stream {
            Some(ref mut s) => s.consume(amt),
            None => ()
        }
    }
}

pub struct WSMessages<'a> {
    sock: &'a mut WebSocket
}

pub struct WSDefragMessages<'a> {
    underlying: &'a mut WSMessages<'a>,
    buffer: WSMessage
}

impl<'a> WSMessages<'a> {
    pub fn defrag(&'a mut self) -> WSDefragMessages<'a> {
        WSDefragMessages{ underlying: self, buffer: WSMessage{ header: WSHeader::empty(), data: Vec::new(), status: None } }
    }
}

impl<'a> Iterator for WSMessages<'a> {
    type Item = WSMessage;
    fn next(&mut self) -> Option<WSMessage> {
        self.sock.read_message().ok()
    }
}

impl<'a> WSDefragMessages<'a> {
    fn popbuf(&mut self) -> Option<WSMessage> {
        if self.buffer.data.is_empty() {
            None
        } else {
            let mut buf = WSMessage{ header: WSHeader::empty(), data: Vec::new(), status: None };
            mem::swap(&mut self.buffer, &mut buf);
            Some(buf)
        }
    }

    fn swapbuf(&mut self, msg: &mut WSMessage) {
        mem::swap(&mut self.buffer, msg);
    }
}

impl<'a> Iterator for WSDefragMessages<'a> {
    type Item = WSMessage;
    fn next(&mut self) -> Option<WSMessage> {
        loop {
            match self.underlying.next() {
                None => return self.popbuf(),
                Some(mut msg) => {
                    if msg.is_whole() {
                        return Some(msg);
                    } else if msg.is_first() {
                        self.swapbuf(&mut msg);
                    } else if msg.is_more() {
                        self.buffer.push(msg);
                    } else if msg.is_last() {
                        self.buffer.push(msg);
                        return self.popbuf().map(|v| v.last());
                    }
                }
            }
        }
    }
}

<commit_msg>use write!<commit_after>use std::io::{self, Buffer, Reader, Writer, IoResult, BufferedStream, standard_error};
use std::mem;
use std::collections::BTreeMap;
use std::rand::{thread_rng, Rng};
use std::u16;
use std::num::{Int, FromPrimitive, ToPrimitive};
use std::slice::SliceConcatExt;
use url::Url;

use nonce::Nonce;
use message::{WSMessage, WSHeader, WS_MASK, WS_LEN, WS_LEN16, WS_LEN64, WS_OPTERM};
use stream::NetworkStream;

pub struct WebSocket<S = NetworkStream> {
    stream: Option<BufferedStream<S>>,
    pub url: Url,
    hostname: String,
    use_ssl: bool,
    version: uint,
    extensions: Option<Vec<String>>,
    protocols: Option<Vec<String>>
}

impl WebSocket {
    pub fn with_options(url: Url, version: uint, protocols: Option<&[&str]>, extensions: Option<&[&str]>) -> WebSocket {
        let use_ssl = url.scheme[] == "wss";

        let port = match url.port() {
            Some(p) => p,
            None if use_ssl => 443,
            _ => 80
        };

        WebSocket {
            stream: None,
            hostname: format!("{}:{}", url.serialize_host().unwrap(), port),
            url: url,
            use_ssl: use_ssl,
            version: version,
            extensions: extensions.map(|v| v.iter().map(|v| v.to_string()).collect()),
            protocols: protocols.map(|v| v.iter().map(|v| v.to_string()).collect())
        }
    }

    #[inline] pub fn new(url: Url) -> WebSocket {
        WebSocket::with_options(url, 1, None, None)
    }

    fn try_connect(&mut self) -> IoResult<()> {
        self.stream = Some(BufferedStream::new(try!(NetworkStream::connect(self.hostname[], self.use_ssl))));
        Ok(())
    }

    fn write_request(&mut self, nonce: &str) -> IoResult<()> {
        let s = match self.stream { Some(ref mut s) => s, None => return Err(standard_error(io::IoErrorKind::NotConnected)) };

        try!(write!(s, "GET {} HTTP/1.1\r\n", self.url.serialize_path().unwrap_or("/".to_string())));
        try!(write!(s, "Host: {}\r\n", self.url.host().unwrap()));
        try!(write!(s, "Origin: {}\r\n", self.url.serialize_no_fragment()));
        try!(write!(s, "Sec-WebSocket-Key: {}\r\n", nonce));

        try!(s.write(b"Upgrade: websocket\r\n"));
        try!(s.write(b"Connection: Upgrade\r\n"));
        try!(write!(s, "Sec-WebSocket-Version: {}\r\n", self.version));
        if let Some(ref protos) = self.protocols {
            try!(write!(s, "Sec-WebSocket-Protocol: {}\r\n", protos.connect(", ")));
        }
        if let Some(ref exts) = self.extensions {
            try!(write!(s, "Sec-WebSocket-Extensions: {}\r\n", exts.connect(", ")));
        }
        try!(s.write(b"\r\n"));

        s.flush()
    }

    fn read_response(&mut self, nonce: &str) -> IoResult<()> {
        let spaces: &[_] = &[' ', '\t', '\r', '\n'];
        let s = match self.stream { Some(ref mut s) => s, None => return Err(standard_error(io::IoErrorKind::NotConnected)) };
        let status = try!(s.read_line())[].splitn(2, ' ').nth(1).and_then(|s| s.parse::<uint>());

        match status {
            Some(101) => (),
            _ => return Err(standard_error(io::InvalidInput))
        }

        let headers = s.lines().map(|r| r.unwrap_or("\r\n".to_string())) .take_while(|l| l[] != "\r\n")
            .map(|s| s[].splitn(1, ':').map(|s| s.trim_matches(spaces).to_string()).collect::<Vec<String>>())
            .map(|p| (p[0].to_string(), p[1].to_string()))
            .collect::<BTreeMap<String, String>>();

        try!(s.flush());

        let response = headers.get("Sec-WebSocket-Accept");
        match response {
            Some(r) if nonce == r[] => (),
            _ => return Err(standard_error(io::InvalidInput))
        }

        Ok(())
    }

    pub fn connect(&mut self) -> IoResult<()> {
        let mut nonce = Nonce::new();

        try!(self.try_connect());
        try!(self.write_request(nonce[]));

        nonce = nonce.encode();
        try!(self.read_response(nonce[]));

        Ok(())
    }

    fn read_header(&mut self) -> IoResult<WSHeader> {
        // XXX: this is a bug, WSHeader should accept u16
        Ok(WSHeader::from_bits_truncate(try!(self.read_be_u16())))
    }

    fn read_length(&mut self, header: &WSHeader) -> IoResult<uint> {
        let wslen = *header & WS_LEN;
        if wslen == WS_LEN16 { self.read_be_u16().map(|v| v as uint) }
        else if wslen == WS_LEN64 { self.read_be_u64().map(|v| v as uint) }
        else { Ok(wslen.bits() as uint) }
    }

    pub fn read_message(&mut self) -> IoResult<WSMessage> {
        let header = try!(self.read_header());
        let mut len = try!(self.read_length(&header));

        let mask = if header.contains(WS_MASK) {
            Some(try!(self.read_be_u32()))
        } else {
            None
        };

        // If this is the terminating frame (close command),
        // first two bytes of data MUST BE u16 status code
        let mut status = if header.contains(WS_OPTERM) {
            // compensate length of status code
            len = len - 2;
            Some(try!(self.read_be_u16()))
        } else {
            None
        };

        let mut data = try!(self.read_exact(len));

        // If we have mask, decrypt data
        if let Some(mut m) = mask {
            // decrypt status if present
            if let Some(s) = status {
                status = Some(s ^ (m & 0xffff) as u16);
                // compensate the usage of two mask bytes
                m = m.rotate_right(16);
            }
            data = WebSocket::mask_data(data[], m);
        }

        Ok(WSMessage { header: header, data: data, status: status.and_then(FromPrimitive::from_u16) })
    }

    fn mask_data(data: &[u8], mask: u32) -> Vec<u8> {
        data.iter().enumerate().map(|(i, b)| *b ^ (mask >> ((i % 4) << 3) & 0xff) as u8).collect::<Vec<u8>>()
    }

    pub fn send_message(&mut self, msg: &WSMessage) -> IoResult<()> {
        let mut len = msg.data.len();
        let mut hdr = msg.header - WS_LEN;

        // If we have status set, the data length is increased by status size
        if msg.status.is_some() {
            len = len + 2;
        }

        // Encode and send length along with header
        if len < WS_LEN16.bits() as uint {
            hdr = hdr | WSHeader::from_bits_truncate(len as u16 & WS_LEN.bits());
            try!(self.write_be_u16(hdr.bits()));

        } else if len < u16::MAX as uint {
            hdr = hdr | WS_LEN16;
            try!(self.write_be_u16(hdr.bits()));
            try!(self.write_be_u16(len as u16));

        } else {
            hdr = hdr | WS_LEN64;
            try!(self.write_be_u16(hdr.bits()));
            try!(self.write_be_u64(len as u64));
        }

        // If user required masking, encrypt all data
        if hdr.contains(WS_MASK) {
            // Generate and send random mask
            let mut mask = thread_rng().gen::<u32>();
            try!(self.write_be_u32(mask));

            // Encrypt status code if present
            if let Some(status) = msg.status {
                try!(self.write_be_u16(status.to_u16().unwrap() ^ (mask & 0xffff) as u16));
                // compensate for mask already used for status encryption
                mask = mask.rotate_right(16);
            }

            try!(self.write(WebSocket::mask_data(msg.data[], mask)[]));
        } else {
            // Send status code if present
            if let Some(status) = msg.status {
                try!(self.write_be_u16(status.to_u16().unwrap()));
            }
            try!(self.write(msg.data[]));
        }

        self.flush()
    }

    pub fn iter(&mut self) -> WSMessages {
        WSMessages { sock: self }
    }
}

impl Reader for WebSocket {
    fn read(&mut self, buf: &mut [u8]) -> IoResult<uint> {
        match self.stream {
            Some(ref mut s) => s.read(buf),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }
}

impl Writer for WebSocket {
    fn write(&mut self, buf: &[u8]) -> IoResult<()> {
        match self.stream {
            Some(ref mut s) => s.write(buf),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }

    fn flush(&mut self) -> IoResult<()> {
        match self.stream {
            Some(ref mut s) => s.flush(),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }
}

impl Buffer for WebSocket {
    fn fill_buf<'a>(&'a mut self) -> IoResult<&'a [u8]> {
        match self.stream {
            Some(ref mut s) => s.fill_buf(),
            None => Err(standard_error(io::IoErrorKind::NotConnected))
        }
    }

    fn consume(&mut self, amt: uint) {
        match self.stream {
            Some(ref mut s) => s.consume(amt),
            None => ()
        }
    }
}

pub struct WSMessages<'a> {
    sock: &'a mut WebSocket
}

pub struct WSDefragMessages<'a> {
    underlying: &'a mut WSMessages<'a>,
    buffer: WSMessage
}

impl<'a> WSMessages<'a> {
    pub fn defrag(&'a mut self) -> WSDefragMessages<'a> {
        WSDefragMessages{ underlying: self, buffer: WSMessage{ header: WSHeader::empty(), data: Vec::new(), status: None } }
    }
}

impl<'a> Iterator for WSMessages<'a> {
    type Item = WSMessage;
    fn next(&mut self) -> Option<WSMessage> {
        self.sock.read_message().ok()
    }
}

impl<'a> WSDefragMessages<'a> {
    fn popbuf(&mut self) -> Option<WSMessage> {
        if self.buffer.data.is_empty() {
            None
        } else {
            let mut buf = WSMessage{ header: WSHeader::empty(), data: Vec::new(), status: None };
            mem::swap(&mut self.buffer, &mut buf);
            Some(buf)
        }
    }

    fn swapbuf(&mut self, msg: &mut WSMessage) {
        mem::swap(&mut self.buffer, msg);
    }
}

impl<'a> Iterator for WSDefragMessages<'a> {
    type Item = WSMessage;
    fn next(&mut self) -> Option<WSMessage> {
        loop {
            match self.underlying.next() {
                None => return self.popbuf(),
                Some(mut msg) => {
                    if msg.is_whole() {
                        return Some(msg);
                    } else if msg.is_first() {
                        self.swapbuf(&mut msg);
                    } else if msg.is_more() {
                        self.buffer.push(msg);
                    } else if msg.is_last() {
                        self.buffer.push(msg);
                        return self.popbuf().map(|v| v.last());
                    }
                }
            }
        }
    }
}

<|endoftext|>


<commit_before>//! The module defines MPD status data structures

use std::str::FromStr;
use std::fmt;
use time::Duration;

use error::{Error, ParseError};
use song::{Id, QueuePlace};
use convert::FromIter;

/// MPD status
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Status {
    /// volume (0-100, or -1 if volume is unavailable (e.g. for HTTPD output type)
    pub volume: i8,
    /// repeat mode
    pub repeat: bool,
    /// random mode
    pub random: bool,
    /// single mode
    pub single: bool,
    /// consume mode
    pub consume: bool,
    /// queue version number
    pub queue_version: u32,
    /// queue length
    pub queue_len: u32,
    /// playback state
    pub state: State,
    /// currently playing song place in the queue
    pub song: Option<QueuePlace>,
    /// next song to play place in the queue
    pub nextsong: Option<QueuePlace>,
    /// time current song played, and total song duration (in seconds resolution)
    pub time: Option<(Duration, Duration)>,
    /// elapsed play time current song played (in milliseconds resolution)
    pub elapsed: Option<Duration>,
    /// current song duration
    pub duration: Option<Duration>,
    /// current song bitrate, kbps
    pub bitrate: Option<u32>,
    /// crossfade timeout, seconds
    pub crossfade: Option<Duration>,
    /// mixramp threshold, dB
    pub mixrampdb: f32,
    /// mixramp duration, seconds
    pub mixrampdelay: Option<Duration>,
    /// current audio playback format
    pub audio: Option<AudioFormat>,
    /// current DB updating job number (if DB updating is in progress)
    pub updating_db: Option<u32>,
    /// last player error (if happened, can be reset with `clearerror()` method)
    pub error: Option<String>,
    /// replay gain mode
    pub replaygain: Option<ReplayGain>
}

impl FromIter for Status {
    fn from_iter<I: Iterator<Item=Result<(String, String), Error>>>(iter: I) -> Result<Status, Error> {
        let mut result = Status::default();

        for res in iter {
            let line = try!(res);
            match &*line.0 {
                "volume" => result.volume = try!(line.1.parse()),

                "repeat" => result.repeat = &*line.1 == "1",
                "random" => result.random = &*line.1 == "1",
                "single" => result.single = &*line.1 == "1",
                "consume" => result.consume = &*line.1 == "1",

                "playlist" => result.queue_version = try!(line.1.parse()),
                "playlistlength" => result.queue_len = try!(line.1.parse()),
                "state" => result.state = try!(line.1.parse()),
                "songid" => match result.song {
                    None => result.song = Some(QueuePlace { id: Id(try!(line.1.parse())), pos: 0, prio: 0 }),
                    Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                },
                "song" => match result.song {
                    None => result.song = Some(QueuePlace { pos: try!(line.1.parse()), id: Id(0), prio: 0 }),
                    Some(ref mut place) => place.pos = try!(line.1.parse()),
                },
                "nextsongid" => match result.nextsong {
                    None => result.song = Some(QueuePlace { id: Id(try!(line.1.parse())), pos: 0, prio: 0 }),
                    Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                },
                "nextsong" => match result.nextsong {
                    None => result.song = Some(QueuePlace { pos: try!(line.1.parse()), id: Id(0), prio: 0 }),
                    Some(ref mut place) => place.pos = try!(line.1.parse()),
                },
                "time" => result.time = try!({
                    let mut splits = line.1.splitn(2, ':').map(|v| v.parse().map_err(ParseError::BadInteger).map(Duration::seconds));
                    match (splits.next(), splits.next()) {
                        (Some(Ok(a)), Some(Ok(b))) => Ok(Some((a, b))),
                        (Some(Err(e)), _) | (_, Some(Err(e))) => Err(e),
                        _ => Ok(None)
                    }
                }),
                // TODO" => float errors don't work on stable
                "elapsed" => result.elapsed = line.1.parse::<f32>().ok().map(|v| Duration::seconds((v * 1000.0) as i64)),
                "duration" => result.duration = Some(Duration::seconds(try!(line.1.parse()))),
                "bitrate" => result.bitrate = Some(try!(line.1.parse())),
                "xfade" => result.crossfade = Some(Duration::seconds(try!(line.1.parse()))),
                //"mixrampdb" => 0.0, //get_field!(map, "mixrampdb"),
                //"mixrampdelay" => None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
                "audio" => result.audio = Some(try!(line.1.parse())),
                "updating_db" => result.updating_db = Some(try!(line.1.parse())),
                "error" => result.error = Some(line.1.to_owned()),
                "replay_gain_mode" => result.replaygain = Some(try!(line.1.parse())),
                _ => ()
            }
        }

        Ok(result)
    }
}

/// Audio playback format
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub struct AudioFormat {
    /// sample rate, kbps
    pub rate: u32,
    /// sample resolution in bits, can be 0 for floating point resolution
    pub bits: u8,
    /// number of channels
    pub chans: u8
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next().ok_or(ParseError::NoRate).and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next().ok_or(ParseError::NoBits).and_then(|v| if &*v == "f" { Ok(0) } else { v.parse().map_err(ParseError::BadBits) })),
            chans: try!(it.next().ok_or(ParseError::NoChans).and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

/// Playback state
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub enum State {
    /// player stopped
    Stop,
    /// player is playing
    Play,
    /// player paused
    Pause
}

impl Default for State {
    fn default() -> State {
        State::Stop
    }
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}

/// Replay gain mode
#[derive(Debug, Clone, Copy, PartialEq, RustcEncodable)]
pub enum ReplayGain {
    /// off
    Off,
    /// track
    Track,
    /// album
    Album,
    /// auto
    Auto
}

impl FromStr for ReplayGain {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<ReplayGain, ParseError> {
        use self::ReplayGain::*;
        match s {
            "off" => Ok(Off),
            "track" => Ok(Track),
            "album" => Ok(Album),
            "auto" => Ok(Auto),
            _ => Err(ParseError::BadValue(s.to_owned()))
        }
    }
}

impl fmt::Display for ReplayGain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ReplayGain::*;
        f.write_str(match *self {
            Off => "off",
            Track => "track",
            Album => "album",
            Auto => "auto",
        })
    }
}
<commit_msg>impl encodable for status<commit_after>//! The module defines MPD status data structures

use std::str::FromStr;
use std::fmt;
use time::Duration;
use rustc_serialize::{Encodable, Encoder};

use error::{Error, ParseError};
use song::{Id, QueuePlace};
use convert::FromIter;

/// MPD status
#[derive(Debug, PartialEq, Clone, Default)]
pub struct Status {
    /// volume (0-100, or -1 if volume is unavailable (e.g. for HTTPD output type)
    pub volume: i8,
    /// repeat mode
    pub repeat: bool,
    /// random mode
    pub random: bool,
    /// single mode
    pub single: bool,
    /// consume mode
    pub consume: bool,
    /// queue version number
    pub queue_version: u32,
    /// queue length
    pub queue_len: u32,
    /// playback state
    pub state: State,
    /// currently playing song place in the queue
    pub song: Option<QueuePlace>,
    /// next song to play place in the queue
    pub nextsong: Option<QueuePlace>,
    /// time current song played, and total song duration (in seconds resolution)
    pub time: Option<(Duration, Duration)>,
    /// elapsed play time current song played (in milliseconds resolution)
    pub elapsed: Option<Duration>,
    /// current song duration
    pub duration: Option<Duration>,
    /// current song bitrate, kbps
    pub bitrate: Option<u32>,
    /// crossfade timeout, seconds
    pub crossfade: Option<Duration>,
    /// mixramp threshold, dB
    pub mixrampdb: f32,
    /// mixramp duration, seconds
    pub mixrampdelay: Option<Duration>,
    /// current audio playback format
    pub audio: Option<AudioFormat>,
    /// current DB updating job number (if DB updating is in progress)
    pub updating_db: Option<u32>,
    /// last player error (if happened, can be reset with `clearerror()` method)
    pub error: Option<String>,
    /// replay gain mode
    pub replaygain: Option<ReplayGain>
}

impl Encodable for Status {
    fn encode<S: Encoder>(&self, e: &mut S) -> Result<(), S::Error> {
        e.emit_struct("Status", 21, |e|
            e.emit_struct_field("volume", 0, |e| self.volume.encode(e)).and_then(|_|
            e.emit_struct_field("repeat", 1, |e| self.repeat.encode(e))).and_then(|_|
            e.emit_struct_field("random", 2, |e| self.random.encode(e))).and_then(|_|
            e.emit_struct_field("single", 3, |e| self.single.encode(e))).and_then(|_|
            e.emit_struct_field("consume", 4, |e| self.consume.encode(e))).and_then(|_|
            e.emit_struct_field("queue_version", 5, |e| self.queue_version.encode(e))).and_then(|_|
            e.emit_struct_field("queue_len", 6, |e| self.queue_len.encode(e))).and_then(|_|
            e.emit_struct_field("state", 7, |e| self.state.encode(e))).and_then(|_|
            e.emit_struct_field("song", 8, |e| self.song.encode(e))).and_then(|_|
            e.emit_struct_field("nextsong", 9, |e| self.nextsong.encode(e))).and_then(|_|
            e.emit_struct_field("time", 10, |e| e.emit_option(
                    |e| self.time.map(|p| e.emit_option_some(|e| e.emit_tuple(2, |e|
                                                                e.emit_tuple_arg(0, |e| p.0.num_seconds().encode(e)).and_then(|_|
                                                                e.emit_tuple_arg(1, |e| p.1.num_seconds().encode(e)))
                                                                ))).unwrap_or_else(|| e.emit_option_none())))).and_then(|_|
            e.emit_struct_field("elapsed", 11, |e| e.emit_option(
                    |e| self.elapsed.map(|d| e.emit_option_some(|e| d.num_seconds().encode(e))).unwrap_or_else(|| e.emit_option_none())))).and_then(|_|
            e.emit_struct_field("duration", 12, |e| e.emit_option(
                    |e| self.duration.map(|d| e.emit_option_some(|e| d.num_seconds().encode(e))).unwrap_or_else(|| e.emit_option_none())))).and_then(|_|
            e.emit_struct_field("bitrate", 13, |e| self.bitrate.encode(e))).and_then(|_|
            e.emit_struct_field("crossfade", 14, |e| e.emit_option(
                    |e| self.crossfade.map(|d| e.emit_option_some(|e| d.num_seconds().encode(e))).unwrap_or_else(|| e.emit_option_none())))).and_then(|_|
            e.emit_struct_field("mixrampdb", 15, |e| self.mixrampdb.encode(e))).and_then(|_|
            e.emit_struct_field("mixrampdelay", 16, |e| e.emit_option(
                    |e| self.mixrampdelay.map(|d| e.emit_option_some(|e| d.num_seconds().encode(e))).unwrap_or_else(|| e.emit_option_none())))).and_then(|_|
            e.emit_struct_field("audio", 17, |e| self.audio.encode(e))).and_then(|_|
            e.emit_struct_field("updating_db", 18, |e| self.updating_db.encode(e))).and_then(|_|
            e.emit_struct_field("error", 19, |e| self.error.encode(e))).and_then(|_|
            e.emit_struct_field("replaygain", 20,|e| self.replaygain.encode(e))))

    }
}

impl FromIter for Status {
    fn from_iter<I: Iterator<Item=Result<(String, String), Error>>>(iter: I) -> Result<Status, Error> {
        let mut result = Status::default();

        for res in iter {
            let line = try!(res);
            match &*line.0 {
                "volume" => result.volume = try!(line.1.parse()),

                "repeat" => result.repeat = &*line.1 == "1",
                "random" => result.random = &*line.1 == "1",
                "single" => result.single = &*line.1 == "1",
                "consume" => result.consume = &*line.1 == "1",

                "playlist" => result.queue_version = try!(line.1.parse()),
                "playlistlength" => result.queue_len = try!(line.1.parse()),
                "state" => result.state = try!(line.1.parse()),
                "songid" => match result.song {
                    None => result.song = Some(QueuePlace { id: Id(try!(line.1.parse())), pos: 0, prio: 0 }),
                    Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                },
                "song" => match result.song {
                    None => result.song = Some(QueuePlace { pos: try!(line.1.parse()), id: Id(0), prio: 0 }),
                    Some(ref mut place) => place.pos = try!(line.1.parse()),
                },
                "nextsongid" => match result.nextsong {
                    None => result.song = Some(QueuePlace { id: Id(try!(line.1.parse())), pos: 0, prio: 0 }),
                    Some(ref mut place) => place.id = Id(try!(line.1.parse())),
                },
                "nextsong" => match result.nextsong {
                    None => result.song = Some(QueuePlace { pos: try!(line.1.parse()), id: Id(0), prio: 0 }),
                    Some(ref mut place) => place.pos = try!(line.1.parse()),
                },
                "time" => result.time = try!({
                    let mut splits = line.1.splitn(2, ':').map(|v| v.parse().map_err(ParseError::BadInteger).map(Duration::seconds));
                    match (splits.next(), splits.next()) {
                        (Some(Ok(a)), Some(Ok(b))) => Ok(Some((a, b))),
                        (Some(Err(e)), _) | (_, Some(Err(e))) => Err(e),
                        _ => Ok(None)
                    }
                }),
                // TODO" => float errors don't work on stable
                "elapsed" => result.elapsed = line.1.parse::<f32>().ok().map(|v| Duration::seconds((v * 1000.0) as i64)),
                "duration" => result.duration = Some(Duration::seconds(try!(line.1.parse()))),
                "bitrate" => result.bitrate = Some(try!(line.1.parse())),
                "xfade" => result.crossfade = Some(Duration::seconds(try!(line.1.parse()))),
                //"mixrampdb" => 0.0, //get_field!(map, "mixrampdb"),
                //"mixrampdelay" => None, //get_field!(map, opt "mixrampdelay").map(|v: f64| Duration::milliseconds((v * 1000.0) as i64)),
                "audio" => result.audio = Some(try!(line.1.parse())),
                "updating_db" => result.updating_db = Some(try!(line.1.parse())),
                "error" => result.error = Some(line.1.to_owned()),
                "replay_gain_mode" => result.replaygain = Some(try!(line.1.parse())),
                _ => ()
            }
        }

        Ok(result)
    }
}

/// Audio playback format
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub struct AudioFormat {
    /// sample rate, kbps
    pub rate: u32,
    /// sample resolution in bits, can be 0 for floating point resolution
    pub bits: u8,
    /// number of channels
    pub chans: u8
}

impl FromStr for AudioFormat {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<AudioFormat, ParseError> {
        let mut it = s.split(':');
        Ok(AudioFormat {
            rate: try!(it.next().ok_or(ParseError::NoRate).and_then(|v| v.parse().map_err(ParseError::BadRate))),
            bits: try!(it.next().ok_or(ParseError::NoBits).and_then(|v| if &*v == "f" { Ok(0) } else { v.parse().map_err(ParseError::BadBits) })),
            chans: try!(it.next().ok_or(ParseError::NoChans).and_then(|v| v.parse().map_err(ParseError::BadChans))),
        })
    }
}

/// Playback state
#[derive(Debug, Copy, Clone, PartialEq, RustcEncodable)]
pub enum State {
    /// player stopped
    Stop,
    /// player is playing
    Play,
    /// player paused
    Pause
}

impl Default for State {
    fn default() -> State {
        State::Stop
    }
}

impl FromStr for State {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<State, ParseError> {
        match s {
            "stop" => Ok(State::Stop),
            "play" => Ok(State::Play),
            "pause" => Ok(State::Pause),
            _ => Err(ParseError::BadState(s.to_owned())),
        }
    }
}

/// Replay gain mode
#[derive(Debug, Clone, Copy, PartialEq, RustcEncodable)]
pub enum ReplayGain {
    /// off
    Off,
    /// track
    Track,
    /// album
    Album,
    /// auto
    Auto
}

impl FromStr for ReplayGain {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<ReplayGain, ParseError> {
        use self::ReplayGain::*;
        match s {
            "off" => Ok(Off),
            "track" => Ok(Track),
            "album" => Ok(Album),
            "auto" => Ok(Auto),
            _ => Err(ParseError::BadValue(s.to_owned()))
        }
    }
}

impl fmt::Display for ReplayGain {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        use self::ReplayGain::*;
        f.write_str(match *self {
            Off => "off",
            Track => "track",
            Album => "album",
            Auto => "auto",
        })
    }
}
<|endoftext|>


<commit_before>pub use sensors::SensorTemplate;
pub use sensors::Sensors;
pub use sensors::TemperatureSensor;
pub use sensors::PeopleNowPresentSensor;

use std::collections::HashMap;

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Location {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,
    pub lat: f64,
    pub lon: f64,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Icon {
    pub open: String,
    pub close: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct State {
    pub open: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lastchange: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trigger_person: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon: Option<Icon>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub extra: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Keymaster {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub irc_nick: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub twitter: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct GoogleContact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub plus: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Contact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keymasters: Option<Vec<Keymaster>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub irc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub twitter: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub facebook: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub google: Option<GoogleContact>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub identica: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub foursquare: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ml: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jabber: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub issue_mail: Option<String>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Feed {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub _type: Option<String>,
    pub url: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Feeds {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub blog: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub wiki: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub calendar: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub flickr: Option<Feed>,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Cache {
    pub schedule: String,
}

#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

/// The main Space API status object.
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spacefed: Option<Spacefed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub projects: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cam: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub feeds: Option<Feeds>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub events: Option<Vec<Event>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub radio_show: Option<Vec<RadioShow>>,

    // SpaceAPI internal usage
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cache: Option<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sensors: Option<Sensors>,

    // Version extension
    // TODO: Once we move to serde, maybe we can store this
    // as `HashMap<String, &'static str>`?
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ext_versions: Option<HashMap<String, String>>,
}

impl Status {
    /// Create a new Status object with only the absolutely required fields.
    pub fn new<S: Into<String>>(space: S, logo: S, url: S, location: Location, contact: Contact,
                                issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".into(),
            space: space.into(),
            logo: logo.into(),
            url: url.into(),
            location: location,
            contact: contact,

            spacefed: None,
            projects: None,
            cam: None,
            feeds: None,
            events: None,
            radio_show: None,

            cache: None,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: None,
                trigger_person: None,
                message: None,
                icon: None,
            },
            sensors: None,

            ext_versions: None,
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use serde_json::{to_string, from_str};

    #[test]
    fn serialize_deserialize_cache() {
        let a = Cache { schedule: "bla".into() };
        let b: Cache = from_str(&to_string(&a).unwrap()).unwrap();
        assert_eq!(a.schedule, b.schedule);
    }

    #[test]
    fn serialize_deserialize_simple_contact() {
        let a: Contact = Contact {
            phone: None,
            sip: None,
            keymasters: Some(vec![
                              Keymaster {
                                  name: Some("Joe".into()),
                                  irc_nick: None,
                                  phone: None,
                                  email: Some("joe@example.com".into()),
                                  twitter: None,
                              },
            ]),
            irc: Some("bla".into()),
            twitter: None,
            facebook: None,
            google: Some(GoogleContact { plus: Some("http://gplus/profile".into()) }),
            identica: None,
            foursquare: None,
            email: Some("bli@bla".into()),
            ml: None,
            jabber: None,
            issue_mail: None,
        };
        let b: Contact = from_str(&to_string(&a).unwrap()).unwrap();

        assert_eq!(a, b);
    }

    #[test]
    fn serialize_skip_none() {
        let f1 = Feed {
            _type: Some("rss".to_string()),
            url: "https://some/rss.xml".to_string(),
        };
        let f2 = Feed {
            _type: None,
            url: "https://some/rss.xml".to_string(),
        };
        assert_eq!(to_string(&f1).unwrap(),
            "{\"_type\":\"rss\",\"url\":\"https://some/rss.xml\"}".to_string());
        assert_eq!(to_string(&f2).unwrap(),
            "{\"url\":\"https://some/rss.xml\"}".to_string());
    }

}

<commit_msg>Derive Default implementation for status structs<commit_after>pub use sensors::SensorTemplate;
pub use sensors::Sensors;
pub use sensors::TemperatureSensor;
pub use sensors::PeopleNowPresentSensor;

use std::collections::HashMap;

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Location {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub address: Option<String>,
    pub lat: f64,
    pub lon: f64,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Spacefed {
    pub spacenet: bool,
    pub spacesaml: bool,
    pub spacephone: bool,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Icon {
    pub open: String,
    pub close: String,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct State {
    pub open: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub lastchange: Option<u64>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub trigger_person: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub message: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub icon: Option<Icon>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Event {
    pub name: String,
    pub _type: String,
    pub timestamp: u64,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub extra: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Keymaster {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub name: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub irc_nick: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub twitter: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct GoogleContact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub plus: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Contact {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub phone: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sip: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub keymasters: Option<Vec<Keymaster>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub irc: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub twitter: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub facebook: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub google: Option<GoogleContact>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub identica: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub foursquare: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub email: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ml: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub jabber: Option<String>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub issue_mail: Option<String>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Feed {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub _type: Option<String>,
    pub url: String,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Feeds {
    #[serde(skip_serializing_if = "Option::is_none")]
    pub blog: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub wiki: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub calendar: Option<Feed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub flickr: Option<Feed>,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Cache {
    pub schedule: String,
}

#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct RadioShow {
    pub name: String,
    pub url: String,
    pub _type: String,
    pub start: String,
    pub end: String,
}

/// The main Space API status object.
#[derive(Serialize, Deserialize, Default, Debug, Clone, PartialEq)]
pub struct Status {

    // Hackerspace properties
    pub api: String,
    pub space: String,
    pub logo: String,
    pub url: String,
    pub location: Location,
    pub contact: Contact,

    // Hackerspace features / projects
    #[serde(skip_serializing_if = "Option::is_none")]
    pub spacefed: Option<Spacefed>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub projects: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cam: Option<Vec<String>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub feeds: Option<Feeds>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub events: Option<Vec<Event>>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub radio_show: Option<Vec<RadioShow>>,

    // SpaceAPI internal usage
    #[serde(skip_serializing_if = "Option::is_none")]
    pub cache: Option<Cache>,
    pub issue_report_channels: Vec<String>,

    // Mutable data
    pub state: State,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub sensors: Option<Sensors>,

    // Version extension
    // TODO: Once we move to serde, maybe we can store this
    // as `HashMap<String, &'static str>`?
    #[serde(skip_serializing_if = "Option::is_none")]
    pub ext_versions: Option<HashMap<String, String>>,
}

impl Status {
    /// Create a new Status object with only the absolutely required fields.
    pub fn new<S: Into<String>>(space: S, logo: S, url: S, location: Location, contact: Contact,
                                issue_report_channels: Vec<String>) -> Status {
        Status {
            api: "0.13".into(),
            space: space.into(),
            logo: logo.into(),
            url: url.into(),
            location: location,
            contact: contact,

            spacefed: None,
            projects: None,
            cam: None,
            feeds: None,
            events: None,
            radio_show: None,

            cache: None,
            issue_report_channels: issue_report_channels,

            state: State {
                open: None,
                lastchange: None,
                trigger_person: None,
                message: None,
                icon: None,
            },
            sensors: None,

            ext_versions: None,
        }
    }

}

#[cfg(test)]
mod test {
    use super::*;
    use serde_json::{to_string, from_str};

    #[test]
    fn serialize_deserialize_cache() {
        let a = Cache { schedule: "bla".into() };
        let b: Cache = from_str(&to_string(&a).unwrap()).unwrap();
        assert_eq!(a.schedule, b.schedule);
    }

    #[test]
    fn serialize_deserialize_simple_contact() {
        let a: Contact = Contact {
            phone: None,
            sip: None,
            keymasters: Some(vec![
                              Keymaster {
                                  name: Some("Joe".into()),
                                  irc_nick: None,
                                  phone: None,
                                  email: Some("joe@example.com".into()),
                                  twitter: None,
                              },
            ]),
            irc: Some("bla".into()),
            twitter: None,
            facebook: None,
            google: Some(GoogleContact { plus: Some("http://gplus/profile".into()) }),
            identica: None,
            foursquare: None,
            email: Some("bli@bla".into()),
            ml: None,
            jabber: None,
            issue_mail: None,
        };
        let b: Contact = from_str(&to_string(&a).unwrap()).unwrap();

        assert_eq!(a, b);
    }

    #[test]
    fn serialize_skip_none() {
        let f1 = Feed {
            _type: Some("rss".to_string()),
            url: "https://some/rss.xml".to_string(),
        };
        let f2 = Feed {
            _type: None,
            url: "https://some/rss.xml".to_string(),
        };
        assert_eq!(to_string(&f1).unwrap(),
            "{\"_type\":\"rss\",\"url\":\"https://some/rss.xml\"}".to_string());
        assert_eq!(to_string(&f2).unwrap(),
            "{\"url\":\"https://some/rss.xml\"}".to_string());
    }

}

<|endoftext|>


<commit_before>use nom;
use nom::{Err, IResult};

use metadata;
use frame;
use subframe;

use metadata::{Metadata, StreamInfo, metadata_parser};
use frame::{frame_parser, Frame};
use utility::{ErrorKind, ByteStream, ReadStream, StreamProducer};

use std::io;
use std::usize;
use std::fs::File;

enum ParserState {
  Marker,
  Metadata,
  Frame,
}

/// FLAC stream that decodes and hold file information.
pub struct Stream
 {
  info: StreamInfo,
  metadata: Vec<Metadata>,
  frames: Vec<Frame>,
  state: ParserState,
  output: Vec<i32>,
  frame_index: usize,
  producer: P,
}

fn parser<'a>(input: &'a [u8], is_start: &mut bool)
              -> IResult<&'a [u8], Metadata> {
  let mut slice = input;

  if *is_start {
    let (i, _) = try_parse!(slice, tag!("fLaC"));

    slice     = i;
    *is_start = false;
  }

  metadata_parser(slice)
}

impl Stream {
  /// Constructor for the default state of a FLAC stream.
  ///
  /// This doesn't actually decode anything, it just hold the default values
  /// of each field.
  pub fn new() -> Stream {
    Stream {
      info: StreamInfo::new(),
      metadata: Vec::new(),
      frames: Vec::new(),
      state: ParserState::Marker,
      output: Vec::new(),
      frame_index: 0,
    }
  }

  /// Returns information for the current stream.
  pub fn info(&self) -> StreamInfo {
    self.info
  }

  /// Returns a slice of `Metadata`
  ///
  /// This slice excludes `StreamInfo`, which is located in `Stream::info`.
  /// Everything else is related to metadata for the FLAC stream is in the
  /// slice.
  pub fn metadata(&self) -> &[Metadata] {
    &self.metadata
  }

  /// Constructs a decoder with the given file name.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::NotFound` is returned when the given filename isn't found.
  /// * `ErrorKind::InvalidData` is returned when the data within the file
  ///   isn't valid FLAC data.
  pub fn from_file(filename: &str) -> io::Result<Stream> {
    File::open(filename).and_then(|file| {
      let mut producer = ReadStream::new(file);
      let error_str    = format!("parser: couldn't parse the given file {}",
                                 filename);

      Stream::from_stream_producer(&mut producer, &error_str)
    })
  }

  /// Constructs a decoder with the given buffer.
  ///
  /// This constructor assumes that an entire FLAC file is in the buffer.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::InvalidData` is returned when the data within the buffer
  ///   isn't valid FLAC data.
  pub fn from_buffer(buffer: &[u8]) -> io::Result<Stream> {
    let mut producer = ByteStream::new(buffer);
    let error_str    = "parser: couldn't parse the buffer";

    Stream::from_stream_producer(&mut producer, error_str)
  }

  fn from_stream_producer
(producer: &mut P, error_str: &str)
                             -> io::Result<Stream>
   where P: StreamProducer {
    let mut is_error = false;
    let mut stream   = Stream {
      info: StreamInfo::new(),
      metadata: Vec::new(),
      frames: Vec::new(),
      state: ParserState::Marker,
      output: Vec::new(),
      frame_index: 0,
    };

    loop {
      match stream.handle(producer) {
        Ok(_)                      => break,
        Err(ErrorKind::EndOfInput) => break,
        Err(ErrorKind::Continue)   => continue,
        Err(_)                     => {
          is_error = true;

          break;
        }
      }
    }

    if !is_error {
      let channels    = stream.info.channels as usize;
      let block_size  = stream.info.max_block_size as usize;
      let output_size = block_size * channels;

      stream.output.reserve_exact(output_size);

      unsafe { stream.output.set_len(output_size) }

      Ok(stream)
    } else {
      Err(io::Error::new(io::ErrorKind::InvalidData, error_str))
    }
  }

  /// Returns an iterator over the decoded samples.
  pub fn iter(&mut self) -> Iter
 {
    let samples_left = self.info.total_samples;

    Iter {
      stream: self,
      channel: 0,
      block_size: 0,
      sample_index: 0,
      samples_left: samples_left,
    }
  }

  fn next_frame<'a>(&'a mut self) -> Option<&'a [i32]> {
    if self.frames.is_empty() || self.frame_index >= self.frames.len() {
      None
    } else {
      let frame       = &self.frames[self.frame_index];
      let channels    = frame.header.channels as usize;
      let block_size  = frame.header.block_size as usize;
      let mut channel = 0;

      for subframe in &frame.subframes[0..channels] {
        let start  = channel * block_size;
        let end    = (channel + 1) * block_size;
        let output = &mut self.output[start..end];

        subframe::decode(&subframe, output);

        channel += 1;
      }

      frame::decode(frame.header.channel_assignment, &mut self.output);

      self.frame_index += 1;

      Some(&self.output[0..(block_size * channels)])
    }
  }

  fn handle_marker<'a>(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(0);

    match tag!(input, "fLaC") {
      IResult::Done(i, _)    => {
        self.state = ParserState::Metadata;

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle_metadata<'a>(&mut self, input: &'a [u8])
                         -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(1);

    match metadata_parser(input) {
      IResult::Done(i, block) => {
        let is_last = block.is_last;

        if let metadata::Data::StreamInfo(info) = block.data {
          self.info = info;
        } else {
          self.metadata.push(block);
        }

        if is_last {
          self.state = ParserState::Frame;
        }

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle_frame<'a>(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(2);

    match frame_parser(input, &self.info) {
      IResult::Done(i, frame) => {
        self.frames.push(frame);

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle<S: StreamProducer>(&mut self, stream: &mut S)
                               -> Result<(), ErrorKind> {
    stream.parse(|input| {
      match self.state {
        ParserState::Marker   => self.handle_marker(input),
        ParserState::Metadata => self.handle_metadata(input),
        ParserState::Frame    => self.handle_frame(input),
      }
    })
  }
}

/// An iterator over a reference of the decoded FLAC stream.
pub struct Iter<'a, P> where P: 'a + StreamProducer {
  stream: &'a mut Stream
,
  channel: usize,
  block_size: usize,
  sample_index: usize,
  samples_left: u64,
}

impl<'a, P> Iterator for Iter<'a, P> where P: StreamProducer {
  type Item = i32;

  fn next(&mut self) -> Option<Self::Item> {
    if self.sample_index == self.block_size {
      let frame_index = self.stream.frame_index;

      if self.stream.next_frame().is_none() {
        return None;
      } else {
        let frame = &self.stream.frames[frame_index];

        self.sample_index = 0;
        self.block_size   = frame.header.block_size as usize;
      }
    }

    let channels = self.stream.info.channels as usize;
    let index    = self.sample_index + (self.channel * self.block_size);
    let sample   = self.stream.output[index];

    self.channel += 1;

    // Reset current channel
    if self.channel == channels {
      self.channel       = 0;
      self.sample_index += 1;
      self.samples_left -= 1;
    }

    Some(sample)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    let samples_left = self.samples_left as usize;
    let max_value    = usize::max_value() as u64;

    // There is a chance that samples_left will be larger than a usize since
    // it is a u64. Make the upper bound None when it is.
    if self.samples_left > max_value {
      (samples_left, None)
    } else {
      (samples_left, Some(samples_left))
    }
  }
}

impl<'a, P> IntoIterator for &'a mut Stream

 where P: StreamProducer {
  type Item     = i32;
  type IntoIter = Iter<'a, P>;

  fn into_iter(self) -> Self::IntoIter {
    self.iter()
  }
}
<commit_msg>Add generic `StreamProducer` trait to `Stream`<commit_after>use nom;
use nom::{Err, IResult};

use metadata;
use frame;
use subframe;

use metadata::{Metadata, StreamInfo, metadata_parser};
use frame::{frame_parser, Frame};
use utility::{ErrorKind, ByteStream, ReadStream, StreamProducer};

use std::io;
use std::usize;
use std::fs::File;

enum ParserState {
  Marker,
  Metadata,
  Frame,
}

/// FLAC stream that decodes and hold file information.
pub struct Stream
 {
  info: StreamInfo,
  metadata: Vec<Metadata>,
  frames: Vec<Frame>,
  state: ParserState,
  output: Vec<i32>,
  frame_index: usize,
  producer: P,
}

fn parser<'a>(input: &'a [u8], is_start: &mut bool)
              -> IResult<&'a [u8], Metadata> {
  let mut slice = input;

  if *is_start {
    let (i, _) = try_parse!(slice, tag!("fLaC"));

    slice     = i;
    *is_start = false;
  }

  metadata_parser(slice)
}

impl
 Stream
 where P: StreamProducer {
  /// Constructor for the default state of a FLAC stream.
  ///
  /// This doesn't actually decode anything, it just hold the default values
  /// of each field.
  pub fn new() -> Stream {
    Stream {
      info: StreamInfo::new(),
      metadata: Vec::new(),
      frames: Vec::new(),
      state: ParserState::Marker,
      output: Vec::new(),
      frame_index: 0,
    }
  }

  /// Returns information for the current stream.
  pub fn info(&self) -> StreamInfo {
    self.info
  }

  /// Returns a slice of `Metadata`
  ///
  /// This slice excludes `StreamInfo`, which is located in `Stream::info`.
  /// Everything else is related to metadata for the FLAC stream is in the
  /// slice.
  pub fn metadata(&self) -> &[Metadata] {
    &self.metadata
  }

  /// Constructs a decoder with the given file name.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::NotFound` is returned when the given filename isn't found.
  /// * `ErrorKind::InvalidData` is returned when the data within the file
  ///   isn't valid FLAC data.
  pub fn from_file(filename: &str) -> io::Result<Stream> {
    File::open(filename).and_then(|file| {
      let mut producer = ReadStream::new(file);
      let error_str    = format!("parser: couldn't parse the given file {}",
                                 filename);

      Stream::from_stream_producer(&mut producer, &error_str)
    })
  }

  /// Constructs a decoder with the given buffer.
  ///
  /// This constructor assumes that an entire FLAC file is in the buffer.
  ///
  /// # Failures
  ///
  /// * `ErrorKind::InvalidData` is returned when the data within the buffer
  ///   isn't valid FLAC data.
  pub fn from_buffer(buffer: &[u8]) -> io::Result<Stream> {
    let mut producer = ByteStream::new(buffer);
    let error_str    = "parser: couldn't parse the buffer";

    Stream::from_stream_producer(&mut producer, error_str)
  }

  fn from_stream_producer
(producer: &mut P, error_str: &str)
                             -> io::Result<Stream>
   where P: StreamProducer {
    let mut is_error = false;
    let mut stream   = Stream {
      info: StreamInfo::new(),
      metadata: Vec::new(),
      frames: Vec::new(),
      state: ParserState::Marker,
      output: Vec::new(),
      frame_index: 0,
    };

    loop {
      match stream.handle(producer) {
        Ok(_)                      => break,
        Err(ErrorKind::EndOfInput) => break,
        Err(ErrorKind::Continue)   => continue,
        Err(_)                     => {
          is_error = true;

          break;
        }
      }
    }

    if !is_error {
      let channels    = stream.info.channels as usize;
      let block_size  = stream.info.max_block_size as usize;
      let output_size = block_size * channels;

      stream.output.reserve_exact(output_size);

      unsafe { stream.output.set_len(output_size) }

      Ok(stream)
    } else {
      Err(io::Error::new(io::ErrorKind::InvalidData, error_str))
    }
  }

  /// Returns an iterator over the decoded samples.
  pub fn iter(&mut self) -> Iter
 {
    let samples_left = self.info.total_samples;

    Iter {
      stream: self,
      channel: 0,
      block_size: 0,
      sample_index: 0,
      samples_left: samples_left,
    }
  }

  fn next_frame<'a>(&'a mut self) -> Option<&'a [i32]> {
    if self.frames.is_empty() || self.frame_index >= self.frames.len() {
      None
    } else {
      let frame       = &self.frames[self.frame_index];
      let channels    = frame.header.channels as usize;
      let block_size  = frame.header.block_size as usize;
      let mut channel = 0;

      for subframe in &frame.subframes[0..channels] {
        let start  = channel * block_size;
        let end    = (channel + 1) * block_size;
        let output = &mut self.output[start..end];

        subframe::decode(&subframe, output);

        channel += 1;
      }

      frame::decode(frame.header.channel_assignment, &mut self.output);

      self.frame_index += 1;

      Some(&self.output[0..(block_size * channels)])
    }
  }

  fn handle_marker<'a>(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(0);

    match tag!(input, "fLaC") {
      IResult::Done(i, _)    => {
        self.state = ParserState::Metadata;

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle_metadata<'a>(&mut self, input: &'a [u8])
                         -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(1);

    match metadata_parser(input) {
      IResult::Done(i, block) => {
        let is_last = block.is_last;

        if let metadata::Data::StreamInfo(info) = block.data {
          self.info = info;
        } else {
          self.metadata.push(block);
        }

        if is_last {
          self.state = ParserState::Frame;
        }

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle_frame<'a>(&mut self, input: &'a [u8]) -> IResult<&'a [u8], ()> {
    let kind = nom::ErrorKind::Custom(2);

    match frame_parser(input, &self.info) {
      IResult::Done(i, frame) => {
        self.frames.push(frame);

        IResult::Error(Err::Position(kind, i))
      }
      IResult::Error(_)      => IResult::Error(Err::Code(kind)),
      IResult::Incomplete(n) => IResult::Incomplete(n),
    }
  }

  fn handle<S: StreamProducer>(&mut self, stream: &mut S)
                               -> Result<(), ErrorKind> {
    stream.parse(|input| {
      match self.state {
        ParserState::Marker   => self.handle_marker(input),
        ParserState::Metadata => self.handle_metadata(input),
        ParserState::Frame    => self.handle_frame(input),
      }
    })
  }
}

/// An iterator over a reference of the decoded FLAC stream.
pub struct Iter<'a, P> where P: 'a + StreamProducer {
  stream: &'a mut Stream
,
  channel: usize,
  block_size: usize,
  sample_index: usize,
  samples_left: u64,
}

impl<'a, P> Iterator for Iter<'a, P> where P: StreamProducer {
  type Item = i32;

  fn next(&mut self) -> Option<Self::Item> {
    if self.sample_index == self.block_size {
      let frame_index = self.stream.frame_index;

      if self.stream.next_frame().is_none() {
        return None;
      } else {
        let frame = &self.stream.frames[frame_index];

        self.sample_index = 0;
        self.block_size   = frame.header.block_size as usize;
      }
    }

    let channels = self.stream.info.channels as usize;
    let index    = self.sample_index + (self.channel * self.block_size);
    let sample   = self.stream.output[index];

    self.channel += 1;

    // Reset current channel
    if self.channel == channels {
      self.channel       = 0;
      self.sample_index += 1;
      self.samples_left -= 1;
    }

    Some(sample)
  }

  fn size_hint(&self) -> (usize, Option<usize>) {
    let samples_left = self.samples_left as usize;
    let max_value    = usize::max_value() as u64;

    // There is a chance that samples_left will be larger than a usize since
    // it is a u64. Make the upper bound None when it is.
    if self.samples_left > max_value {
      (samples_left, None)
    } else {
      (samples_left, Some(samples_left))
    }
  }
}

impl<'a, P> IntoIterator for &'a mut Stream

 where P: StreamProducer {
  type Item     = i32;
  type IntoIter = Iter<'a, P>;

  fn into_iter(self) -> Self::IntoIter {
    self.iter()
  }
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;
use consts::IEC::Mi;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{initialize, resolve_devices};
use super::blockdevmgr::BlockDevMgr;
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

pub const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
pub const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let mut block_mgr =
            BlockDevMgr::new(try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force)));

        if block_mgr.avail_space() < INITIAL_META_SIZE + INITIAL_DATA_SIZE {
            let short_bytes = *(INITIAL_META_SIZE + INITIAL_DATA_SIZE - block_mgr.avail_space()) *
                              SECTOR_SIZE as u64;
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Initial total usable pool capacity too \
                                                    small by {} bytes",
                                                   short_bytes)));
        }

        let meta_regions = block_mgr.alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name), dm, &meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr.alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &data_regions));
        try!(wipe_sectors(&try!(data_dev.devnode()), Sectors(0), DATA_BLOCK_SIZE));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    pub fn load_state(&self) -> Option<Vec<u8>> {
        self.block_devs.load_state()
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs.save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs.to_save(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<commit_msg>Wiping start of data device should not be needed<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashSet;
use std::collections::hash_map::RandomState;
use std::iter::FromIterator;
use std::path::Path;
use std::path::PathBuf;
use std::vec::Vec;

use devicemapper::consts::SECTOR_SIZE;
use devicemapper::DM;
use devicemapper::{DataBlocks, Sectors};
use devicemapper::LinearDev;
use devicemapper::{ThinPoolDev, ThinPoolStatus, ThinPoolWorkingStatus};
use time::now;
use uuid::Uuid;
use serde_json;

use engine::EngineError;
use engine::EngineResult;
use engine::ErrorEnum;
use engine::Filesystem;
use engine::Pool;
use engine::RenameAction;
use engine::engine::Redundancy;
use engine::strat_engine::blockdev::wipe_sectors;
use consts::IEC::Mi;

use super::super::engine::{FilesystemUuid, HasName, HasUuid};
use super::super::structures::Table;

use super::serde_structs::StratSave;
use super::blockdev::{initialize, resolve_devices};
use super::blockdevmgr::BlockDevMgr;
use super::filesystem::{StratFilesystem, FilesystemStatus};
use super::metadata::MIN_MDA_SECTORS;

pub const DATA_BLOCK_SIZE: Sectors = Sectors(2048);
pub const META_LOWATER: u64 = 512;
pub const DATA_LOWATER: DataBlocks = DataBlocks(512);

pub const INITIAL_META_SIZE: Sectors = Sectors(16 * Mi / SECTOR_SIZE as u64);
pub const INITIAL_DATA_SIZE: Sectors = Sectors(512 * Mi / SECTOR_SIZE as u64);

#[derive(Debug)]
pub struct StratPool {
    name: String,
    pool_uuid: Uuid,
    pub block_devs: BlockDevMgr,
    pub filesystems: Table<StratFilesystem>,
    redundancy: Redundancy,
    thin_pool: ThinPoolDev,
}

impl StratPool {
    /// Initialize a Stratis Pool.
    /// 1. Initialize the block devices specified by paths.
    /// 2. Set up thinpool device to back filesystems.
    pub fn initialize(name: &str,
                      dm: &DM,
                      paths: &[&Path],
                      redundancy: Redundancy,
                      force: bool)
                      -> EngineResult<StratPool> {
        let pool_uuid = Uuid::new_v4();

        let devices = try!(resolve_devices(paths));
        let mut block_mgr =
            BlockDevMgr::new(try!(initialize(&pool_uuid, devices, MIN_MDA_SECTORS, force)));

        if block_mgr.avail_space() < INITIAL_META_SIZE + INITIAL_DATA_SIZE {
            let short_bytes = *(INITIAL_META_SIZE + INITIAL_DATA_SIZE - block_mgr.avail_space()) *
                              SECTOR_SIZE as u64;
            try!(block_mgr.destroy_all());
            return Err(EngineError::Engine(ErrorEnum::Invalid,
                                           format!("Initial total usable pool capacity too \
                                                    small by {} bytes",
                                                   short_bytes)));
        }

        let meta_regions = block_mgr.alloc_space(INITIAL_META_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let meta_dev = try!(LinearDev::new(&format!("stratis_{}_meta", name), dm, &meta_regions));

        // When constructing a thin-pool, Stratis reserves the first N
        // sectors on a block device by creating a linear device with a
        // starting offset. DM writes the super block in the first block.
        // DM requires this first block to be zeros when the meta data for
        // the thin-pool is initially created. If we don't zero the
        // superblock DM issue error messages because it triggers code paths
        // that are trying to re-adopt the device with the attributes that
        // have been passed.
        try!(wipe_sectors(&try!(meta_dev.devnode()), Sectors(0), INITIAL_META_SIZE));

        let data_regions = block_mgr.alloc_space(INITIAL_DATA_SIZE)
            .expect("blockmgr must not fail, already checked for space");
        let data_dev = try!(LinearDev::new(&format!("stratis_{}_data", name), dm, &data_regions));
        let length = try!(data_dev.size()).sectors();

        // TODO Fix hard coded data blocksize and low water mark.
        let thinpool_dev = try!(ThinPoolDev::new(&format!("stratis_{}_thinpool", name),
                                                 dm,
                                                 length,
                                                 DATA_BLOCK_SIZE,
                                                 DataBlocks(256000),
                                                 meta_dev,
                                                 data_dev));

        let mut pool = StratPool {
            name: name.to_owned(),
            pool_uuid: pool_uuid,
            block_devs: block_mgr,
            filesystems: Table::new(),
            redundancy: redundancy,
            thin_pool: thinpool_dev,
        };

        try!(pool.write_metadata());

        Ok(pool)
    }

    /// Return the metadata from the first blockdev with up-to-date, readable
    /// metadata.
    pub fn load_state(&self) -> Option<Vec<u8>> {
        self.block_devs.load_state()
    }

    // TODO: Check current time against global last updated, and use
    // alternate time value if earlier, as described in SWDD
    pub fn write_metadata(&mut self) -> EngineResult<()> {
        let data = try!(serde_json::to_string(&self.to_save()));
        self.block_devs.save_state(&now().to_timespec(), data.as_bytes())
    }

    pub fn to_save(&self) -> StratSave {
        StratSave {
            name: self.name.clone(),
            id: self.pool_uuid.simple().to_string(),
            block_devs: self.block_devs.to_save(),
        }
    }

    pub fn check(&mut self) -> () {
        let dm = DM::new().expect("Could not get DM handle");

        let result = match self.thin_pool.status(&dm) {
            Ok(r) => r,
            Err(_) => {
                error!("Could not get thinpool status");
                // TODO: Take pool offline?
                return;
            }
        };

        match result {
            ThinPoolStatus::Good(wstatus, usage) => {
                match wstatus {
                    ThinPoolWorkingStatus::Good => {}
                    ThinPoolWorkingStatus::ReadOnly => {
                        // TODO: why is pool r/o and how do we get it
                        // rw again?
                    }
                    ThinPoolWorkingStatus::OutOfSpace => {
                        // TODO: Add more space if possible, or
                        // prevent further usage
                        // Should never happen -- we should be extending first!
                    }
                    ThinPoolWorkingStatus::NeedsCheck => {
                        // TODO: Take pool offline?
                        // TODO: run thin_check
                    }
                }

                if usage.used_meta > usage.total_meta - META_LOWATER {
                    // TODO: Extend meta device
                }

                if usage.used_data > usage.total_data - DATA_LOWATER {
                    // TODO: Extend data device
                }
            }
            ThinPoolStatus::Fail => {
                // TODO: Take pool offline?
                // TODO: Run thin_check
            }
        }

        for fs in self.filesystems.iter_mut() {
            match fs.check(&dm) {
                Ok(f_status) => {
                    if let FilesystemStatus::Failed = f_status {
                        // TODO: recover fs? (how?)
                    }
                }
                Err(_e) => error!("fs.check() failed"),
            }
        }
    }
}

impl Pool for StratPool {
    fn create_filesystems<'a, 'b>(&'a mut self,
                                  specs: &[&'b str])
                                  -> EngineResult<Vec<(&'b str, FilesystemUuid)>> {
        let names: HashSet<_, RandomState> = HashSet::from_iter(specs);
        for name in names.iter() {
            if self.filesystems.contains_name(name) {
                return Err(EngineError::Engine(ErrorEnum::AlreadyExists, name.to_string()));
            }
        }

        let mut result = Vec::new();
        for name in names.iter() {
            let uuid = Uuid::new_v4();
            let dm = try!(DM::new());
            let new_filesystem = try!(StratFilesystem::new(uuid, name, &dm, &mut self.thin_pool));
            self.filesystems.insert(new_filesystem);
            result.push((**name, uuid));
        }

        Ok(result)
    }

    fn add_blockdevs(&mut self, paths: &[&Path], force: bool) -> EngineResult<Vec<PathBuf>> {
        let bdev_paths = try!(self.block_devs.add(&self.pool_uuid, paths, force));
        try!(self.write_metadata());
        Ok(bdev_paths)
    }

    fn destroy(self) -> EngineResult<()> {
        let dm = try!(DM::new());
        try!(self.thin_pool.teardown(&dm));
        try!(self.block_devs.destroy_all());

        Ok(())
    }

    fn destroy_filesystems<'a, 'b>(&'a mut self,
                                   fs_uuids: &[&'b FilesystemUuid])
                                   -> EngineResult<Vec<&'b FilesystemUuid>> {
        destroy_filesystems!{self; fs_uuids}
    }

    fn rename_filesystem(&mut self,
                         uuid: &FilesystemUuid,
                         new_name: &str)
                         -> EngineResult<RenameAction> {
        rename_filesystem!{self; uuid; new_name}
    }

    fn rename(&mut self, name: &str) {
        self.name = name.to_owned();
    }

    fn get_filesystem(&mut self, uuid: &FilesystemUuid) -> Option<&mut Filesystem> {
        get_filesystem!(self; uuid)
    }
}

impl HasUuid for StratPool {
    fn uuid(&self) -> &FilesystemUuid {
        &self.pool_uuid
    }
}

impl HasName for StratPool {
    fn name(&self) -> &str {
        &self.name
    }
}
<|endoftext|>