<commit_before>use std::time::Duration;
use plist::Plist;

use Stream;
use Result;
use Error;
use message_type;

/// A Client for usbmuxd.
pub struct Client {
    stream: Stream,
}

impl Client {
    /// Tries to create a new instance of the `Client`.
    pub fn new() -> Result<Self> {
        let mut stream = try!(Stream::connect());
        try!(stream.set_send_tymeout(Some(Duration::new(1, 0))));
        try!(stream.set_receive_timeout(Some(Duration::new(1, 0))));
        Ok(Client {
            stream: stream,
        })
    }

    /// Returns a list of connected devices.
    pub fn devices(&mut self) -> Result<Vec<Device>> {
        let mut plist = try!(self.request(Plist::Dictionary(message_type("ListDevices"))));
        let mut dict = try!(plist.as_dictionary_mut().ok_or(Error::UnexpectedFormat));
        match dict.remove("DeviceList") {
            Some(Plist::Array(array)) => {
                let results = array.into_iter().filter_map(|item| {
                    match item {
                        Plist::Dictionary(mut dict) => {
                            match dict.remove("Properties") {
                                Some(plist) => Device::from_plist(plist),
                                _ => None,
                            }
                        },
                        _ => None,
                    }
                }).collect();
                Ok(results)
            },
            _ => Err(Error::UnexpectedFormat),
        }
    }

    /// Sends a request and receives a response.
    pub fn request(&mut self, message: Plist) -> Result<Plist> {
        try!(self.stream.send(message));
        Ok(try!(self.stream.receive()))
    }
}

/// Represents a device.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Device {
    pub device_id: u32,
    pub product_id: u32,
    pub location_id: u32,
    pub serial_number: String,
}

impl Device {
    /// Creates an instance of `Device` from plist.
    pub fn from_plist(mut plist: Plist) -> Option<Device> {
        let mut dict = try_opt!(plist.as_dictionary_mut());
        Some(Device {
            device_id: try_opt!(dict.get("DeviceID").and_then(Plist::as_integer).map(|x| x as u32)),
            product_id: try_opt!(dict.get("ProductID").and_then(Plist::as_integer).map(|x| x as u32)),
            location_id: try_opt!(dict.get("LocationID").and_then(Plist::as_integer).map(|x| x as u32)),
            serial_number: try_opt!(dict.remove("SerialNumber").and_then(Plist::into_string)),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use expectest::prelude::*;
    use plist::Plist;
    use std::collections::BTreeMap;

    #[test]
    fn test_device_from_plist() {
        let mut map = BTreeMap::new();
        map.insert("ConnectionSpeed".to_owned(), Plist::Integer(480000000));
        map.insert("ConnectionType".to_owned(), Plist::String("USB".to_owned()));
        map.insert("DeviceID".to_owned(), Plist::Integer(3));
        map.insert("LocationID".to_owned(), Plist::Integer(336592896));
        map.insert("ProductID".to_owned(), Plist::Integer(4778));
        map.insert("SerialNumber".to_owned(),Plist::String("fffffffff".to_owned()));

        let device = Device {
            device_id: 3,
            product_id: 4778,
            location_id: 336592896,
            serial_number: "fffffffff".to_owned(),
        };

        expect!(Device::from_plist(Plist::Dictionary(map))).to(be_some().value(device));
    }
}<commit_msg>Improve option handling in the devices method.<commit_after>use std::time::Duration;
use plist::Plist;

use Stream;
use Result;
use Error;
use message_type;

/// A Client for usbmuxd.
pub struct Client {
    stream: Stream,
}

impl Client {
    /// Tries to create a new instance of the `Client`.
    pub fn new() -> Result<Self> {
        let mut stream = try!(Stream::connect());
        try!(stream.set_send_tymeout(Some(Duration::new(1, 0))));
        try!(stream.set_receive_timeout(Some(Duration::new(1, 0))));
        Ok(Client {
            stream: stream,
        })
    }

    /// Returns a list of connected devices.
    pub fn devices(&mut self) -> Result<Vec<Device>> {
        let mut plist = try!(self.request(Plist::Dictionary(message_type("ListDevices"))));
        let mut dict = try!(plist.as_dictionary_mut().ok_or(Error::UnexpectedFormat));
        match dict.remove("DeviceList") {
            Some(Plist::Array(array)) => {
                let results = array.into_iter().filter_map(|mut item| {
                    item.as_dictionary_mut().and_then(|dict| {
                        dict.remove("Properties").and_then(|plist| {
                            Device::from_plist(plist)
                        })
                    })
                }).collect();
                Ok(results)
            },
            _ => Err(Error::UnexpectedFormat),
        }
    }

    /// Sends a request and receives a response.
    pub fn request(&mut self, message: Plist) -> Result<Plist> {
        try!(self.stream.send(message));
        Ok(try!(self.stream.receive()))
    }
}

/// Represents a device.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Device {
    pub device_id: u32,
    pub product_id: u32,
    pub location_id: u32,
    pub serial_number: String,
}

impl Device {
    /// Creates an instance of `Device` from plist.
    pub fn from_plist(mut plist: Plist) -> Option<Device> {
        let mut dict = try_opt!(plist.as_dictionary_mut());
        Some(Device {
            device_id: try_opt!(dict.get("DeviceID").and_then(Plist::as_integer).map(|x| x as u32)),
            product_id: try_opt!(dict.get("ProductID").and_then(Plist::as_integer).map(|x| x as u32)),
            location_id: try_opt!(dict.get("LocationID").and_then(Plist::as_integer).map(|x| x as u32)),
            serial_number: try_opt!(dict.remove("SerialNumber").and_then(Plist::into_string)),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use expectest::prelude::*;
    use plist::Plist;
    use std::collections::BTreeMap;

    #[test]
    fn test_device_from_plist() {
        let mut map = BTreeMap::new();
        map.insert("ConnectionSpeed".to_owned(), Plist::Integer(480000000));
        map.insert("ConnectionType".to_owned(), Plist::String("USB".to_owned()));
        map.insert("DeviceID".to_owned(), Plist::Integer(3));
        map.insert("LocationID".to_owned(), Plist::Integer(336592896));
        map.insert("ProductID".to_owned(), Plist::Integer(4778));
        map.insert("SerialNumber".to_owned(),Plist::String("fffffffff".to_owned()));

        let device = Device {
            device_id: 3,
            product_id: 4778,
            location_id: 336592896,
            serial_number: "fffffffff".to_owned(),
        };

        expect!(Device::from_plist(Plist::Dictionary(map))).to(be_some().value(device));
    }
}
{'fraction_non_alphanumeric': 0.15350877192982457, 'fraction_numerical': 0.015497076023391813, 'mean_word_length': 2.4290726817042607, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 29, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 6, 'word_list_counts': {'cursed_substrings.json': 1, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4574062', 'n_tokens_mistral': 2178, 'n_tokens_neox': 2059, 'n_words': 402}