<commit_before>use std::io::{Error, ErrorKind, Result};
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
use std::time::Duration;
use std::thread;
use std::sync::mpsc;
use url::Url;
use log::*;
use super::message::packet::{Packet, ObserveOption};
use super::message::response::{CoAPResponse, Status};
use super::message::request::CoAPRequest;
use super::message::IsMessage;
use regex::Regex;

const DEFAULT_RECEIVE_TIMEOUT: u64 = 1; // 1s

enum ObserveMessage {
    Terminate,
}

pub struct CoAPClient {
    socket: UdpSocket,
    peer_addr: SocketAddr,
    observe_sender: Option<mpsc::Sender<ObserveMessage>>,
    observe_thread: Option<thread::JoinHandle<()>>,
}

impl CoAPClient {
    /// Create a CoAP client with the specific source and peer address.
    pub fn new_with_specific_source<A: ToSocketAddrs, B: ToSocketAddrs>(
        bind_addr: A,
        peer_addr: B,
    ) -> Result<CoAPClient> {
        peer_addr
            .to_socket_addrs()
            .and_then(|mut iter| match iter.next() {
                Some(paddr) => UdpSocket::bind(bind_addr).and_then(|s| {
                    s.set_read_timeout(Some(Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0)))
                        .and_then(|_| {
                            Ok(CoAPClient {
                                socket: s,
                                peer_addr: paddr,
                                observe_sender: None,
                                observe_thread: None,
                            })
                        })
                }),
                None => Err(Error::new(ErrorKind::Other, "no address")),
            })
    }

    /// Create a CoAP client with the peer address.
    pub fn new<A: ToSocketAddrs>(addr: A) -> Result<CoAPClient> {
        addr.to_socket_addrs()
            .and_then(|mut iter| match iter.next() {
                Some(SocketAddr::V4(_)) => Self::new_with_specific_source("0.0.0.0:0", addr),
                Some(SocketAddr::V6(_)) => Self::new_with_specific_source(":::0", addr),
                None => Err(Error::new(ErrorKind::Other, "no address")),
            })
    }

    /// Execute a get request
    pub fn get(url: &str) -> Result<CoAPResponse> {
        Self::get_with_timeout(url, Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0))
    }

    /// Execute a get request with the coap url and a specific timeout.
    pub fn get_with_timeout(url: &str, timeout: Duration) -> Result<CoAPResponse> {
        let (domain, port, path) = Self::parse_coap_url(url)?;

        let mut packet = CoAPRequest::new();
        packet.set_path(path.as_str());

        let client = Self::new((domain.as_str(), port))?;
        client.send(&packet)?;

        client.set_receive_timeout(Some(timeout))?;
        match client.receive() {
            Ok(receive_packet) => Ok(receive_packet),
            Err(e) => Err(e),
        }
    }

    /// Observe a resource with the handler
    pub fn observe<H: FnMut(Packet) + Send + 'static>(&mut self, resource_path: &str, mut handler: H) -> Result<()> {
        // TODO: support observe multi resources at the same time
        let mut message_id: u16 = 0;
        let mut register_packet = CoAPRequest::new();
        register_packet.set_observe(vec![ObserveOption::Register as u8]);
        register_packet.set_message_id(Self::gen_message_id(&mut message_id));
        register_packet.set_path(resource_path);

        self.send(&register_packet)?;

        self.set_receive_timeout(Some(Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0)))?;
        let response = self.receive()?;
        if *response.get_status() != Status::Content {
            return Err(Error::new(ErrorKind::NotFound, "the resource not found"));
        }

        handler(response.message);

        let socket;
        match self.socket.try_clone() {
            Ok(good_socket) => socket = good_socket,
            Err(_) => return Err(Error::new(ErrorKind::Other, "network error")),
        }
        let peer_addr = self.peer_addr.clone();
        let (observe_sender, observe_receiver) = mpsc::channel();
        let observe_path = String::from(resource_path);

        let observe_thread = thread::spawn(move || loop {
            match Self::receive_from_socket(&socket) {
                Ok(packet) => {
                    let receive_packet = CoAPRequest::from_packet(packet, &peer_addr);

                    handler(receive_packet.message);

                    if let Some(response) = receive_packet.response {
                        let mut packet = Packet::new();
                        packet.header.set_type(response.message.header.get_type());
                        packet.header.set_message_id(response.message.header.get_message_id());
                        packet.set_token(response.message.get_token().clone());

                        match Self::send_with_socket(&socket, &peer_addr, &packet) {
                            Ok(_) => (),
                            Err(e) => {
                                warn!("reply ack failed {}", e)
                            }
                        }
                    }
                },
                Err(e) => {
                    match e.kind() {
                        ErrorKind::WouldBlock => (),                          // timeout
                        _ => warn!("observe failed {:?}", e),
                    }
                },
            };

            match observe_receiver.try_recv() {
                Ok(ObserveMessage::Terminate) => {
                    let mut deregister_packet = CoAPRequest::new();
                    deregister_packet.set_message_id(Self::gen_message_id(&mut message_id));
                    deregister_packet.set_observe(vec![ObserveOption::Deregister as u8]);
                    deregister_packet.set_path(observe_path.as_str());

                    Self::send_with_socket(&socket, &peer_addr, &deregister_packet.message).unwrap();
                    Self::receive_from_socket(&socket).unwrap();
                    break;
                },
                _ => continue,
            }
        });
        self.observe_sender = Some(observe_sender);
        self.observe_thread = Some(observe_thread);

        return Ok(());
    }

    /// Stop observing
    pub fn unobserve(&mut self) {
        match self.observe_sender.take() {
            Some(ref sender) => {
                sender.send(ObserveMessage::Terminate).unwrap();

                self.observe_thread.take().map(|g| g.join().unwrap());
            }
            _ => {}
        }
    }

    /// Execute a request.
    pub fn send(&self, request: &CoAPRequest) -> Result<()> {
        Self::send_with_socket(&self.socket, &self.peer_addr, &request.message)
    }

    /// Receive a response.
    pub fn receive(&self) -> Result<CoAPResponse> {
        let packet = Self::receive_from_socket(&self.socket)?;
        Ok(CoAPResponse { message: packet })
    }

    /// Set the receive timeout.
    pub fn set_receive_timeout(&self, dur: Option<Duration>) -> Result<()> {
        self.socket.set_read_timeout(dur)
    }

    fn send_with_socket(socket: &UdpSocket, peer_addr: &SocketAddr, message: &Packet) -> Result<()> {
        match message.to_bytes() {
            Ok(bytes) => {
                let size = socket.send_to(&bytes[..], peer_addr)?;
                if size == bytes.len() {
                    Ok(())
                } else {
                    Err(Error::new(ErrorKind::Other, "send length error"))
                }
            }
            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error")),
        }
    }

    fn receive_from_socket(socket: &UdpSocket) -> Result<Packet> {
        let mut buf = [0; 1500];

        let (nread, _src) = socket.recv_from(&mut buf)?;
        match Packet::from_bytes(&buf[..nread]) {
            Ok(packet) => Ok(packet),
            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error")),
        }
    }

    fn parse_coap_url(url: &str) -> Result<(String, u16, String)> {
        let url_params = match Url::parse(url) {
            Ok(url_params) => url_params,
            Err(_) => return Err(Error::new(ErrorKind::InvalidInput, "url error")),
        };

        let host = match url_params.host_str() {
            Some("") => return Err(Error::new(ErrorKind::InvalidInput, "host error")),
            Some(h) => h,
            None => return Err(Error::new(ErrorKind::InvalidInput, "host error")),
        };
        let host = Regex::new(r"^\[(.*?)]$").unwrap().replace(&host, "$1").to_string();

        let port = match url_params.port() {
            Some(p) => p,
            None => 5683,
        };

        let path = url_params.path().to_string();

        return Ok((host.to_string(), port, path));
    }

    fn gen_message_id(message_id: &mut u16) -> u16 {
        (*message_id) += 1;
        return *message_id;
    }
}

impl Drop for CoAPClient {
    fn drop(&mut self) {
        self.unobserve();
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::*;
    use std::time::Duration;
    use std::io::ErrorKind;
    use tokio::runtime::current_thread::Runtime;

    #[test]
    fn test_parse_coap_url_good_url() {
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1:5683").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[::1]").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[::1]:5683").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[bbbb::9329:f033:f558:7418]").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[bbbb::9329:f033:f558:7418]:5683").is_ok());
    }

    #[test]
    fn test_parse_coap_url_bad_url() {
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1:65536").is_err());
        assert!(CoAPClient::parse_coap_url("coap://").is_err());
        assert!(CoAPClient::parse_coap_url("coap://:5683").is_err());
        assert!(CoAPClient::parse_coap_url("127.0.0.1").is_err());
    }

    fn request_handler(_: CoAPRequest) -> Option<CoAPResponse> {
        None
    }

    #[test]
    fn test_get() {
        let mut server = Server::new("127.0.0.1:0").unwrap();
        let server_port = server.socket_addr().unwrap().port();
        thread::spawn(move || {
            Runtime::new().unwrap().block_on(async move {
                server.run(request_handler).await.unwrap();
            })
        });

        let error = CoAPClient::get(&format!("coap://127.0.0.1:{}/Rust", server_port))
            .unwrap_err();
        if cfg!(windows) {
            assert_eq!(error.kind(), ErrorKind::TimedOut);
        } else {
            assert_eq!(error.kind(), ErrorKind::WouldBlock);
        }
    }

    #[test]
    fn test_get_timeout() {
        let mut server = Server::new("127.0.0.1:0").unwrap();
        let server_port = server.socket_addr().unwrap().port();
        thread::spawn(move || {
            Runtime::new().unwrap().block_on(async move {
                server.run(request_handler).await.unwrap();
            })
        });

        let error = CoAPClient::get_with_timeout(&format!("coap://127.0.0.1:{}/Rust", server_port), Duration::new(1, 0))
            .unwrap_err();
        if cfg!(windows) {
            assert_eq!(error.kind(), ErrorKind::TimedOut);
        } else {
            assert_eq!(error.kind(), ErrorKind::WouldBlock);
        }
    }
}
<commit_msg>fix test case with coap.me<commit_after>use std::io::{Error, ErrorKind, Result};
use std::net::{SocketAddr, ToSocketAddrs, UdpSocket};
use std::time::Duration;
use std::thread;
use std::sync::mpsc;
use url::Url;
use log::*;
use super::message::packet::{Packet, ObserveOption};
use super::message::response::{CoAPResponse, Status};
use super::message::request::CoAPRequest;
use super::message::IsMessage;
use regex::Regex;

const DEFAULT_RECEIVE_TIMEOUT: u64 = 1; // 1s

enum ObserveMessage {
    Terminate,
}

pub struct CoAPClient {
    socket: UdpSocket,
    peer_addr: SocketAddr,
    observe_sender: Option<mpsc::Sender<ObserveMessage>>,
    observe_thread: Option<thread::JoinHandle<()>>,
}

impl CoAPClient {
    /// Create a CoAP client with the specific source and peer address.
    pub fn new_with_specific_source<A: ToSocketAddrs, B: ToSocketAddrs>(
        bind_addr: A,
        peer_addr: B,
    ) -> Result<CoAPClient> {
        peer_addr
            .to_socket_addrs()
            .and_then(|mut iter| match iter.next() {
                Some(paddr) => UdpSocket::bind(bind_addr).and_then(|s| {
                    s.set_read_timeout(Some(Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0)))
                        .and_then(|_| {
                            Ok(CoAPClient {
                                socket: s,
                                peer_addr: paddr,
                                observe_sender: None,
                                observe_thread: None,
                            })
                        })
                }),
                None => Err(Error::new(ErrorKind::Other, "no address")),
            })
    }

    /// Create a CoAP client with the peer address.
    pub fn new<A: ToSocketAddrs>(addr: A) -> Result<CoAPClient> {
        addr.to_socket_addrs()
            .and_then(|mut iter| match iter.next() {
                Some(SocketAddr::V4(_)) => Self::new_with_specific_source("0.0.0.0:0", addr),
                Some(SocketAddr::V6(_)) => Self::new_with_specific_source(":::0", addr),
                None => Err(Error::new(ErrorKind::Other, "no address")),
            })
    }

    /// Execute a get request
    pub fn get(url: &str) -> Result<CoAPResponse> {
        Self::get_with_timeout(url, Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0))
    }

    /// Execute a get request with the coap url and a specific timeout.
    pub fn get_with_timeout(url: &str, timeout: Duration) -> Result<CoAPResponse> {
        let (domain, port, path) = Self::parse_coap_url(url)?;

        let mut packet = CoAPRequest::new();
        packet.set_path(path.as_str());

        let client = Self::new((domain.as_str(), port))?;
        client.send(&packet)?;

        client.set_receive_timeout(Some(timeout))?;
        match client.receive() {
            Ok(receive_packet) => Ok(receive_packet),
            Err(e) => Err(e),
        }
    }

    /// Observe a resource with the handler
    pub fn observe<H: FnMut(Packet) + Send + 'static>(&mut self, resource_path: &str, mut handler: H) -> Result<()> {
        // TODO: support observe multi resources at the same time
        let mut message_id: u16 = 0;
        let mut register_packet = CoAPRequest::new();
        register_packet.set_observe(vec![ObserveOption::Register as u8]);
        register_packet.set_message_id(Self::gen_message_id(&mut message_id));
        register_packet.set_path(resource_path);

        self.send(&register_packet)?;

        self.set_receive_timeout(Some(Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0)))?;
        let response = self.receive()?;
        if *response.get_status() != Status::Content {
            return Err(Error::new(ErrorKind::NotFound, "the resource not found"));
        }

        handler(response.message);

        let socket;
        match self.socket.try_clone() {
            Ok(good_socket) => socket = good_socket,
            Err(_) => return Err(Error::new(ErrorKind::Other, "network error")),
        }
        let peer_addr = self.peer_addr.clone();
        let (observe_sender, observe_receiver) = mpsc::channel();
        let observe_path = String::from(resource_path);

        let observe_thread = thread::spawn(move || loop {
            match Self::receive_from_socket(&socket) {
                Ok(packet) => {
                    let receive_packet = CoAPRequest::from_packet(packet, &peer_addr);

                    handler(receive_packet.message);

                    if let Some(response) = receive_packet.response {
                        let mut packet = Packet::new();
                        packet.header.set_type(response.message.header.get_type());
                        packet.header.set_message_id(response.message.header.get_message_id());
                        packet.set_token(response.message.get_token().clone());

                        match Self::send_with_socket(&socket, &peer_addr, &packet) {
                            Ok(_) => (),
                            Err(e) => {
                                warn!("reply ack failed {}", e)
                            }
                        }
                    }
                },
                Err(e) => {
                    match e.kind() {
                        ErrorKind::WouldBlock => (),                          // timeout
                        _ => warn!("observe failed {:?}", e),
                    }
                },
            };

            match observe_receiver.try_recv() {
                Ok(ObserveMessage::Terminate) => {
                    let mut deregister_packet = CoAPRequest::new();
                    deregister_packet.set_message_id(Self::gen_message_id(&mut message_id));
                    deregister_packet.set_observe(vec![ObserveOption::Deregister as u8]);
                    deregister_packet.set_path(observe_path.as_str());

                    Self::send_with_socket(&socket, &peer_addr, &deregister_packet.message).unwrap();
                    Self::receive_from_socket(&socket).unwrap();
                    break;
                },
                _ => continue,
            }
        });
        self.observe_sender = Some(observe_sender);
        self.observe_thread = Some(observe_thread);

        return Ok(());
    }

    /// Stop observing
    pub fn unobserve(&mut self) {
        match self.observe_sender.take() {
            Some(ref sender) => {
                sender.send(ObserveMessage::Terminate).unwrap();

                self.observe_thread.take().map(|g| g.join().unwrap());
            }
            _ => {}
        }
    }

    /// Execute a request.
    pub fn send(&self, request: &CoAPRequest) -> Result<()> {
        Self::send_with_socket(&self.socket, &self.peer_addr, &request.message)
    }

    /// Receive a response.
    pub fn receive(&self) -> Result<CoAPResponse> {
        let packet = Self::receive_from_socket(&self.socket)?;
        Ok(CoAPResponse { message: packet })
    }

    /// Set the receive timeout.
    pub fn set_receive_timeout(&self, dur: Option<Duration>) -> Result<()> {
        self.socket.set_read_timeout(dur)
    }

    fn send_with_socket(socket: &UdpSocket, peer_addr: &SocketAddr, message: &Packet) -> Result<()> {
        match message.to_bytes() {
            Ok(bytes) => {
                let size = socket.send_to(&bytes[..], peer_addr)?;
                if size == bytes.len() {
                    Ok(())
                } else {
                    Err(Error::new(ErrorKind::Other, "send length error"))
                }
            }
            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error")),
        }
    }

    fn receive_from_socket(socket: &UdpSocket) -> Result<Packet> {
        let mut buf = [0; 1500];

        let (nread, _src) = socket.recv_from(&mut buf)?;
        match Packet::from_bytes(&buf[..nread]) {
            Ok(packet) => Ok(packet),
            Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error")),
        }
    }

    fn parse_coap_url(url: &str) -> Result<(String, u16, String)> {
        let url_params = match Url::parse(url) {
            Ok(url_params) => url_params,
            Err(_) => return Err(Error::new(ErrorKind::InvalidInput, "url error")),
        };

        let host = match url_params.host_str() {
            Some("") => return Err(Error::new(ErrorKind::InvalidInput, "host error")),
            Some(h) => h,
            None => return Err(Error::new(ErrorKind::InvalidInput, "host error")),
        };
        let host = Regex::new(r"^\[(.*?)]$").unwrap().replace(&host, "$1").to_string();

        let port = match url_params.port() {
            Some(p) => p,
            None => 5683,
        };

        let path = url_params.path().to_string();

        return Ok((host.to_string(), port, path));
    }

    fn gen_message_id(message_id: &mut u16) -> u16 {
        (*message_id) += 1;
        return *message_id;
    }
}

impl Drop for CoAPClient {
    fn drop(&mut self) {
        self.unobserve();
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use super::super::*;
    use std::time::Duration;
    use std::io::ErrorKind;
    use tokio::runtime::current_thread::Runtime;

    #[test]
    fn test_parse_coap_url_good_url() {
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1:5683").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[::1]").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[::1]:5683").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[bbbb::9329:f033:f558:7418]").is_ok());
        assert!(CoAPClient::parse_coap_url("coap://[bbbb::9329:f033:f558:7418]:5683").is_ok());
    }

    #[test]
    fn test_parse_coap_url_bad_url() {
        assert!(CoAPClient::parse_coap_url("coap://127.0.0.1:65536").is_err());
        assert!(CoAPClient::parse_coap_url("coap://").is_err());
        assert!(CoAPClient::parse_coap_url("coap://:5683").is_err());
        assert!(CoAPClient::parse_coap_url("127.0.0.1").is_err());
    }

    fn request_handler(_: CoAPRequest) -> Option<CoAPResponse> {
        None
    }

    #[test]
    fn test_get() {
        let resp = CoAPClient::get("coap://coap.me:5683/hello")
            .unwrap();
        assert_eq!(resp.message.payload, b"world".to_vec());
    }

    #[test]
    fn test_get_timeout() {
        let mut server = Server::new("127.0.0.1:0").unwrap();
        let server_port = server.socket_addr().unwrap().port();
        thread::spawn(move || {
            Runtime::new().unwrap().block_on(async move {
                server.run(request_handler).await.unwrap();
            })
        });

        let error = CoAPClient::get_with_timeout(&format!("coap://127.0.0.1:{}/Rust", server_port), Duration::new(1, 0))
            .unwrap_err();
        if cfg!(windows) {
            assert_eq!(error.kind(), ErrorKind::TimedOut);
        } else {
            assert_eq!(error.kind(), ErrorKind::WouldBlock);
        }
    }
}
<|endoftext|>


<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));
    }
}<|endoftext|>


<commit_before>use Endpoint;
use error;
use PageableEndpoint;
use Pager;
use reqwest;
use reqwest::StatusCode;
use reqwest::header;
use serde_json;
use serde_urlencoded;
use std;
use std::env;
use std::fmt;
use std::io::Read;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

#[derive(Debug, Clone)]
pub struct ClientConfig {
	pub cooldown: time::Duration,
}

impl Default for ClientConfig {
	/// Default ClientConfig:
	/// - No cooldown

	fn default() -> Self {
		Self {
			cooldown: time::Duration::new(0, 0),
		}
	}
}

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
	config: ClientConfig,
	last_request: Arc<Mutex<Option<time::Instant>>>,
}

#[allow(unused)]
impl Client {
	/// Create a new api client with a given api key
	pub fn new(api_key: String) -> Self {
		let crates_name = env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

		let crates_version = &env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
			config: ClientConfig::default(),
			last_request: Arc::new(Mutex::new(None)),
		}
	}

	/// load api key from an environment variable
	pub fn with_env_key(env_key: &str) -> Option<Client> {
		match env::var_os(env_key) {
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	pub fn cooldown_time(&mut self, time: time::Duration) {
		self.config.cooldown = time;
	}

	/// execute a request that satisfies [`Endpoint`](../trait.Endpoint.html)
	pub fn execute<T>(&mut self, mut endpoint: T) -> Result<T::ResponseType, error::Error>
	where
		T: super::Endpoint + Clone + fmt::Debug,
	{
		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(reqwest::Method::Post, uristring.parse().unwrap());

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.clone()) {
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		debug!("creating reqwest client");
		let client = reqwest::Client::new();

		self.waiting_for_cooldown();
		debug!("sending request");

		let response = client.execute(http_req);
		debug!("response received");
		self.reset_cooldown();

		// This section needs some rewriting. maybe... later
		match response {
			Err(e) => return Err(error::Error::Unknown),
			Ok(mut res) => {
				debug!("http response code: {}", res.status());

				if res.status() != StatusCode::Ok {
					return Err(error::Error::Http)
				}

				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				debug!("http response length: {}kb", json_string.len() * 1000);

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(&json_string) {
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(error::Error::Api(error::api::Api::new(
								r.code.unwrap(),
								r.message,
							)))
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}

	fn remaining_cooldown_time(&self) -> time::Duration {
		match *self.last_request.lock().unwrap() {
			None => self.config.cooldown,
			Some(time) => time.elapsed().clone(),
		}
	}

	fn reset_cooldown(&mut self) {
		*self.last_request.lock().unwrap() = Some(time::Instant::now());
	}

	// vlocking till we can create a new request
	fn waiting_for_cooldown(&mut self) {
		let conf_cooldown = self.config.cooldown;
		let since_last = self.remaining_cooldown_time();

		debug!(
			"configurated cooldown {:2}.{}s",
			conf_cooldown.as_secs(),
			conf_cooldown.subsec_nanos()
		);
		debug!(
			"last request {:2}.{}s ago",
			since_last.as_secs(),
			since_last.subsec_nanos()
		);

		if since_last >= conf_cooldown {
			return;
		}

		let wait = self.config.cooldown - self.remaining_cooldown_time();
		debug!(
			"waiting for cooldown: {:2}.{}s",
			wait.as_secs(),
			wait.subsec_nanos()
		);

		thread::sleep(wait);
	}

	pub fn pager<T>(self, mut endpoint: T) -> Pager<T>
	where
		T: Endpoint + PageableEndpoint + Clone + fmt::Debug,
		<T as Endpoint>::ResponseType: IntoIterator + Clone + fmt::Debug,
		<<T as Endpoint>::ResponseType as std::iter::IntoIterator>::Item: Clone + fmt::Debug,
	{
		Pager::new(self, endpoint, None, None)
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<commit_msg>fix stupid error...<commit_after>use Endpoint;
use error;
use PageableEndpoint;
use Pager;
use reqwest;
use reqwest::StatusCode;
use reqwest::header;
use serde_json;
use serde_urlencoded;
use std;
use std::env;
use std::fmt;
use std::io::Read;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::time;

const API_BASE_PATH: &str = "http://proxer.me/api/v1/";

#[derive(Debug, Clone)]
pub struct ClientConfig {
	pub cooldown: time::Duration,
}

impl Default for ClientConfig {
	/// Default ClientConfig:
	/// - No cooldown

	fn default() -> Self {
		Self {
			cooldown: time::Duration::new(0, 0),
		}
	}
}

/// Client that holds the api key and sends requests
#[derive(Debug, Clone)]
pub struct Client {
	api_key: String,
	base_uri: String,
	user_agent: String,
	config: ClientConfig,
	last_request: Arc<Mutex<Option<time::Instant>>>,
}

#[allow(unused)]
impl Client {
	/// Create a new api client with a given api key
	pub fn new(api_key: String) -> Self {
		let crates_name = env::var("CARGO_PKG_NAME").unwrap_or("unknown".to_string());

		let crates_version = &env::var("CARGO_PKG_VERSION").unwrap_or("unknown".to_string());
		let ua = format!("libproxer-rust({}/v{})", crates_name, crates_version);

		Self {
			api_key: api_key,
			base_uri: API_BASE_PATH.to_string(),
			user_agent: ua,
			config: ClientConfig::default(),
			last_request: Arc::new(Mutex::new(None)),
		}
	}

	/// load api key from an environment variable
	pub fn with_env_key(env_key: &str) -> Option<Client> {
		match env::var_os(env_key) {
			Some(r) => Some(Client::new(r.into_string().unwrap())),
			None => None,
		}
	}

	pub fn cooldown_time(&mut self, time: time::Duration) {
		self.config.cooldown = time;
	}

	/// execute a request that satisfies [`Endpoint`](../trait.Endpoint.html)
	pub fn execute<T>(&mut self, mut endpoint: T) -> Result<T::ResponseType, error::Error>
	where
		T: super::Endpoint + Clone + fmt::Debug,
	{
		let uristring = self.base_uri.to_string() + T::URL;

		header!{(ProxerApiKey, "Proxer-Api-Key") => [String]}

		let mut headers = reqwest::header::Headers::new();

		headers.set(header::UserAgent::new(self.user_agent.to_string()));
		headers.set(header::Host::new("proxer.me", None));
		headers.set(header::ContentType::form_url_encoded());
		headers.set(ProxerApiKey(self.api_key.clone()));

		let mut http_req = reqwest::Request::new(reqwest::Method::Post, uristring.parse().unwrap());

		// add our headers to the request
		http_req.headers_mut().extend(headers.iter());

		match serde_urlencoded::to_string(endpoint.clone()) {
			Ok(body) => {
				debug!("post data: {}", body);
				*http_req.body_mut() = Some(body.into());
			}
			Err(err) => panic!("can't serialize form parameters"),
		}

		debug!("creating reqwest client");
		let client = reqwest::Client::new();

		self.waiting_for_cooldown();
		debug!("sending request");

		let response = client.execute(http_req);
		debug!("response received");
		self.reset_cooldown();

		// This section needs some rewriting. maybe... later
		match response {
			Err(e) => return Err(error::Error::Unknown),
			Ok(mut res) => {
				debug!("http response code: {}", res.status());

				if res.status() != StatusCode::Ok {
					return Err(error::Error::Http)
				}

				let mut json_string = String::new();
				res.read_to_string(&mut json_string);

				debug!("http response length: {}kb", json_string.len() / 1000);

				match serde_json::from_str::<ApiResponse<T::ResponseType>>(&json_string) {
					Err(e) => return Err(error::Error::Json(e)),
					Ok(r) => {
						if r.error != 0 {
							Err(error::Error::Api(error::api::Api::new(
								r.code.unwrap(),
								r.message,
							)))
						}
						else {
							Ok(r.data.unwrap())
						}
					}
				}
			}
		}
	}

	fn remaining_cooldown_time(&self) -> time::Duration {
		match *self.last_request.lock().unwrap() {
			None => self.config.cooldown,
			Some(time) => time.elapsed().clone(),
		}
	}

	fn reset_cooldown(&mut self) {
		*self.last_request.lock().unwrap() = Some(time::Instant::now());
	}

	// vlocking till we can create a new request
	fn waiting_for_cooldown(&mut self) {
		let conf_cooldown = self.config.cooldown;
		let since_last = self.remaining_cooldown_time();

		debug!(
			"configurated cooldown {:2}.{}s",
			conf_cooldown.as_secs(),
			conf_cooldown.subsec_nanos()
		);
		debug!(
			"last request {:2}.{}s ago",
			since_last.as_secs(),
			since_last.subsec_nanos()
		);

		if since_last >= conf_cooldown {
			return;
		}

		let wait = self.config.cooldown - self.remaining_cooldown_time();
		debug!(
			"waiting for cooldown: {:2}.{}s",
			wait.as_secs(),
			wait.subsec_nanos()
		);

		thread::sleep(wait);
	}

	pub fn pager<T>(self, mut endpoint: T) -> Pager<T>
	where
		T: Endpoint + PageableEndpoint + Clone + fmt::Debug,
		<T as Endpoint>::ResponseType: IntoIterator + Clone + fmt::Debug,
		<<T as Endpoint>::ResponseType as std::iter::IntoIterator>::Item: Clone + fmt::Debug,
	{
		Pager::new(self, endpoint, None, None)
	}
}

/// responses are parsed into ApiRequest
#[derive(Debug, Clone, Deserialize)]
pub struct ApiResponse<T> {
	/// api error
	pub error: i64,
	/// api message
	pub message: String,
	/// actual data
	pub data: Option<T>,
	/// optional error code
	/// in case of an error this contains the error code
	pub code: Option<i64>,
}
<|endoftext|>


<commit_before>#[cfg(feature = "getopts")] extern crate getopts;
#[cfg(feature = "getopts")] extern crate markov;

#[cfg(all(feature = "getopts", not(test)))] use std::env::args;
#[cfg(feature = "getopts")] use std::path::Path;

#[cfg(feature = "getopts")] use getopts::Options;
#[cfg(feature = "getopts")] use markov::Chain;

#[cfg(all(feature = "getopts", not(test)))]
fn main() {
    markov_gen(args().collect()).iter().map(|s| println!("{}", s)).count();
}

#[cfg(all(not(feature = "getopts"), not(test)))]
fn main() {
    println!("markgen must be compiled with getopts enabled.")
}

/// Generates a number of strings using a markov chain on specified inputs. This is designed
/// primarily for command-line usage. The arguments are expected to be paths to files to be fed
/// into the chain. Additionally, the argument `-n #` is supported to specify the number of phrases
/// to be generated. This number must be a positive, non-zero integer. `-o #` is also supported to
/// specify the order of Markov chain to be used. Note `-o` must be specified before any file
/// names and must also be a positive, non-zero integer.
///
/// Some valid usages of this function:
/// `markov_gen(vec!["test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "2".to_owned(), "test".to_owned()])`
///
/// Some invalid usages of this function:
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "0".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-o".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "0".to_owned()], "test".to_owned())`
#[cfg(feature = "getopts")]
fn markov_gen(args: Vec<String>) -> Vec<String>{
    let mut opts = Options::new();
    opts.optopt("n", "count", "set the number of phrases to generate", "COUNT");
    opts.optopt("o", "order", "set the order of the Markov chain", "ORDER");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!(e.to_string()),
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE1 [FILE2 FILE3 ...] [options]", args[0]);
        print!("{}", opts.usage(&brief));
        Vec::new()
    } else {
        let count = match matches.opt_str("n") {
            Some(arg) => match arg.parse() {
                Ok(n) if n > 0 => n,
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            },
            None => 1
        };
        let mut chain = match matches.opt_str("o").map(|arg| arg.parse()) {
            Some(Ok(n)) if n > 0 => Chain::of_order(n),
            None => Chain::new(),
            Some(_) => panic!(
                "Expected positive integer argument to -n, found {}.", matches.opt_str("o").unwrap()
            ),
        };
        for path in matches.free.iter() {
            chain.feed_file(Path::new(&path)).unwrap();
        }
        if chain.is_empty() { panic!("No files were fed into the chain.") }
        chain.str_iter_for(count).collect()
    }
}

#[cfg(all(feature = "getopts", test))]
mod test {
    use super::markov_gen;
    use std::borrow::ToOwned;

    #[test]
    fn gen_default() {
        assert_eq!(markov_gen(vec!["prog".to_owned(), "test".to_owned()]).len(), 1)
    }

    #[test]
    fn gen_number_after() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "test".to_owned(), "-n".to_owned(), "3".to_owned()]
        ).len(), 3)
    }

    #[test]
    fn gen_before_number() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "-n".to_owned(), "3".to_owned(), "test".to_owned()]
        ).len(), 3)
    }

    #[test]
    #[should_panic(expected = "No files were fed into the chain.")]
    fn gen_invalid_no_files() {
        markov_gen(vec!["prog".to_owned(), "-n".to_owned(), "3".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found 0.")]
    fn gen_invalid_n_arg_zero() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "0".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found test.")]
    fn gen_invalid_n_arg_string() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "test".to_owned()]);
    }
}
<commit_msg>Added the ability to save out a Markov chain with markgen.<commit_after>#[cfg(feature = "getopts")] extern crate getopts;
#[cfg(feature = "getopts")] extern crate markov;

#[cfg(all(feature = "getopts", not(test)))] use std::env::args;
#[cfg(feature = "getopts")] use std::path::Path;

#[cfg(feature = "getopts")] use getopts::Options;
#[cfg(feature = "getopts")] use markov::Chain;

#[cfg(all(feature = "getopts", not(test)))]
fn main() {
    markov_gen(args().collect()).iter().map(|s| println!("{}", s)).count();
}

#[cfg(all(not(feature = "getopts"), not(test)))]
fn main() {
    println!("markgen must be compiled with getopts enabled.")
}

/// Generates a number of strings using a markov chain on specified inputs. This is designed
/// primarily for command-line usage. The arguments are expected to be paths to files to be fed
/// into the chain. Additionally, the argument `-n #` is supported to specify the number of phrases
/// to be generated. This number must be a positive, non-zero integer. `-o #` is also supported to
/// specify the order of Markov chain to be used. Note `-o` must be specified before any file
/// names and must also be a positive, non-zero integer.
///
/// Some valid usages of this function:
/// `markov_gen(vec!["test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "2".to_owned(), "test".to_owned()])`
///
/// Some invalid usages of this function:
/// `markov_gen(vec!["-n".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "0".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-n".to_owned(), "test".to_owned()])`
/// `markov_gen(vec!["test".to_owned(), "-o".to_owned(), "3".to_owned()])`
/// `markov_gen(vec!["-o".to_owned(), "0".to_owned()], "test".to_owned())`
#[cfg(feature = "getopts")]
fn markov_gen(args: Vec<String>) -> Vec<String>{
    let mut opts = Options::new();
    opts.optopt("n", "count", "set the number of phrases to generate", "COUNT");
    opts.optopt("o", "order", "set the order of the Markov chain", "ORDER");
    opts.optopt("s", "save", "save the Markov chain to the specified path", "PATH");
    opts.optflag("h", "help", "print this help menu");
    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!(e.to_string()),
    };
    if matches.opt_present("h") {
        let brief = format!("Usage: {} FILE1 [FILE2 FILE3 ...] [options]", args[0]);
        print!("{}", opts.usage(&brief));
        Vec::new()
    } else {
        let count = match matches.opt_str("n") {
            Some(arg) => match arg.parse() {
                Ok(n) if n > 0 => n,
                _ => panic!("Expected positive integer argument to -n, found {}.", &arg),
            },
            None => 1
        };
        let mut chain = match matches.opt_str("o").map(|arg| arg.parse()) {
            Some(Ok(n)) if n > 0 => Chain::of_order(n),
            None => Chain::new(),
            Some(_) => panic!(
                "Expected positive integer argument to -n, found {}.", matches.opt_str("o").unwrap()
            ),
        };
        for path in matches.free.iter() {
            chain.feed_file(Path::new(&path)).unwrap();
        }
        if chain.is_empty() { panic!("No files were fed into the chain.") }
        if let Some(path) = matches.opt_str("s") {
            chain.save(path).unwrap();
        }
        chain.str_iter_for(count).collect()
    }
}

#[cfg(all(feature = "getopts", test))]
mod test {
    use super::markov_gen;
    use std::borrow::ToOwned;

    #[test]
    fn gen_default() {
        assert_eq!(markov_gen(vec!["prog".to_owned(), "test".to_owned()]).len(), 1)
    }

    #[test]
    fn gen_number_after() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "test".to_owned(), "-n".to_owned(), "3".to_owned()]
        ).len(), 3)
    }

    #[test]
    fn gen_before_number() {
        assert_eq!(markov_gen(
            vec!["markgen".to_owned(), "-n".to_owned(), "3".to_owned(), "test".to_owned()]
        ).len(), 3)
    }

    #[test]
    #[should_panic(expected = "No files were fed into the chain.")]
    fn gen_invalid_no_files() {
        markov_gen(vec!["prog".to_owned(), "-n".to_owned(), "3".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found 0.")]
    fn gen_invalid_n_arg_zero() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "0".to_owned()]);
    }

    #[test]
    #[should_panic(expected = "Expected positive integer argument to -n, found test.")]
    fn gen_invalid_n_arg_string() {
        markov_gen(vec!["prog".to_owned(), "test".to_owned(), "-n".to_owned(), "test".to_owned()]);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{WriteMode, run};
use rustfmt::config::Config;

use std::env;
use std::fs::{self, File};
use std::io::{self, Read};
use std::path::PathBuf;

use getopts::Options;

// Try to find a project file in the current directory and its parents.
fn lookup_project_file() -> io::Result<PathBuf> {
    let mut current = try!(env::current_dir());
    loop {
        let config_file = current.join("rustfmt.toml");
        if fs::metadata(&config_file).is_ok() {
            return Ok(config_file);
        } else {
            current = match current.parent() {
                // if the current directory has no parent, we're done searching
                None => return Err(io::Error::new(io::ErrorKind::NotFound, "config not found")),
                Some(path) => path.to_path_buf(),
            };
        }
    }
}

// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file() -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file());
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn execute() -> i32 {
    let (file, write_mode) = match determine_params(std::env::args().skip(1)) {
        Some(params) => params,
        None => return 1,
    };

    let config = match lookup_and_read_project_file() {
        Ok((path, toml)) => {
            println!("Project config file: {}", path.display());
            Config::from_toml(&toml)
        }
        Err(_) => Default::default(),
    };

    run(&file, write_mode, &config);
    0
}

fn main() {
    use std::io::Write;
    let _ = env_logger::init();

    let exit_code = execute();
    // Make sure standard output is flushed before we exit
    std::io::stdout().flush().unwrap();
    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>",
                         reason,
                         env::current_exe().unwrap().display());
    println!("{}", opts.usage(&reason));
    Config::print_docs();
}

fn determine_params<I>(args: I) -> Option<(PathBuf, WriteMode)>
    where I: Iterator<Item = String>
{
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optopt("",
                "write-mode",
                "mode to write in",
                "[replace|overwrite|display|diff]");
    let matches = match opts.parse(args) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return None;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => {
                    print_usage(&opts, "Unrecognized write mode");
                    return None;
                }
            }
        }
        None => WriteMode::Replace,
    };

    if matches.free.len() != 1 {
        print_usage(&opts, "Please provide one file to format");
        return None;
    }

    Some((PathBuf::from(&matches.free[0]), write_mode))
}
<commit_msg>Lookup rustfmt.toml file is relative from input file, not from current directory<commit_after>// Copyright 2015 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

#![cfg(not(test))]

#[macro_use]
extern crate log;
extern crate rustfmt;
extern crate toml;
extern crate env_logger;
extern crate getopts;

use rustfmt::{WriteMode, run};
use rustfmt::config::Config;

use std::env;
use std::fs::{self, File};
use std::io::{self, Read};
use std::path::{Path, PathBuf};

use getopts::Options;

// Try to find a project file in the input file directory and its parents.
fn lookup_project_file(input_file: &Path) -> io::Result<PathBuf> {
    let mut current = if input_file.is_relative() {
        try!(env::current_dir()).join(input_file)
    } else {
        input_file.to_path_buf()
    };

    // TODO: We should canonize path to properly handle its parents,
    // but `canonicalize` function is unstable now (recently added API)
    // current = try!(fs::canonicalize(current));

    loop {
        // if the current directory has no parent, we're done searching
        if !current.pop() {
            return Err(io::Error::new(io::ErrorKind::NotFound, "config not found"));
        }
        let config_file = current.join("rustfmt.toml");
        if fs::metadata(&config_file).is_ok() {
            return Ok(config_file);
        }
    }
}

// Try to find a project file. If it's found, read it.
fn lookup_and_read_project_file(input_file: &Path) -> io::Result<(PathBuf, String)> {
    let path = try!(lookup_project_file(input_file));
    let mut file = try!(File::open(&path));
    let mut toml = String::new();
    try!(file.read_to_string(&mut toml));
    Ok((path, toml))
}

fn execute() -> i32 {
    let (file, write_mode) = match determine_params(std::env::args().skip(1)) {
        Some(params) => params,
        None => return 1,
    };

    let config = match lookup_and_read_project_file(&file) {
        Ok((path, toml)) => {
            println!("Project config file: {}", path.display());
            Config::from_toml(&toml)
        }
        Err(_) => Default::default(),
    };

    run(&file, write_mode, &config);
    0
}

fn main() {
    use std::io::Write;
    let _ = env_logger::init();

    let exit_code = execute();
    // Make sure standard output is flushed before we exit
    std::io::stdout().flush().unwrap();
    // Exit with given exit code.
    //
    // NOTE: This immediately terminates the process without doing any cleanup,
    // so make sure to finish all necessary cleanup before this is called.
    std::process::exit(exit_code);
}

fn print_usage(opts: &Options, reason: &str) {
    let reason = format!("{}\nusage: {} [options] <file>",
                         reason,
                         env::current_exe().unwrap().display());
    println!("{}", opts.usage(&reason));
    Config::print_docs();
}

fn determine_params<I>(args: I) -> Option<(PathBuf, WriteMode)>
    where I: Iterator<Item = String>
{
    let mut opts = Options::new();
    opts.optflag("h", "help", "show this message");
    opts.optopt("",
                "write-mode",
                "mode to write in",
                "[replace|overwrite|display|diff]");
    let matches = match opts.parse(args) {
        Ok(m) => m,
        Err(e) => {
            print_usage(&opts, &e.to_string());
            return None;
        }
    };

    if matches.opt_present("h") {
        print_usage(&opts, "");
    }

    let write_mode = match matches.opt_str("write-mode") {
        Some(mode) => {
            match mode.parse() {
                Ok(mode) => mode,
                Err(..) => {
                    print_usage(&opts, "Unrecognized write mode");
                    return None;
                }
            }
        }
        None => WriteMode::Replace,
    };

    if matches.free.len() != 1 {
        print_usage(&opts, "Please provide one file to format");
        return None;
    }

    Some((PathBuf::from(&matches.free[0]), write_mode))
}
<|endoftext|>


<commit_before>use super::request::*;

use std::cmp::Ordering;

/// HSB colour representation - hue, saturation, brightness (aka value).
/// Aka HSV (LIFX terminology) - hue, saturation, value.
/// This is not the same as HSL as used in CSS.
#[derive(Debug)]
pub struct HSB {
    pub hue: u16,
    pub saturation: u8,
    pub brightness: u8,
}

///  RGB colour representation - red, green, blue.
pub struct RGB {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
}

/// HSBK colour representation - hue, saturation, brightness, kelvin.
/// Kelvin seems to be relevant only to whites - temperature of white.
#[derive(Debug)]
pub struct HSBK {
    pub hue: u16,
    pub saturation: u8,
    pub brightness: u8,
    pub kelvin: u16,
}

impl HSB {
    pub fn new(h: u16, s: u8, b: u8) -> HSB {
        HSB {
            hue: h,
            saturation: s,
            brightness: b,
        }
    }
}

impl From<HSBK> for HSB {
    fn from(c: HSBK) -> HSB {
        HSB::new(c.hue, c.saturation, c.brightness)
    }
}

/// The max value of bytes used to represent the colour element as bytes.
const WORDSIZE: f32 = 65535.0;

// LIFX uses HSB aka HSV, not HSL.

pub fn hue_degrees_to_word(degrees: u16) -> [u8; 2] {
    let f = degrees as f32 / 360.0 * WORDSIZE;
    let b = RequestBin::u16_to_u8_array(f as u16);
    [b[0], b[1]]
}

pub fn hue_word_to_degrees(word: u16) -> u16 {
    let f: f32 = word as f32 / WORDSIZE;
    let d: u16 = (f * 360.0) as u16;
    d
}

pub fn saturation_percent_to_word(percent: u8) -> [u8; 2] {
    let f = percent as f32 / 100.0 * WORDSIZE;
    let b = RequestBin::u16_to_u8_array(f as u16);
    [b[0], b[1]]
}

pub fn saturation_word_to_percent(word: u16) -> u8 {
    let f = word as f32 / WORDSIZE;
    (f * 100.0) as u8
}

pub fn brightness_percent_to_word(percent: u8) -> [u8; 2] {
    saturation_percent_to_word(percent)
}

pub fn brightness_word_to_percent(word: u16) -> u8 {
    let f = word as f32 / WORDSIZE;
    (f * 100.0) as u8
}

pub fn rgb_to_hsv(rgb: RGB) -> HSB {
    let r1 = rgb.red as f32 / 255.0;
    let g1 = rgb.green as f32 / 255.0;
    let b1 = rgb.blue as f32 / 255.0;

    let mut floats: Vec<f32> = vec![r1, g1, b1];
    floats.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
    let cmax = floats[2];
    let cmin = floats[0];
    let d = cmax - cmin;

    // Hue.
    let h = match cmax {
        _ if r1 == cmax => (((g1 - b1) / d) % 6.0) * 60.0,
        _ if g1 == cmax => (((b1 - r1) / d) + 2.0) * 60.0,
        _ if b1 == cmax => (((r1 - g1) / d) + 4.0) * 60.0,
        _ => 0.0,
    };

    // Saturation.
    let s = match cmax {
        0.0 => 0.0,
        _ => d / cmax,
    };

    // Value / brightness.
    let v = cmax;

    HSB {
        hue: h as u16,
        saturation: (s * 100.0) as u8,
        brightness: (v * 100.0) as u8,
    }
}

#[cfg(test)]
mod tests {

    use colour::*;

    #[test]
    fn test_hue_degrees_to_word() {
        assert_eq!([0x55, 0x55], hue_degrees_to_word(120));
    }

    #[test]
    fn test_hue_word_to_degrees() {
        assert_eq!(360, hue_word_to_degrees(65535));
        assert_eq!(0, hue_word_to_degrees(0));
        assert_eq!(180, hue_word_to_degrees(32768));
    }

    #[test]
    fn test_saturation_percent_to_word() {
        assert_eq!([0x7F, 0xFF], saturation_percent_to_word(50));
    }

    #[test]
    fn test_rgb_to_hsv() {
        struct Test {
            rgb: RGB,
            hsb: HSB,
        };

        let tests = vec![
                        Test {
                             rgb: RGB { // olive
                                 red: 128,
                                 green: 128,
                                 blue: 0,
                             },
                             hsb: HSB {
                                 hue: 60,
                                 saturation: 100,
                                 brightness: 50,
                             },
                         },
                         Test {
                             rgb: RGB { // chartreuse
                                 red: 127,
                                 green: 255,
                                 blue: 0,
                             },
                             hsb: HSB {
                                 hue: 90,
                                 saturation: 100,
                                 brightness: 100,
                             },
                         },
        ];

        for t in tests {
            let res = rgb_to_hsv(t.rgb);
            assert_eq!(res.hue, t.hsb.hue);
            assert_eq!(res.saturation, t.hsb.saturation);
            assert_eq!(res.brightness, t.hsb.brightness);
        }
    }
}

pub fn named_colours() -> Vec<String> {
    vec!(
        "beige".to_string(),
        "blue".to_string(),
        "chartreuse".to_string(),
        "coral".to_string(),
        "cornflower".to_string(),
        "crimson".to_string(),
        "deep_sky_blue".to_string(),
        "green".to_string(),
        "red".to_string(),
        "slate_gray".to_string(),
    )
}

pub fn get_colour(s: &str) -> HSB {
    let colour: &str = &(s.to_lowercase());
    match colour {
        "beige" => {
            HSB {
                hue: 60,
                saturation: 56,
                brightness: 91,
            }
        }
        "blue" => {
            HSB {
                hue: 240,
                saturation: 100,
                brightness: 50,
            }
        }
        "chartreuse" => {
            HSB {
                hue: 90,
                saturation: 100,
                brightness: 50,
            }
        }
        "coral" => {
            HSB {
                hue: 16,
                saturation: 100,
                brightness: 66,
            }
        }
        "cornflower" => {
            HSB {
                hue: 219,
                saturation: 79,
                brightness: 66,
            }
        }
        "crimson" => {
            HSB {
                hue: 348,
                saturation: 83,
                brightness: 47,
            }
        }
        "deep_sky_blue" => {
            HSB {
                hue: 195,
                saturation: 100,
                brightness: 50,
            }
        }
        "green" => {
            HSB {
                hue: 120,
                saturation: 100,
                brightness: 50,
            }
        }
        "red" => {
            HSB {
                hue: 0,
                saturation: 100,
                brightness: 50,
            }
        }
        "slate_gray" => {
            HSB {
                hue: 210,
                saturation: 13,
                brightness: 50,
            }
        }
        _ => panic!("no such colour."),
    }

}
<commit_msg>Improve float calculations.<commit_after>use super::request::*;

use std::cmp::Ordering;

/// HSB colour representation - hue, saturation, brightness (aka value).
/// Aka HSV (LIFX terminology) - hue, saturation, value.
/// This is not the same as HSL as used in CSS.
/// LIFX uses HSB aka HSV, not HSL.
#[derive(Debug)]
pub struct HSB {
    pub hue: u16,
    pub saturation: u8,
    pub brightness: u8,
}

///  RGB colour representation - red, green, blue.
pub struct RGB {
    pub red: u8,
    pub green: u8,
    pub blue: u8,
}

/// HSBK colour representation - hue, saturation, brightness, kelvin.
/// Kelvin seems to be relevant only to whites - temperature of white.
#[derive(Debug)]
pub struct HSBK {
    pub hue: u16,
    pub saturation: u8,
    pub brightness: u8,
    pub kelvin: u16,
}

impl HSB {
    pub fn new(h: u16, s: u8, b: u8) -> HSB {
        HSB {
            hue: h,
            saturation: s,
            brightness: b,
        }
    }
}

impl From<HSBK> for HSB {
    fn from(c: HSBK) -> HSB {
        HSB::new(c.hue, c.saturation, c.brightness)
    }
}

/// The max value of the two byte representation of colour element as used in the protocol.
const WORD_SIZE: usize = 65535;
const DEGREES_UBOUND: usize = 360;
const PERCENT_UBOUND: usize = 100;

// (WORD_SIZE / DEGREES_UBOUND) is ~182.0417
// The two-byte represenation only represents integers, so decimals will be truncated.
// This can result in a get_state returning a slightly different result from the
// preceding set_state for hue, saturation, and brightness.

pub fn hue_degrees_to_word(degrees: u16) -> [u8; 2] {
    let f = degrees as f64 * WORD_SIZE as f64 / DEGREES_UBOUND as f64;
    let b = RequestBin::u16_to_u8_array(f.round() as u16);
    [b[0], b[1]]
}

pub fn hue_word_to_degrees(word: u16) -> u16 {
    (word as usize * 360 / WORD_SIZE) as u16
}

pub fn saturation_percent_to_word(percent: u8) -> [u8; 2] {
    let f: f64 = percent as f64 * WORD_SIZE as f64 / 100.0;
    let b = RequestBin::u16_to_u8_array(f.round() as u16);
    [b[0], b[1]]
}

pub fn saturation_word_to_percent(word: u16) -> u8 {
    (word as usize * 100 / WORD_SIZE) as u8
}

pub fn brightness_percent_to_word(percent: u8) -> [u8; 2] {
    saturation_percent_to_word(percent)
}

pub fn brightness_word_to_percent(word: u16) -> u8 {
    (word as usize * 100 / WORD_SIZE) as u8
}

pub fn rgb_to_hsv(rgb: RGB) -> HSB {
    let r1 = rgb.red as f32 / 255.0;
    let g1 = rgb.green as f32 / 255.0;
    let b1 = rgb.blue as f32 / 255.0;

    let mut floats: Vec<f32> = vec![r1, g1, b1];
    floats.sort_by(|a, b| a.partial_cmp(b).unwrap_or(Ordering::Equal));
    let cmax = floats[2];
    let cmin = floats[0];
    let d = cmax - cmin;

    // Hue.
    let h = match cmax {
        _ if r1 == cmax => (((g1 - b1) / d) % 6.0) * 60.0,
        _ if g1 == cmax => (((b1 - r1) / d) + 2.0) * 60.0,
        _ if b1 == cmax => (((r1 - g1) / d) + 4.0) * 60.0,
        _ => 0.0,
    };

    // Saturation.
    let s = match cmax {
        0.0 => 0.0,
        _ => d / cmax,
    };

    // Value / brightness.
    let v = cmax;

    HSB {
        hue: h as u16,
        saturation: (s * 100.0) as u8,
        brightness: (v * 100.0) as u8,
    }
}

#[cfg(test)]
mod tests {

    use colour::*;

    #[test]
    fn test_hue_degrees_to_word() {
        assert_eq!([0x55, 0x55], hue_degrees_to_word(120));
        assert_eq!([0x47, 0x1C], hue_degrees_to_word(100));
        assert_eq!([0x44, 0x44], hue_degrees_to_word(96));
        assert_eq!([0x43, 0x8E], hue_degrees_to_word(95));
    }

    #[test]
    fn test_hue_word_to_degrees() {
        assert_eq!(360, hue_word_to_degrees(65535));
        assert_eq!(0, hue_word_to_degrees(0));
        assert_eq!(180, hue_word_to_degrees(32768));
    }

    #[test]
    fn test_saturation_percent_to_word() {
        assert_eq!([0x80, 0x00], saturation_percent_to_word(50));
    }

    #[test]
    fn test_rgb_to_hsv() {
        struct Test {
            rgb: RGB,
            hsb: HSB,
        };

        let tests = vec![
                        Test {
                             rgb: RGB { // olive
                                 red: 128,
                                 green: 128,
                                 blue: 0,
                             },
                             hsb: HSB {
                                 hue: 60,
                                 saturation: 100,
                                 brightness: 50,
                             },
                         },
                         Test {
                             rgb: RGB { // chartreuse
                                 red: 127,
                                 green: 255,
                                 blue: 0,
                             },
                             hsb: HSB {
                                 hue: 90,
                                 saturation: 100,
                                 brightness: 100,
                             },
                         },
        ];

        for t in tests {
            let res = rgb_to_hsv(t.rgb);
            assert_eq!(res.hue, t.hsb.hue);
            assert_eq!(res.saturation, t.hsb.saturation);
            assert_eq!(res.brightness, t.hsb.brightness);
        }
    }
}

pub fn named_colours() -> Vec<String> {
    vec!(
        "beige".to_string(),
        "blue".to_string(),
        "chartreuse".to_string(),
        "coral".to_string(),
        "cornflower".to_string(),
        "crimson".to_string(),
        "deep_sky_blue".to_string(),
        "green".to_string(),
        "red".to_string(),
        "slate_gray".to_string(),
    )
}

pub fn get_colour(s: &str) -> HSB {
    let colour: &str = &(s.to_lowercase());
    match colour {
        "beige" => {
            HSB {
                hue: 60,
                saturation: 56,
                brightness: 91,
            }
        }
        "blue" => {
            HSB {
                hue: 240,
                saturation: 100,
                brightness: 50,
            }
        }
        "chartreuse" => {
            HSB {
                hue: 90,
                saturation: 100,
                brightness: 50,
            }
        }
        "coral" => {
            HSB {
                hue: 16,
                saturation: 100,
                brightness: 66,
            }
        }
        "cornflower" => {
            HSB {
                hue: 219,
                saturation: 79,
                brightness: 66,
            }
        }
        "crimson" => {
            HSB {
                hue: 348,
                saturation: 83,
                brightness: 47,
            }
        }
        "deep_sky_blue" => {
            HSB {
                hue: 195,
                saturation: 100,
                brightness: 50,
            }
        }
        "green" => {
            HSB {
                hue: 120,
                saturation: 100,
                brightness: 50,
            }
        }
        "red" => {
            HSB {
                hue: 0,
                saturation: 100,
                brightness: 50,
            }
        }
        "slate_gray" => {
            HSB {
                hue: 210,
                saturation: 13,
                brightness: 50,
            }
        }
        _ => panic!("no such colour."),
    }

}
<|endoftext|>


<commit_before>//! View bits in memory with turtles
//!
//! This example uses [@myrrlyn]'s [`bitvec`] crate to turn data into strings of
//! bits, and then draws them on the screen.
//!
//! You are encouraged to change both the data used to seed the turtle, and the
//! `bitvec` calls that control how the turtle acts, to see what changes.
//!
//! [@myrrlyn]: //github.com/myrrlyn
//! [`bitvec`]: //crates.io/crates/bitvec

// The `bitvec` crate provides a lot of types; we only need a few:
use bitvec::prelude::*;
use turtle::Turtle;

// Modify these constants to change the behavior of the example.

/// This controls the width of the drawn line for each bit.
const BIT_WIDTH: f64 = 20.0;

/// This controls the vertical spacing between rows of bit lines.
const BIT_HEIGHT: f64 = 10.0;

/// This text will be inspected as individual bytes, and drawn on the screen.
/// You can change it to see what different text looks like when viewed as bits.
///
/// The example program will print more information about the parts of the text
/// to the console while the turtle draws, so that you can see how each glyph
/// corresponds to parts of the rendered memory.
static TEXT: &str = "¡Hola, mundo! 🌍🌏🌎";

/// This number will have its bit pattern printed on screen. Rust provides some
/// interesting numbers in its standard library; you can replace this with other
/// numbers to see what they look like. Pi is provided as the default solely
/// because it is well-known, and has an interesting pattern.
const NUMBER: f64 = std::f64::consts::PI;

// These are helpers for the turtle's actions.

/// Set the horizontal spacing between successive bits in a row
const BIT_MARGIN: f64 = BIT_WIDTH / 2.0;
/// Compute the total width of a bit plus its spacing
const BIT_BOX: f64 = BIT_WIDTH + BIT_MARGIN;

fn main() {
    // This block sets up the turtle to draw bits more or less centered in the
    // screen. The turtle works by walking horizontally for each bit in a byte,
    // then backtracking and walking vertically to the next byte.
    let mut turtle = Turtle::new();
    // The turtle starts in the center, but we want to move it around before
    // drawing.
    turtle.pen_up();
    // Compute the boundaries of the part of the screen where the turtle will
    // draw
    let right_edge = BIT_BOX * 4.0;
    // Place the turtle according to the length of the text span
    let top_edge = BIT_HEIGHT * (TEXT.len() as f64 / 2.0);
    // The turtle starts from the top right of the region,
    turtle.go_to((right_edge, top_edge));
    // and walks left
    turtle.set_heading(180.0);

    // Render the text as bits of memory.

    // Rust strings can iterate over their individual characters. This block
    // loops over characters, collecting their start point in the text so that
    // we can grab the encoded bytes of each one.
    let mut row_num = 0;
    for (char_num, (start, codepoint)) in TEXT.char_indices().enumerate() {
        println!("Character {}: {}", char_num, codepoint);
        // Each character has a variable width, so we need to find that.
        let byte_count = codepoint.len_utf8();
        // And then collect the bytes of the string that make up the character.
        // `start` gives us the starting position in the text sequence, and
        // `byte_count` gives us the length in bytes of the character, so we
        // need to select the range beginning at `start`, running for
        // `byte_count`. Another style of writing this that you might see in
        // Rust libraries is `[start ..][.. length]`.
        let row: &[u8] = &TEXT.as_bytes()[start .. start + byte_count];

        // For each byte (`u8`), we use `bitvec` to make a view into its bits.
        // `bitvec` provides the `.bits::<_>()` method on Rust integers for easy
        // access to its view types.
        //
        // The `LittleEndian` means that the view moves from least significant
        // bit to most significant. Since we want to display on screen the most
        // significant bit on the left, and the least on the right, the turtle
        // will have to move from right to left to match.
        //
        // These types describe different ways to view the same data. You can
        // read more about them in the `bitvec` docs, and at Wikipedia:
        // https://docs.rs/bitvec/0.16.1/bitvec/cursor/index.html
        // https://en.wikipedia.org/wiki/Endianness#Bit_endianness
        for byte in row {
            println!("  Byte {:02}:\n    Value: 0x{:02X}\n    Bits: {:08b}", row_num, byte, byte);

            let bits: &BitSlice<_, _> = byte.bits::<LittleEndian>();

            // Then we draw the byte's bits as a row
            draw_row(&mut turtle, bits);
            // And reset the turtle to the right edge.
            turtle.set_x(right_edge);

            row_num += 1;
        }
    }

    // `bitvec` can look at more than just `u8`. Let's try looking at the bits
    // that represent a number!
    //
    // But `bitvec` doesn't know how to view all numbers. The standard library
    // provides a function `to_bits(f64) -> u64`, which turns it into a number
    // `bitvec` does know how to view.
    //
    // The `f64` data type has a memory encoding standardized by the IEEE-754
    // document. You can read more about it here:
    // https://en.wikipedia.org/wiki/Double-precision_floating-point_format
    let raw_number: u64 = NUMBER.to_bits();

    // `bitvec` can also view bits from left to right, with `BigEndian`.
    let bits: &BitSlice<_, _> = raw_number.bits::<BigEndian>();

    // Since we are reading bits from left to right, the turtle should move from
    // left to right also. Change the `* 2.0` to move the section horizontally.
    let left_edge = -right_edge * 2.0;
    turtle.set_x(left_edge);
    // Walk from left to right
    turtle.set_heading(0.0);

    // The `&BitSlice` type acts just like `&[bool]`, so it comes with a
    // `.chunks` method which divides it into smaller pieces. `bitvec` can take
    // any number, not just multiples of 8, but 16 is a convenient number to
    // look at. Try changing it to a different number, like 10, to see what
    // happens!
    for (num, row) in bits.chunks(16).enumerate() {
        println!("Row {} bits: {:b}", num, row);
        // Each chunk produced is a smaller `&BitSlice`, just like
        // `&[bool].chunks` produces smaller `&[bool]`s, so we can draw it.
        draw_row(&mut turtle, row);
        // After each row, the turtle has to go back to the left edge.
        turtle.set_x(left_edge);
    }

    // Reader exercise:
    //
    // The IEEE-754 format for `f64` numbers separates them into three parts:
    //
    // 1. The sign marks whether the number is positive or negative: 1 bit
    // 2. The exponent marks how far from zero the number is: 11 bits
    // 3. The fraction describes the number: 52 bits.
    //
    // Using these widths (1 bit, 11 bits, 52 bits), the knowledge that
    // `&BitSlice` is a normal Rust slice, and the API documentation for
    // `std::iter::Iterator`, see if you can display each portion of an `f64`
    // as its own row.
    //
    // Hints:
    //
    // - The variable `bits` is set up to view the entire number, from most
    // significant bit to least.
    // - You can get access to a structure that performs iteration by calling
    //   `bits.iter()`.
    // - You can use the `Iterator::by_ref` method to prevent `Iterator` adapter
    //   functions from destroying the source iterator.
    // - `&BitSlice` is an ordinary Rust slice, so you can use `[start .. end]`
    //   range indexing to get smaller pieces of it.
}

/// Draw a row of bits on the screen.
///
/// This takes a reference to a turtle, which draws, and a reference to a slice
/// of bits, which provides the data to draw.
fn draw_row<C, T>(t: &mut Turtle, row: &BitSlice<C, T>)
where C: Cursor, T: BitStore {
    // `&BitSlice` can iterate over bits. It is just like `&[bool]`, and so it
    // produces `&bool` for each loop.
    for &bit in row {
        // This checks if the bit produced by the row is `1` or `0`, and sets
        // the pen color to black (`1`) or light grey (`0`)
        if bit {
            t.set_pen_color("black");
        }
        else {
            t.set_pen_color("light grey");
        }

        // For each bit, the loop puts down the pen to draw a line of the bit's
        // color, then picks up the pen to add some horizontal spacing between
        // them.
        t.pen_down();
        t.forward(BIT_WIDTH);
        t.pen_up();
        t.forward(BIT_MARGIN);
    }

    // After the row is complete, the turtle picks up its pen,
    t.pen_up();
    // rememebers which direction it was going,
    let old_heading = t.heading();
    // turns to face down the screen,
    t.set_heading(270.0);
    // moves down by a row,
    t.forward(BIT_HEIGHT);
    // then goes back to its old direction.
    t.set_heading(old_heading);
    // This way each row gets vertical spacing between them.
}
<commit_msg>Address review<commit_after>//! View bits in memory with turtles
//!
//! This example uses [@myrrlyn]'s [`bitvec`] crate to turn data into strings of
//! bits, and then draws them on the screen.
//!
//! You are encouraged to change both the data used to seed the turtle, and the
//! `bitvec` calls that control how the turtle acts, to see what changes.
//!
//! [@myrrlyn]: //github.com/myrrlyn
//! [`bitvec`]: //crates.io/crates/bitvec

// This imports the things we need from `bitvec`, including the `Bits` trait for
// the `.bits::<_>()` method we use to view memory.
use bitvec::prelude::*;
use turtle::Turtle;

// Modify these constants to change the behavior of the example.

/// This text will be inspected as individual bytes, and drawn on the screen.
/// You can change it to see what different text looks like when viewed as bits.
///
/// The example program will print more information about the parts of the text
/// to the console while the turtle draws, so that you can see how each glyph
/// corresponds to parts of the rendered memory.
static TEXT: &str = "¡Hola, mundo! 🌍🌏🌎";

/// This number will have its bit pattern printed on screen. Rust provides some
/// interesting numbers in its standard library; you can replace this with other
/// numbers to see what they look like. Pi is provided as the default solely
/// because it is well-known, and has an interesting pattern.
const NUMBER: f64 = std::f64::consts::PI;

/// This controls the width of the drawn line for each bit.
const BIT_WIDTH: f64 = 20.0;

/// This controls the vertical spacing between rows of bit lines.
const BIT_HEIGHT: f64 = 10.0;

/// Set the horizontal spacing between successive bits in a row
const BIT_MARGIN: f64 = BIT_WIDTH / 2.0;

/// Compute the total width of a bit plus its spacing
const BIT_BOX: f64 = BIT_WIDTH + BIT_MARGIN;

fn main() {
    // This block sets up the turtle to draw bits more or less centered in the
    // screen. The turtle works by walking horizontally for each bit in a byte,
    // then backtracking and walking vertically to the next byte.
    let mut turtle = Turtle::new();
    // The turtle starts in the center of the screen, but we want to move it
    // around before drawing.
    turtle.pen_up();
    // Compute the boundaries of the part of the screen where the turtle will
    // draw. We expect to be drawing eight bits, with half to the right of
    // center and half to the left.
    let right_edge = BIT_BOX * 8.0 / 2.0;
    // We also expect to be drawing a row for each byte in the text, with an
    // additional separator row for each *character*, half above and half below
    // the center of the screen. This computes how many rows of text we will
    // draw, then moves the turtle appropriately.
    let byte_rows = TEXT.len();
    let char_gaps = TEXT.chars().count();
    let top_edge = BIT_HEIGHT * ((byte_rows + char_gaps) as f64 / 2.0);
    // The turtle starts from the top right of the region,
    turtle.go_to((right_edge, top_edge));
    // and walks left
    turtle.set_heading(180.0);

    // Render the text as bits of memory.

    // Rust strings can iterate over their individual characters. This block
    // loops over characters, collecting their start point in the text so that
    // we can grab the encoded bytes of each one.
    let mut row_num = 0;
    for (char_num, (start, codepoint)) in TEXT.char_indices().enumerate() {
        println!("Character {}: {}", char_num, codepoint);
        // Each character has a variable width, so we need to find that.
        let byte_count = codepoint.len_utf8();
        // And then collect the bytes of the string that make up the character.
        // `start` gives us the starting position in the text sequence, and
        // `byte_count` gives us the length in bytes of the character, so we
        // need to select the range beginning at `start`, running for
        // `byte_count`. Another style of writing this that you might see in
        // Rust libraries is `[start ..][.. length]`.
        let row: &[u8] = &TEXT.as_bytes()[start .. start + byte_count];

        // For each byte (`u8`), we use `bitvec` to make a view into its bits.
        // `bitvec` provides the `.bits::<_>()` method on Rust integers for easy
        // access to its view types.
        //
        // The `LittleEndian` means that the view moves from least significant
        // bit to most significant. Since we want to display on screen the most
        // significant bit on the left, and the least on the right, the turtle
        // will have to move from right to left to match.
        //
        // These types describe different ways to view the same data. You can
        // read more about them in the `bitvec` docs, and at Wikipedia:
        // https://docs.rs/bitvec/0.16.1/bitvec/cursor/index.html
        // https://en.wikipedia.org/wiki/Endianness#Bit_endianness
        for byte in row {
            println!("  Byte {:02}:\n    Value: 0x{:02X}\n    Bits: {:08b}", row_num, byte, byte);

            let bits: &BitSlice<_, _> = byte.bits::<LittleEndian>();

            // Then we draw the byte's bits as a row
            draw_row(&mut turtle, bits);
            // And reset the turtle to the right edge.
            turtle.set_x(right_edge);

            row_num += 1;
        }
        // This puts a dividing line between each *character* in the text.
        // Some characters may have more than one byte, and those bytes will be
        // grouped together.
        delimit(&mut turtle, 8.0 * BIT_BOX - BIT_MARGIN);
    }

    // `bitvec` can look at more than just `u8`. Let's try looking at the bits
    // that represent a number!
    //
    // Some numbers, like `f64`, have special rules for their representation in
    // bits. `bitvec` only knows about raw bits, so it does not provide direct
    // support for `f64`. Rust lets us get the bit representation from an `f64`
    // with the method `to_bits(f64) -> u64`, which forgets about the `f64`
    // rules and uses the number's storage as ordinary bits.
    //
    // You can read more about the rules for `f64`'s storage in memory, and
    // behavior in programs, here:
    // https://en.wikipedia.org/wiki/Double-precision_floating-point_format
    let raw_number: u64 = NUMBER.to_bits();

    // `bitvec` can also view bits from left to right, with `BigEndian`.
    let bits: &BitSlice<_, _> = raw_number.bits::<BigEndian>();

    // Since we are reading bits from left to right, the turtle should move from
    // left to right also. You can change the `* 2.0` to move the section
    // horizontally.
    let left_edge = -right_edge * 2.0;
    turtle.set_x(left_edge);
    // Walk from left to right
    turtle.set_heading(0.0);

    // The `&BitSlice` type acts just like `&[bool]`, so it comes with a
    // `.chunks` method which divides it into smaller pieces. `bitvec` can take
    // any number, not just multiples of 8, but 16 is a convenient number to
    // look at. Try changing it to a different number, like 10, to see what
    // happens!
    for (num, row) in bits.chunks(16).enumerate() {
        println!("Row {} bits: {:b}", num, row);
        // Each chunk produced is a smaller `&BitSlice`, just like
        // `&[bool].chunks` produces smaller `&[bool]`s, so we can draw it.
        draw_row(&mut turtle, row);
        // After each row, the turtle has to go back to the left edge.
        turtle.set_x(left_edge);
    }

    // Reader exercise:
    //
    // The IEEE-754 format for `f64` numbers separates them into three parts:
    //
    // 1. The sign marks whether the number is positive or negative: 1 bit
    // 2. The exponent marks how far from zero the number is: 11 bits
    // 3. The fraction describes the number: 52 bits.
    //
    // Using these widths (1 bit, 11 bits, 52 bits), the knowledge that
    // `&BitSlice` is a normal Rust slice, and the API documentation for
    // `std::iter::Iterator`, see if you can display each portion of an `f64`
    // as its own row.
    //
    // Hints:
    //
    // - The variable `bits` is set up to view the entire number, from most
    // significant bit to least.
    // - You can get access to a structure that performs iteration by calling
    //   `bits.iter()`.
    // - You can use the `Iterator::by_ref` method to prevent `Iterator` adapter
    //   functions from destroying the source iterator.
    // - `&BitSlice` is an ordinary Rust slice, so you can use `[start .. end]`
    //   range indexing to get smaller pieces of it.
}

/// Draw a row of bits on the screen.
///
/// This takes a reference to a turtle, which draws, and a reference to a slice
/// of bits, which provides the data to draw.
///
/// Note that this works whether we're going through the bits left to right
/// (`BigEndian`) or right to left (`LittleEndian`), because we assume that the
/// turtle is going to start on the correct side and be facing the correct way
/// for this drawing to work.
fn draw_row<C, T>(t: &mut Turtle, row: &BitSlice<C, T>)
where C: Cursor, T: BitStore {
    // `&BitSlice` can iterate over bits. It is just like `&[bool]`, and so it
    // produces `&bool` for each loop.
    for &bit in row {
        // This checks if the bit produced by the row is `1` or `0`, and sets
        // the pen color to black (`1`) or light grey (`0`)
        if bit {
            t.set_pen_color("black");
        }
        else {
            t.set_pen_color("light grey");
        }

        // For each bit, the loop puts down the pen to draw a line of the bit's
        // color, then picks up the pen to add some horizontal spacing between
        // them.
        t.pen_down();
        t.forward(BIT_WIDTH);
        t.pen_up();
        t.forward(BIT_MARGIN);
    }

    next_row(t, BIT_HEIGHT);
}

/// Produces a separator line to demark different sections of memory.
fn delimit(t: &mut Turtle, width: f64) {
    t.set_pen_color("grey");
    t.pen_down();
    t.forward(width);
    t.backward(width);

    next_row(t, BIT_HEIGHT);
}

/// Moves the turtle down a row
fn next_row(t: &mut Turtle, by: f64) {
    // To move down, the turtle picks up its pen,
    t.pen_up();
    // rememebers which direction it was going,
    let old_heading = t.heading();
    // turns to face down the screen,
    t.set_heading(270.0);
    // moves down by a row,
    t.forward(by);
    // then goes back to its old direction.
    t.set_heading(old_heading);
    // This way each row gets vertical spacing between them.
}
<|endoftext|>