<commit_before>//! Low level and internal http and https implementation.

use std::collections::HashMap;
use std::io::{stderr, Read, Result as IoResult, Write};
use std::net::{SocketAddr, TcpStream};
use std::sync::Arc;
use std::time::Duration;

use log::Level::Info;
use rustls::{ClientConfig, ClientSession, ProtocolVersion, Session};
use webpki::DNSNameRef;
use webpki_roots;

use super::constants;
use super::dns::Resolver;
use super::request::Request;
use super::results::{CabotError, CabotResult};

const BUFFER_PAGE_SIZE: usize = 2048;
const RESPONSE_BUFFER_SIZE: usize = 1024;

#[derive(Debug, PartialEq)]
enum TransferEncoding {
    ContentLength(usize),
    Chunked,
    Unkown,
    None,
}

impl From<&[u8]> for TransferEncoding {
    fn from(hdr: &[u8]) -> Self {
        let hdr = String::from_utf8_lossy(hdr);
        let hdrup = hdr.to_ascii_uppercase();
        match hdrup.as_str() {
            "CHUNKED" => TransferEncoding::Chunked,
            _ => TransferEncoding::Unkown,
        }
    }
}

struct HttpDecoder<'a, T> {
    writer: &'a mut Write,
    reader: &'a mut T,
    buffer: Vec<u8>,
    transfer_encoding: TransferEncoding,
}

impl<'a, T> HttpDecoder<'a, T>
where
    for<'b> T: Read + Sized,
{
    fn new(writer: &'a mut Write, reader: &'a mut T) -> Self {
        HttpDecoder {
            writer,
            reader,
            buffer: Vec::with_capacity(BUFFER_PAGE_SIZE),
            transfer_encoding: TransferEncoding::None,
        }
    }
    fn chunk_read(&mut self) -> IoResult<usize> {
        let mut buf = [0; BUFFER_PAGE_SIZE];
        let ret = self.reader.read(&mut buf[..]);
        if let Ok(count) = ret {
            if count > 0 {
                self.buffer.extend_from_slice(&buf[0..count]);
            }
        }
        ret
    }
    fn read_write_headers(&mut self) -> IoResult<()> {
        info!("Reading headers");
        let to_drain = loop {
            let count = self.chunk_read()?;
            info!("Count: {}", count);
            if count == 0 {
                break 0;
            }
            let resp_header: Vec<&[u8]> = constants::SPLIT_HEADERS_RE
                .splitn(self.buffer.as_slice(), 2)
                .collect();
            if resp_header.len() == 2 {
                // We have headers
                let headers = resp_header.get(0).unwrap();
                if let Some(header) = constants::TRANSFER_ENCODING.captures(headers) {
                    if let Some(tenc) = header.get(1) {
                        self.transfer_encoding = TransferEncoding::from(tenc.as_bytes());
                    }
                } else if let Some(header) = constants::CONTENT_LENGTH.captures(headers) {
                    if let Some(clength) = header.get(1) {
                        let clength = String::from_utf8_lossy(clength.as_bytes()).into_owned();
                        let clength = usize::from_str_radix(clength.as_str(), 10).unwrap();
                        self.transfer_encoding = TransferEncoding::ContentLength(clength);
                    }
                }
                let resp = headers.len();
                self.writer.write(headers).unwrap();
                self.writer.write(b"\r\n\r\n").unwrap();
                break resp + 4; // + CRLF CRLF
            }
        };
        let buffer = self.buffer.drain(to_drain..).collect();
        self.buffer = buffer;
        info!("Headers read");
        Ok(())
    }

    fn read_write_no_transfer_encoding(&mut self) -> IoResult<()> {
        loop {
            self.writer.write(self.buffer.as_slice()).unwrap();
            self.buffer.clear();

            let cnt = self.chunk_read()?;
            if cnt == 0 {
                break;
            }
        }

        self.writer.flush()?;
        Ok(())
    }

    fn read_content_length(&mut self, size: usize) -> IoResult<()> {
        let mut read_count = self.buffer.len();
        loop {
            self.writer.write(self.buffer.as_slice()).unwrap();
            self.buffer.clear();

            if read_count >= size {
                break;
            }
            read_count = read_count + self.chunk_read()?;
        }
        Ok(())
    }

    fn read_write_chunk(&mut self) -> IoResult<()> {
        // vars for chunk encoding only
        let mut read_chunk = true;
        let mut read_size = 0;
        let mut header_len: usize;

        'outer: loop {
            while read_chunk {
                header_len = 0;
                {
                    // we read the chunk size to drain
                    let header: Vec<&[u8]> = constants::SPLIT_HEADER_BRE
                        .splitn(self.buffer.as_slice(), 2)
                        .collect();
                    if header.len() == 2 {
                        if let Some(header) = constants::GET_CHUNK_SIZE.captures(header[0]) {
                            if let Some(size) = header.get(1) {
                                let size = size.as_bytes();
                                header_len = size.len() + 2;
                                let size = String::from_utf8_lossy(size).into_owned();
                                read_size = usize::from_str_radix(size.as_str(), 16).unwrap();
                                read_chunk = false;
                            }
                        } else {
                            // else return Error
                            break;
                        }
                    }
                }

                if read_size == 0 {
                    self.buffer.clear(); // should we check that it is '0\r\n' ?
                    break 'outer;
                }

                let buffer: Vec<u8> = self.buffer.drain(header_len..).collect();
                self.buffer = buffer;

                if !read_chunk && self.buffer.len() >= read_size + 2 {
                    let mut buffer: Vec<u8> = self.buffer.drain(read_size..).collect();
                    debug!("{:?}", String::from_utf8_lossy(self.buffer.as_slice()));
                    self.writer.write(self.buffer.as_slice()).unwrap();
                    self.writer.flush()?;

                    let buffer2 = buffer.drain(2..).collect(); // CRLF
                    self.buffer = buffer2;
                    read_chunk = true;
                    read_size = 0;
                }
            }

            let cnt = self.chunk_read()?;
            info!("!!!! {:?}", cnt);
            if cnt == 0 {
                break;
            }
        }

        Ok(())
    }

    fn read_write(&mut self) -> IoResult<()> {
        self.read_write_headers()?;
        info!("Reading body");

        match self.transfer_encoding {
            TransferEncoding::ContentLength(size) => {
                self.read_content_length(size)?;
            }
            TransferEncoding::Chunked => {
                self.read_write_chunk()?;
            }
            _ => {
                self.read_write_no_transfer_encoding()?;
            }
        }

        if self.buffer.len() > 0 {
            let b = String::from_utf8_lossy(self.buffer.as_slice());
            error!("Buffer not clear: {}", b);
        }

        Ok(())
    }
}

fn log_request(request: &[u8], verbose: bool) {
    if !log_enabled!(Info) && !verbose {
        return;
    }
    let request: Vec<&[u8]> = constants::SPLIT_HEADERS_RE.splitn(request, 2).collect();
    let headers = String::from_utf8_lossy(&request[0]);
    let headers: Vec<&str> = constants::SPLIT_HEADER_RE.split(&headers).collect();
    let bodylen = if request.len() == 2 {
        let body = &request[1];
        body.len()
    } else {
        0
    };
    if log_enabled!(Info) {
        for header in headers {
            info!("> {}", header);
        }
        if bodylen > 0 {
            info!("> [{} bytes]", bodylen);
        }
        info!(">");
    } else if verbose {
        for header in headers {
            writeln!(&mut stderr(), "> {}", header).unwrap();
        }
        if bodylen > 0 {
            writeln!(&mut stderr(), "> [{} bytes]", bodylen).unwrap();
        }
        writeln!(&mut stderr(), ">").unwrap();
    }
}

fn read_buf<T>(client: &mut T, buf: &mut [u8]) -> Vec<u8>
where
    T: Read + Sized,
{
    let mut response: Vec<u8> = Vec::with_capacity(RESPONSE_BUFFER_SIZE);
    while let Ok(count) = client.read(&mut buf[..]) {
        if count > 0 {
            response.extend_from_slice(&buf[0..count]);
        } else {
            break;
        }
    }
    response
}

fn from_http(
    request: &Request,
    client: &mut TcpStream,
    out: &mut Write,
    verbose: bool,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose);

    debug!("Sending request...");
    client.write_all(&raw_request).unwrap();

    debug!("Reading response headers...");

    let mut http_decoder = HttpDecoder::new(out, client);
    http_decoder.read_write()?;
    Ok(())
}

fn from_https(
    request: &Request,
    mut client: &mut TcpStream,
    out: &mut Write,
    verbose: bool,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    let mut response: Vec<u8> = Vec::with_capacity(RESPONSE_BUFFER_SIZE);
    let mut buf = [0; BUFFER_PAGE_SIZE];

    let mut config = ClientConfig::new();
    config
        .root_store
        .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
    let rc_config = Arc::new(config);
    let host = DNSNameRef::try_from_ascii_str(request.host())
        .map_err(|_| CabotError::HostnameParseError(request.host().to_owned()))?;
    let mut tlsclient = ClientSession::new(&rc_config, host);
    let mut is_handshaking = true;
    loop {
        while tlsclient.wants_write() {
            let count = tlsclient.write_tls(&mut client).unwrap();
            debug!("Write {} TLS bytes", count);
        }
        if is_handshaking && !tlsclient.is_handshaking() {
            info!("Handshake complete");
            is_handshaking = false;
            let protocol = tlsclient.get_protocol_version();
            match protocol {
                Some(ProtocolVersion::SSLv2) => {
                    info!("Protocol SSL v2 negociated");
                }
                Some(ProtocolVersion::SSLv3) => {
                    info!("Protocol SSL v3 negociated");
                }
                Some(ProtocolVersion::TLSv1_0) => {
                    info!("Protocol TLS v1.0 negociated");
                }
                Some(ProtocolVersion::TLSv1_1) => {
                    info!("Protocol TLS v1.1 negociated");
                }
                Some(ProtocolVersion::TLSv1_2) => {
                    info!("Protocol TLS v1.2 negociated");
                }
                Some(ProtocolVersion::TLSv1_3) => {
                    info!("Protocol TLS v1.3 negociated");
                }
                Some(ProtocolVersion::Unknown(num)) => {
                    info!("Unknown TLS Protocol negociated: {}", num);
                }
                None => {
                    info!("No TLS Protocol negociated");
                }
            }
            log_request(&raw_request, verbose);
            tlsclient.write_all(&raw_request).unwrap();
            let count = tlsclient.write_tls(&mut client).unwrap();
            debug!("Write {} TLS bytes", count);
        }

        if tlsclient.wants_read() {
            let count = tlsclient.read_tls(&mut client)?;
            debug!("Read {} TLS bytes", count);
            if count == 0 {
                break;
            }

            tlsclient.process_new_packets()?;

            let mut part: Vec<u8> = read_buf(&mut tlsclient, &mut buf);
            response.append(&mut part);
        } else {
            break;
        }
    }
    out.write_all(response.as_slice()).unwrap();
    Ok(())
}

pub fn http_query(
    request: &Request,
    mut out: &mut Write,
    authorities: &HashMap<String, SocketAddr>,
    verbose: bool,
    ipv4: bool,
    ipv6: bool,
) -> CabotResult<()> {
    debug!(
        "HTTP Query {} {}",
        request.http_method(),
        request.request_uri()
    );

    let authority = request.authority();

    let addr = match authorities.get(authority) {
        Some(val) => {
            info!("Fetch authority {} using autorities map", authority);
            *val
        }
        None => {
            info!("Fetch authority {} using resolver", authority);
            let resolver = Resolver::new(verbose);
            resolver.get_addr(authority, ipv4, ipv6)?
        }
    };

    info!("Connecting to {}", addr);
    let mut client = TcpStream::connect(addr)?;

    client.set_read_timeout(Some(Duration::new(5, 0))).unwrap();

    match request.scheme() {
        "http" => from_http(request, &mut client, &mut out, verbose)?,
        "https" => from_https(request, &mut client, &mut out, verbose)?,
        _ => {
            return Err(CabotError::SchemeError(format!(
                "Unrecognized scheme {}",
                request.scheme()
            )))
        }
    };

    out.flush().unwrap();

    Ok(())
}
<commit_msg>Add an ugly https implementation (not chunk buffer)<commit_after>//! Low level and internal http and https implementation.

use std::collections::HashMap;
use std::io::{stderr, Read, Result as IoResult, Write};
use std::net::{SocketAddr, TcpStream};
use std::sync::Arc;
use std::time::Duration;

use log::Level::Info;
use rustls::{ClientConfig, ClientSession, ProtocolVersion, Session};
use webpki::DNSNameRef;
use webpki_roots;

use super::constants;
use super::dns::Resolver;
use super::request::Request;
use super::results::{CabotError, CabotResult};

const BUFFER_PAGE_SIZE: usize = 2048;
const RESPONSE_BUFFER_SIZE: usize = 1024;

#[derive(Debug, PartialEq)]
enum TransferEncoding {
    ContentLength(usize),
    Chunked,
    Unkown,
    None,
}

impl From<&[u8]> for TransferEncoding {
    fn from(hdr: &[u8]) -> Self {
        let hdr = String::from_utf8_lossy(hdr);
        let hdrup = hdr.to_ascii_uppercase();
        match hdrup.as_str() {
            "CHUNKED" => TransferEncoding::Chunked,
            _ => TransferEncoding::Unkown,
        }
    }
}

struct HttpDecoder<'a, T> {
    writer: &'a mut Write,
    reader: &'a mut T,
    buffer: Vec<u8>,
    transfer_encoding: TransferEncoding,
}

impl<'a, T> HttpDecoder<'a, T> {
    fn process_headers(&mut self) -> Option<usize> {
        let ret = {
            let resp_header: Vec<&[u8]> = constants::SPLIT_HEADERS_RE
                .splitn(self.buffer.as_slice(), 2)
                .collect();
            if resp_header.len() == 2 {
                // We have headers
                let headers = resp_header.get(0).unwrap();
                if let Some(header) = constants::TRANSFER_ENCODING.captures(headers) {
                    if let Some(tenc) = header.get(1) {
                        self.transfer_encoding = TransferEncoding::from(tenc.as_bytes());
                    }
                } else if let Some(header) = constants::CONTENT_LENGTH.captures(headers) {
                    if let Some(clength) = header.get(1) {
                        let clength = String::from_utf8_lossy(clength.as_bytes()).into_owned();
                        let clength = usize::from_str_radix(clength.as_str(), 10).unwrap();
                        self.transfer_encoding = TransferEncoding::ContentLength(clength);
                    }
                }
                let resp = headers.len();
                self.writer.write(headers).unwrap();
                self.writer.write(b"\r\n\r\n").unwrap();
                Some(resp + 4) // + CRLF CRLF
            } else {
                None
            }
        };
        if let Some(to_drain) = ret {
            let buffer = self.buffer.drain(to_drain..).collect();
            self.buffer = buffer;
            info!("Headers read");
        }
        ret
    }
}

impl<'a, T> HttpDecoder<'a, T>
where
    for<'b> T: Read + Sized,
{
    fn new(writer: &'a mut Write, reader: &'a mut T) -> Self {
        HttpDecoder {
            writer,
            reader,
            buffer: Vec::with_capacity(BUFFER_PAGE_SIZE),
            transfer_encoding: TransferEncoding::None,
        }
    }
    fn chunk_read(&mut self) -> IoResult<usize> {
        let mut buf = [0; BUFFER_PAGE_SIZE];
        let ret = self.reader.read(&mut buf[..]);
        if let Ok(count) = ret {
            if count > 0 {
                self.buffer.extend_from_slice(&buf[0..count]);
            }
        }
        ret
    }
    fn read_write_headers(&mut self) -> IoResult<()> {
        info!("Reading headers");
        loop {
            let count = self.chunk_read()?;
            info!("Count: {}", count);
            if count == 0 {
                break;
            }
            if let Some(_) = self.process_headers() {
                break;
            }
        }
        Ok(())
    }

    fn read_write_no_transfer_encoding(&mut self) -> IoResult<()> {
        loop {
            self.writer.write(self.buffer.as_slice()).unwrap();
            self.buffer.clear();

            let cnt = self.chunk_read()?;
            if cnt == 0 {
                break;
            }
        }

        self.writer.flush()?;
        Ok(())
    }

    fn read_content_length(&mut self, size: usize) -> IoResult<()> {
        let mut read_count = self.buffer.len();
        loop {
            self.writer.write(self.buffer.as_slice()).unwrap();
            self.buffer.clear();

            if read_count >= size {
                break;
            }
            read_count = read_count + self.chunk_read()?;
        }
        Ok(())
    }

    fn read_write_chunk(&mut self) -> IoResult<()> {
        // vars for chunk encoding only
        let mut read_chunk = true;
        let mut read_size = 0;
        let mut header_len: usize;

        'outer: loop {
            while read_chunk {
                header_len = 0;
                {
                    // we read the chunk size to drain
                    let header: Vec<&[u8]> = constants::SPLIT_HEADER_BRE
                        .splitn(self.buffer.as_slice(), 2)
                        .collect();
                    if header.len() == 2 {
                        if let Some(header) = constants::GET_CHUNK_SIZE.captures(header[0]) {
                            if let Some(size) = header.get(1) {
                                let size = size.as_bytes();
                                header_len = size.len() + 2;
                                let size = String::from_utf8_lossy(size).into_owned();
                                read_size = usize::from_str_radix(size.as_str(), 16).unwrap();
                                read_chunk = false;
                            }
                        } else {
                            // else return Error
                            break;
                        }
                    }
                }

                if read_size == 0 {
                    self.buffer.clear(); // should we check that it is '0\r\n' ?
                    break 'outer;
                }

                let buffer: Vec<u8> = self.buffer.drain(header_len..).collect();
                self.buffer = buffer;

                if !read_chunk && self.buffer.len() >= read_size + 2 {
                    let mut buffer: Vec<u8> = self.buffer.drain(read_size..).collect();
                    self.writer.write(self.buffer.as_slice()).unwrap();
                    self.writer.flush()?;

                    let buffer2 = buffer.drain(2..).collect(); // CRLF
                    self.buffer = buffer2;
                    read_chunk = true;
                    read_size = 0;
                }
            }

            let cnt = self.chunk_read()?;
            if cnt == 0 {
                break;
            }
        }

        Ok(())
    }

    fn read_write_body(&mut self) -> IoResult<()> {
        info!("Reading body");
        match self.transfer_encoding {
            TransferEncoding::ContentLength(size) => {
                self.read_content_length(size)?;
            }
            TransferEncoding::Chunked => {
                self.read_write_chunk()?;
            }
            _ => {
                warn!("Neither Content-Length, not chunk is response header");
                self.read_write_no_transfer_encoding()?;
            }
        }

        if self.buffer.len() > 0 {
            let b = String::from_utf8_lossy(self.buffer.as_slice());
            error!("Buffer not clear: {}", b);
        }

        Ok(())
    }

    fn read_write(&mut self) -> IoResult<()> {
        self.read_write_headers()?;
        self.read_write_body()
    }
}

fn log_request(request: &[u8], verbose: bool) {
    if !log_enabled!(Info) && !verbose {
        return;
    }
    let request: Vec<&[u8]> = constants::SPLIT_HEADERS_RE.splitn(request, 2).collect();
    let headers = String::from_utf8_lossy(&request[0]);
    let headers: Vec<&str> = constants::SPLIT_HEADER_RE.split(&headers).collect();
    let bodylen = if request.len() == 2 {
        let body = &request[1];
        body.len()
    } else {
        0
    };
    if log_enabled!(Info) {
        for header in headers {
            info!("> {}", header);
        }
        if bodylen > 0 {
            info!("> [{} bytes]", bodylen);
        }
        info!(">");
    } else if verbose {
        for header in headers {
            writeln!(&mut stderr(), "> {}", header).unwrap();
        }
        if bodylen > 0 {
            writeln!(&mut stderr(), "> [{} bytes]", bodylen).unwrap();
        }
        writeln!(&mut stderr(), ">").unwrap();
    }
}

fn read_buf<T>(client: &mut T, buf: &mut [u8]) -> Vec<u8>
where
    T: Read + Sized,
{
    let mut response: Vec<u8> = Vec::with_capacity(RESPONSE_BUFFER_SIZE);
    while let Ok(count) = client.read(&mut buf[..]) {
        if count > 0 {
            response.extend_from_slice(&buf[0..count]);
        } else {
            break;
        }
    }
    response
}

fn from_http(
    request: &Request,
    client: &mut TcpStream,
    out: &mut Write,
    verbose: bool,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose);

    debug!("Sending request...");
    client.write_all(&raw_request).unwrap();

    debug!("Reading response headers...");

    let mut http_decoder = HttpDecoder::new(out, client);
    http_decoder.read_write()?;
    Ok(())
}

fn from_https(
    request: &Request,
    mut client: &mut TcpStream,
    out: &mut Write,
    verbose: bool,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    let mut response: Vec<u8> = Vec::with_capacity(RESPONSE_BUFFER_SIZE);
    let mut buf = [0; BUFFER_PAGE_SIZE];

    let mut config = ClientConfig::new();
    config
        .root_store
        .add_server_trust_anchors(&webpki_roots::TLS_SERVER_ROOTS);
    let rc_config = Arc::new(config);
    let host = DNSNameRef::try_from_ascii_str(request.host())
        .map_err(|_| CabotError::HostnameParseError(request.host().to_owned()))?;
    let mut tlsclient = ClientSession::new(&rc_config, host);
    let mut is_handshaking = true;

    loop {
        while tlsclient.wants_write() {
            let count = tlsclient.write_tls(&mut client).unwrap();
            debug!("Write {} TLS bytes", count);
        }
        if is_handshaking && !tlsclient.is_handshaking() {
            info!("Handshake complete");
            is_handshaking = false;
            let protocol = tlsclient.get_protocol_version();
            match protocol {
                Some(ProtocolVersion::SSLv2) => {
                    info!("Protocol SSL v2 negociated");
                }
                Some(ProtocolVersion::SSLv3) => {
                    info!("Protocol SSL v3 negociated");
                }
                Some(ProtocolVersion::TLSv1_0) => {
                    info!("Protocol TLS v1.0 negociated");
                }
                Some(ProtocolVersion::TLSv1_1) => {
                    info!("Protocol TLS v1.1 negociated");
                }
                Some(ProtocolVersion::TLSv1_2) => {
                    info!("Protocol TLS v1.2 negociated");
                }
                Some(ProtocolVersion::TLSv1_3) => {
                    info!("Protocol TLS v1.3 negociated");
                }
                Some(ProtocolVersion::Unknown(num)) => {
                    info!("Unknown TLS Protocol negociated: {}", num);
                }
                None => {
                    info!("No TLS Protocol negociated");
                }
            }
            log_request(&raw_request, verbose);
            tlsclient.write_all(&raw_request).unwrap();
            let count = tlsclient.write_tls(&mut client).unwrap();
            debug!("Write {} TLS bytes", count);
        }

        if tlsclient.wants_read() {
            let count = tlsclient.read_tls(&mut client)?;
            debug!("Read {} TLS bytes", count);
            if count == 0 {
                break;
            }

            tlsclient.process_new_packets()?;

            let mut part: Vec<u8> = read_buf(&mut tlsclient, &mut buf);
            response.append(&mut part);
        } else {
            break;
        }
    }

    let mut http_decoder = HttpDecoder::new(out, client);
    http_decoder.buffer = response;
    http_decoder.process_headers().unwrap();
    http_decoder.read_write_body()?;
    Ok(())
}

pub fn http_query(
    request: &Request,
    mut out: &mut Write,
    authorities: &HashMap<String, SocketAddr>,
    verbose: bool,
    ipv4: bool,
    ipv6: bool,
) -> CabotResult<()> {
    debug!(
        "HTTP Query {} {}",
        request.http_method(),
        request.request_uri()
    );

    let authority = request.authority();

    let addr = match authorities.get(authority) {
        Some(val) => {
            info!("Fetch authority {} using autorities map", authority);
            *val
        }
        None => {
            info!("Fetch authority {} using resolver", authority);
            let resolver = Resolver::new(verbose);
            resolver.get_addr(authority, ipv4, ipv6)?
        }
    };

    info!("Connecting to {}", addr);
    let mut client = TcpStream::connect(addr)?;

    client.set_read_timeout(Some(Duration::new(5, 0))).unwrap();

    match request.scheme() {
        "http" => from_http(request, &mut client, &mut out, verbose)?,
        "https" => from_https(request, &mut client, &mut out, verbose)?,
        _ => {
            return Err(CabotError::SchemeError(format!(
                "Unrecognized scheme {}",
                request.scheme()
            )))
        }
    };

    out.flush().unwrap();

    Ok(())
}
<|endoftext|>


<commit_before>//! Contains the HTTP server component.

use std::fs;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::sync::{Arc, Mutex};

use handlebars_iron::{Template, HandlebarsEngine, DirectorySource};
use iron::prelude::*;
use iron::{self, Handler, status};
use mount::Mount;
use serde_json::Value;
use staticfile::Static;

lazy_static! {
    static ref CRATE_ROOT: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
}

/// The HTTP server.
///
/// The server listens on the provided port, rendering the markdown preview when a GET request is
/// received at the server root.
pub struct Server {
    websocket_port: u16,
    styles: StyleConfig,
    working_directory: PathBuf,
}

pub struct StyleConfig {
    pub highlight_theme: String,
    pub css: String,
}

impl Server {
    /// Creates a new server that listens on socket address `addr`.
    pub fn new(working_directory: PathBuf, websocket_port: u16, styles: StyleConfig) -> Server {
        Server {
            working_directory: working_directory,
            websocket_port: websocket_port,
            styles: styles,
        }
    }

    /// Starts the server.
    ///
    /// Once a connection is received, the client will initiate WebSocket connections on
    /// `websocket_port`. If `initial_markdown` is present, it will be displayed on the first
    /// connection.
    pub fn listen<A>(self, address: A, initial_html: &str) -> io::Result<Listening>
    where
        A: ToSocketAddrs,
    {
        let working_directory = Arc::new(Mutex::new(self.working_directory));

        let handler = create_handler(MarkdownPreview {
            template_data: json!({
                "websocket_port": self.websocket_port,
                "initial_html": initial_html,
                "highlight_theme": self.styles.highlight_theme,
                "custom_css": self.styles.css,
            }),
            working_directory: working_directory.clone(),
        });

        let listening = Listening {
            working_directory: working_directory,
            listening: Iron::new(handler).http(address).map_err(|e| {
                io::Error::new(io::ErrorKind::Other, e)
            })?,
        };

        Ok(listening)
    }
}

pub struct Listening {
    listening: iron::Listening,
    working_directory: Arc<Mutex<PathBuf>>,
}

impl Listening {
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        Ok(self.listening.socket)
    }

    pub fn change_working_directory<P>(&mut self, dir: P)
    where
        P: AsRef<Path>,
    {
        let mut working_directory = self.working_directory.lock().unwrap();
        *working_directory = dir.as_ref().to_owned();
    }
}

impl Drop for Listening {
    fn drop(&mut self) {
        self.listening.close().unwrap();
    }
}

/// Wraps the markdown handler with other middleware, such as template rendering and static file
/// serving.
fn create_handler(previewer: MarkdownPreview) -> Box<Handler> {
    let mut chain = Chain::new(previewer);

    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(
        CRATE_ROOT.join("templates/").to_str().unwrap(),
        ".html",
    )));
    if let Err(r) = hbse.reload() {
        panic!("{}", r);
    }

    chain.link_after(hbse);

    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/_static", Static::new(CRATE_ROOT.join("static")));

    Box::new(mount)
}

struct MarkdownPreview {
    template_data: Value,
    working_directory: Arc<Mutex<PathBuf>>,
}

impl Handler for MarkdownPreview {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let url_path = req.url.path().join(&MAIN_SEPARATOR.to_string());

        if url_path.is_empty() {
            Ok(Response::with((
                Template::new("markdown_view", &self.template_data),
                status::Ok,
            )))
        } else {
            let local_cwd = self.working_directory.clone();
            let path = local_cwd.lock().unwrap().join(&url_path);

            match fs::metadata(&path) {
                Ok(ref attr) if attr.is_file() => return Ok(Response::with((path, status::Ok))),
                Err(ref e) if e.kind() != io::ErrorKind::NotFound => {
                    debug!("Error getting metadata for file: '{:?}': {:?}", path, e);
                }
                _ => (),
            }

            Ok(Response::with(status::NotFound))
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron_test;

    use std::path::PathBuf;
    use std::sync::{Arc, Mutex};

    use iron::Headers;
    use self::iron_test::request;

    use super::MarkdownPreview;

    #[test]
    fn simple() {
        let handler = super::create_handler(MarkdownPreview {
            template_data: json!({
                "websocket_port": 1337,
                "initial_markdown": "",
                "highlight_theme": ::DEFAULT_HIGHLIGHT_THEME,
                "custom_css": ::DEFAULT_CSS,
            }),
            working_directory: Arc::new(Mutex::new(PathBuf::new())),
        });

        let response = request::get("http://localhost:3000/", Headers::new(), &handler).unwrap();
        assert!(response.status.unwrap().is_success(), "could not load index");

        let response = request::get(
            "http://localhost:3000/_static/js/markdown_client.js",
            Headers::new(),
            &handler,
        ).unwrap();
        assert!(response.status.unwrap().is_success(), "static file not found");

        let response = request::get(
            "http://localhost:3000/non-existent",
            Headers::new(),
            &handler,
        ).unwrap();
        assert!(response.status.unwrap().is_client_error(), "found non-existent file");
    }
}
<commit_msg>test vendored assets are present<commit_after>//! Contains the HTTP server component.

use std::fs;
use std::io;
use std::net::{SocketAddr, ToSocketAddrs};
use std::path::{Path, PathBuf, MAIN_SEPARATOR};
use std::sync::{Arc, Mutex};

use handlebars_iron::{Template, HandlebarsEngine, DirectorySource};
use iron::prelude::*;
use iron::{self, Handler, status};
use mount::Mount;
use serde_json::Value;
use staticfile::Static;

lazy_static! {
    static ref CRATE_ROOT: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
}

/// The HTTP server.
///
/// The server listens on the provided port, rendering the markdown preview when a GET request is
/// received at the server root.
pub struct Server {
    websocket_port: u16,
    styles: StyleConfig,
    working_directory: PathBuf,
}

pub struct StyleConfig {
    pub highlight_theme: String,
    pub css: String,
}

impl Server {
    /// Creates a new server that listens on socket address `addr`.
    pub fn new(working_directory: PathBuf, websocket_port: u16, styles: StyleConfig) -> Server {
        Server {
            working_directory: working_directory,
            websocket_port: websocket_port,
            styles: styles,
        }
    }

    /// Starts the server.
    ///
    /// Once a connection is received, the client will initiate WebSocket connections on
    /// `websocket_port`. If `initial_markdown` is present, it will be displayed on the first
    /// connection.
    pub fn listen<A>(self, address: A, initial_html: &str) -> io::Result<Listening>
    where
        A: ToSocketAddrs,
    {
        let working_directory = Arc::new(Mutex::new(self.working_directory));

        let handler = create_handler(MarkdownPreview {
            template_data: json!({
                "websocket_port": self.websocket_port,
                "initial_html": initial_html,
                "highlight_theme": self.styles.highlight_theme,
                "custom_css": self.styles.css,
            }),
            working_directory: working_directory.clone(),
        });

        let listening = Listening {
            working_directory: working_directory,
            listening: Iron::new(handler).http(address).map_err(|e| {
                io::Error::new(io::ErrorKind::Other, e)
            })?,
        };

        Ok(listening)
    }
}

pub struct Listening {
    listening: iron::Listening,
    working_directory: Arc<Mutex<PathBuf>>,
}

impl Listening {
    pub fn local_addr(&self) -> io::Result<SocketAddr> {
        Ok(self.listening.socket)
    }

    pub fn change_working_directory<P>(&mut self, dir: P)
    where
        P: AsRef<Path>,
    {
        let mut working_directory = self.working_directory.lock().unwrap();
        *working_directory = dir.as_ref().to_owned();
    }
}

impl Drop for Listening {
    fn drop(&mut self) {
        self.listening.close().unwrap();
    }
}

/// Wraps the markdown handler with other middleware, such as template rendering and static file
/// serving.
fn create_handler(previewer: MarkdownPreview) -> Box<Handler> {
    let mut chain = Chain::new(previewer);

    let mut hbse = HandlebarsEngine::new();
    hbse.add(Box::new(DirectorySource::new(
        CRATE_ROOT.join("templates/").to_str().unwrap(),
        ".html",
    )));
    if let Err(r) = hbse.reload() {
        panic!("{}", r);
    }

    chain.link_after(hbse);

    let mut mount = Mount::new();
    mount.mount("/", chain);
    mount.mount("/_static", Static::new(CRATE_ROOT.join("static")));

    Box::new(mount)
}

struct MarkdownPreview {
    template_data: Value,
    working_directory: Arc<Mutex<PathBuf>>,
}

impl Handler for MarkdownPreview {
    fn handle(&self, req: &mut Request) -> IronResult<Response> {
        let url_path = req.url.path().join(&MAIN_SEPARATOR.to_string());

        if url_path.is_empty() {
            Ok(Response::with((
                Template::new("markdown_view", &self.template_data),
                status::Ok,
            )))
        } else {
            let local_cwd = self.working_directory.clone();
            let path = local_cwd.lock().unwrap().join(&url_path);

            match fs::metadata(&path) {
                Ok(ref attr) if attr.is_file() => return Ok(Response::with((path, status::Ok))),
                Err(ref e) if e.kind() != io::ErrorKind::NotFound => {
                    debug!("Error getting metadata for file: '{:?}': {:?}", path, e);
                }
                _ => (),
            }

            Ok(Response::with(status::NotFound))
        }
    }
}

#[cfg(test)]
mod tests {
    extern crate iron_test;

    use std::path::PathBuf;
    use std::sync::{Arc, Mutex};

    use iron::Headers;
    use self::iron_test::request;

    use super::MarkdownPreview;

    #[test]
    fn simple() {
        let handler = super::create_handler(MarkdownPreview {
            template_data: json!({
                "websocket_port": 1337,
                "initial_markdown": "",
                "highlight_theme": ::DEFAULT_HIGHLIGHT_THEME,
                "custom_css": ::DEFAULT_CSS,
            }),
            working_directory: Arc::new(Mutex::new(PathBuf::new())),
        });

        let response = request::get("http://localhost:3000/", Headers::new(), &handler).unwrap();
        assert!(response.status.unwrap().is_success(), "could not load index");

        let response = request::get(
            "http://localhost:3000/_static/js/markdown_client.js",
            Headers::new(),
            &handler,
        ).unwrap();
        assert!(response.status.unwrap().is_success(), "static file not found");

        let response = request::get(
            "http://localhost:3000/non-existent",
            Headers::new(),
            &handler,
        ).unwrap();
        assert!(response.status.unwrap().is_client_error(), "found non-existent file");
    }

    #[test]
    fn vendored() {
        let handler = super::create_handler(MarkdownPreview {
            template_data: json!({
                "websocket_port": 1337,
                "initial_markdown": "",
                "highlight_theme": ::DEFAULT_HIGHLIGHT_THEME,
                "custom_css": ::DEFAULT_CSS,
            }),
            working_directory: Arc::new(Mutex::new(PathBuf::new())),
        });

        let response = request::get(
            "http://localhost:3000/_static/vendor/highlight.js/highlight.pack.js",
            Headers::new(),
            &handler,
        ).unwrap();
        assert!(response.status.unwrap().is_success(), "vendored file not found");
    }
}
<|endoftext|>


<commit_before>use std::fmt;
use std::str;

use column::Column;
use condition::ConditionExpression;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
use nom::IResult;
use select::{JoinClause, SelectStatement};
use table::Table;

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinRightSide {
    /// A single table.
    Table(Table),
    /// A comma-separated (and implicitly joined) sequence of tables.
    Tables(Vec<Table>),
    /// A nested selection, represented as (query, alias).
    NestedSelect(Box<SelectStatement>, Option<String>),
    /// A nested join clause.
    NestedJoin(Box<JoinClause>),
}

impl fmt::Display for JoinRightSide {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinRightSide::Table(ref t) => write!(f, "{}", t)?,
            JoinRightSide::NestedSelect(ref q, ref a) => {
                write!(f, "({})", q)?;
                if a.is_some() {
                    write!(f, " AS {}", a.as_ref().unwrap())?;
                }
            }
            JoinRightSide::NestedJoin(ref jc) => write!(f, "({})", jc)?,
            _ => unimplemented!(),
        }
        Ok(())
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinOperator {
    Join,
    LeftJoin,
    LeftOuterJoin,
    InnerJoin,
    CrossJoin,
    StraightJoin,
}

impl fmt::Display for JoinOperator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinOperator::Join => write!(f, "JOIN")?,
            JoinOperator::LeftJoin => write!(f, "LEFT JOIN")?,
            JoinOperator::LeftOuterJoin => write!(f, "LEFT OUTER JOIN")?,
            JoinOperator::InnerJoin => write!(f, "INNER JOIN")?,
            JoinOperator::CrossJoin => write!(f, "CROSS JOIN")?,
            JoinOperator::StraightJoin => write!(f, "STRAIGHT JOIN")?,
        }
        Ok(())
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinConstraint {
    On(ConditionExpression),
    Using(Vec<Column>),
}

impl fmt::Display for JoinConstraint {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinConstraint::On(ref ce) => write!(f, "ON {}", ce)?,
            JoinConstraint::Using(ref columns) => write!(
                f,
                "USING ({})",
                columns
                    .iter()
                    .map(|c| format!("{}", c))
                    .collect::<Vec<_>>()
                    .join(", ")
            )?,
        }
        Ok(())
    }
}

// Parse binary comparison operators
pub fn join_operator(i: &[u8]) -> IResult<&[u8], JoinOperator> {
    alt((
        map(tag_no_case("join"), |_| JoinOperator::Join),
        map(tag_no_case("left join"), |_| JoinOperator::LeftJoin),
        map(tag_no_case("left outer join"), |_| {
            JoinOperator::LeftOuterJoin
        }),
        map(tag_no_case("inner join"), |_| JoinOperator::InnerJoin),
        map(tag_no_case("cross join"), |_| JoinOperator::CrossJoin),
        map(tag_no_case("straight_join"), |_| JoinOperator::StraightJoin),
    ))(i)
}

#[cfg(test)]
mod tests {
    use super::*;
    use common::{FieldDefinitionExpression, Operator};
    use condition::ConditionBase::*;
    use condition::ConditionExpression::{self, *};
    use condition::ConditionTree;
    use select::{selection, JoinClause, SelectStatement};

    #[test]
    fn inner_join() {
        let qstring = "SELECT tags.* FROM tags \
                       INNER JOIN taggings ON tags.id = taggings.tag_id";

        let res = selection(qstring.as_bytes());

        let ct = ConditionTree {
            left: Box::new(Base(Field(Column::from("tags.id")))),
            right: Box::new(Base(Field(Column::from("taggings.tag_id")))),
            operator: Operator::Equal,
        };
        let join_cond = ConditionExpression::ComparisonOp(ct);
        let expected_stmt = SelectStatement {
            tables: vec![Table::from("tags")],
            fields: vec![FieldDefinitionExpression::AllInTable("tags".into())],
            join: vec![JoinClause {
                operator: JoinOperator::InnerJoin,
                right: JoinRightSide::Table(Table::from("taggings")),
                constraint: JoinConstraint::On(join_cond),
            }],
            ..Default::default()
        };

        let q = res.unwrap().1;
        assert_eq!(q, expected_stmt);
        assert_eq!(qstring, format!("{}", q));
    }
}
<commit_msg>Support right join<commit_after>use std::fmt;
use std::str;

use column::Column;
use condition::ConditionExpression;
use nom::branch::alt;
use nom::bytes::complete::tag_no_case;
use nom::combinator::map;
use nom::IResult;
use select::{JoinClause, SelectStatement};
use table::Table;

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinRightSide {
    /// A single table.
    Table(Table),
    /// A comma-separated (and implicitly joined) sequence of tables.
    Tables(Vec<Table>),
    /// A nested selection, represented as (query, alias).
    NestedSelect(Box<SelectStatement>, Option<String>),
    /// A nested join clause.
    NestedJoin(Box<JoinClause>),
}

impl fmt::Display for JoinRightSide {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinRightSide::Table(ref t) => write!(f, "{}", t)?,
            JoinRightSide::NestedSelect(ref q, ref a) => {
                write!(f, "({})", q)?;
                if a.is_some() {
                    write!(f, " AS {}", a.as_ref().unwrap())?;
                }
            }
            JoinRightSide::NestedJoin(ref jc) => write!(f, "({})", jc)?,
            _ => unimplemented!(),
        }
        Ok(())
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinOperator {
    Join,
    LeftJoin,
    LeftOuterJoin,
    RightJoin,
    InnerJoin,
    CrossJoin,
    StraightJoin,
}

impl fmt::Display for JoinOperator {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinOperator::Join => write!(f, "JOIN")?,
            JoinOperator::LeftJoin => write!(f, "LEFT JOIN")?,
            JoinOperator::LeftOuterJoin => write!(f, "LEFT OUTER JOIN")?,
            JoinOperator::RightJoin => write!(f, "RIGHT JOIN")?,
            JoinOperator::InnerJoin => write!(f, "INNER JOIN")?,
            JoinOperator::CrossJoin => write!(f, "CROSS JOIN")?,
            JoinOperator::StraightJoin => write!(f, "STRAIGHT JOIN")?,
        }
        Ok(())
    }
}

#[derive(Clone, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub enum JoinConstraint {
    On(ConditionExpression),
    Using(Vec<Column>),
}

impl fmt::Display for JoinConstraint {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            JoinConstraint::On(ref ce) => write!(f, "ON {}", ce)?,
            JoinConstraint::Using(ref columns) => write!(
                f,
                "USING ({})",
                columns
                    .iter()
                    .map(|c| format!("{}", c))
                    .collect::<Vec<_>>()
                    .join(", ")
            )?,
        }
        Ok(())
    }
}

// Parse binary comparison operators
pub fn join_operator(i: &[u8]) -> IResult<&[u8], JoinOperator> {
    alt((
        map(tag_no_case("join"), |_| JoinOperator::Join),
        map(tag_no_case("left join"), |_| JoinOperator::LeftJoin),
        map(tag_no_case("left outer join"), |_| {
            JoinOperator::LeftOuterJoin
        }),
        map(tag_no_case("right join"), |_| JoinOperator::RightJoin),
        map(tag_no_case("inner join"), |_| JoinOperator::InnerJoin),
        map(tag_no_case("cross join"), |_| JoinOperator::CrossJoin),
        map(tag_no_case("straight_join"), |_| JoinOperator::StraightJoin),
    ))(i)
}

#[cfg(test)]
mod tests {
    use super::*;
    use common::{FieldDefinitionExpression, Operator};
    use condition::ConditionBase::*;
    use condition::ConditionExpression::{self, *};
    use condition::ConditionTree;
    use select::{selection, JoinClause, SelectStatement};

    #[test]
    fn inner_join() {
        let qstring = "SELECT tags.* FROM tags \
                       INNER JOIN taggings ON tags.id = taggings.tag_id";

        let res = selection(qstring.as_bytes());

        let ct = ConditionTree {
            left: Box::new(Base(Field(Column::from("tags.id")))),
            right: Box::new(Base(Field(Column::from("taggings.tag_id")))),
            operator: Operator::Equal,
        };
        let join_cond = ConditionExpression::ComparisonOp(ct);
        let expected_stmt = SelectStatement {
            tables: vec![Table::from("tags")],
            fields: vec![FieldDefinitionExpression::AllInTable("tags".into())],
            join: vec![JoinClause {
                operator: JoinOperator::InnerJoin,
                right: JoinRightSide::Table(Table::from("taggings")),
                constraint: JoinConstraint::On(join_cond),
            }],
            ..Default::default()
        };

        let q = res.unwrap().1;
        assert_eq!(q, expected_stmt);
        assert_eq!(qstring, format!("{}", q));
    }
}
<|endoftext|>


<commit_before>//! Procedural macro implementing `#[derive(EnumMap)]`
//!
//! This is supposed to used with `enum-map` crate, which provides the
//! actual usage documentation.

#![recursion_limit = "128"]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;

use std::iter;

use syn::{Data, DataEnum, DeriveInput, Fields, Ident, Variant};

fn generate_enum_code(name: Ident, data_enum: DataEnum) -> quote::Tokens {
    let enum_count = data_enum.variants.len();
    let mut has_discriminants = false;

    for &Variant { ref fields, ref discriminant, .. } in &data_enum.variants {
        match *fields {
            Fields::Unit => (),
            _ => panic!("#[derive(EnumMap)] requires C style style enum"),
        }

        if discriminant.is_some() {
            has_discriminants = true;
        }
    }

    let variants_names_a = data_enum.variants.iter().map(|variant| &variant.ident);
    let variants_names_b = data_enum.variants.iter().map(|variant| &variant.ident);
    let repeat_name_a = iter::repeat(name);
    let repeat_name_b = repeat_name_a.clone();
    let counter = 0..enum_count;

    let to_usize = if enum_count == 0 || has_discriminants {
        let variants_names = data_enum.variants.iter().map(|variant| &variant.ident);
        let repeat_name = repeat_name_a.clone();
        let counter = counter.clone();

        quote! {
            match self {
                #(
                    #repeat_name::#variants_names => #counter,
                )*
            }
        }
    } else {
        quote! { self as usize }
    };

    quote! {
        impl<V> ::enum_map::Internal<V> for #name {
            type Array = [V; #enum_count];

            fn slice(array: &Self::Array) -> &[V] {
                array
            }

            fn slice_mut(array: &mut Self::Array) -> &mut [V] {
                array
            }

            fn from_usize(value: usize) -> Self {
                match value {
                    #(
                        #counter => #repeat_name_a::#variants_names_a,
                    )*
                    _ => unreachable!()
                }
            }
            fn to_usize(self) -> usize {
                #to_usize
            }
            fn from_function<F: FnMut(Self) -> V>(mut _f: F) -> Self::Array {
                [#(
                    _f(#repeat_name_b::#variants_names_b),
                )*]
            }
        }
    }
}

#[proc_macro_derive(EnumMap)]
pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input: DeriveInput = syn::parse(input).unwrap();

    let result = match input.data {
        Data::Enum(data_enum) => generate_enum_code(input.ident, data_enum),
        _ => panic!("#[derive(EnumMap)] is only defined for enums"),
    };

    result.into()
}
<commit_msg>Reformat code<commit_after>//! Procedural macro implementing `#[derive(EnumMap)]`
//!
//! This is supposed to used with `enum-map` crate, which provides the
//! actual usage documentation.

#![recursion_limit = "128"]

extern crate proc_macro;
extern crate proc_macro2;
#[macro_use]
extern crate quote;
extern crate syn;

use std::iter;

use syn::{Data, DataEnum, DeriveInput, Fields, Ident, Variant};

fn generate_enum_code(name: Ident, data_enum: DataEnum) -> quote::Tokens {
    let enum_count = data_enum.variants.len();
    let mut has_discriminants = false;

    for &Variant {
        ref fields,
        ref discriminant,
        ..
    } in &data_enum.variants
    {
        match *fields {
            Fields::Unit => (),
            _ => panic!("#[derive(EnumMap)] requires C style style enum"),
        }

        if discriminant.is_some() {
            has_discriminants = true;
        }
    }

    let variants_names_a = data_enum.variants.iter().map(|variant| &variant.ident);
    let variants_names_b = data_enum.variants.iter().map(|variant| &variant.ident);
    let repeat_name_a = iter::repeat(name);
    let repeat_name_b = repeat_name_a.clone();
    let counter = 0..enum_count;

    let to_usize = if enum_count == 0 || has_discriminants {
        let variants_names = data_enum.variants.iter().map(|variant| &variant.ident);
        let repeat_name = repeat_name_a.clone();
        let counter = counter.clone();

        quote! {
            match self {
                #(
                    #repeat_name::#variants_names => #counter,
                )*
            }
        }
    } else {
        quote! { self as usize }
    };

    quote! {
        impl<V> ::enum_map::Internal<V> for #name {
            type Array = [V; #enum_count];

            fn slice(array: &Self::Array) -> &[V] {
                array
            }

            fn slice_mut(array: &mut Self::Array) -> &mut [V] {
                array
            }

            fn from_usize(value: usize) -> Self {
                match value {
                    #(
                        #counter => #repeat_name_a::#variants_names_a,
                    )*
                    _ => unreachable!()
                }
            }
            fn to_usize(self) -> usize {
                #to_usize
            }
            fn from_function<F: FnMut(Self) -> V>(mut _f: F) -> Self::Array {
                [#(
                    _f(#repeat_name_b::#variants_names_b),
                )*]
            }
        }
    }
}

#[proc_macro_derive(EnumMap)]
pub fn derive_enum_map(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
    let input: DeriveInput = syn::parse(input).unwrap();

    let result = match input.data {
        Data::Enum(data_enum) => generate_enum_code(input.ident, data_enum),
        _ => panic!("#[derive(EnumMap)] is only defined for enums"),
    };

    result.into()
}
<|endoftext|>


<commit_before>use conf;
use itertools::Itertools;
use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::front::map::Node;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use std::borrow::Cow;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std;
use syntax::ast::MetaItemKind;
use syntax::ast::{Attribute, FloatTy};
use wait_timeout::ChildExt;

#[derive(Debug)]
pub struct Herbie {
    conf: Option<conf::Conf>,
    initialized: bool,
    subs: Vec<(LispExpr, LispExpr)>,
}

#[derive(Debug)]
pub enum InitError {
    Conf {
        error: conf::ConfError,
    },
    SQL {
        error: sql::Error,
    },
}

impl From<conf::ConfError> for InitError {
    fn from(err: conf::ConfError) -> InitError {
        InitError::Conf { error: err }
    }
}

impl From<sql::Error> for InitError {
    fn from(err: sql::Error) -> InitError {
        InitError::SQL { error: err }
    }
}

impl std::fmt::Display for InitError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        match *self {
            InitError::Conf { ref error } => write!(f, "Configuration error: {}", error),
            InitError::SQL { ref error } => write!(f, "Got SQL error: {}", error),
        }
    }
}

impl Herbie {
    pub fn new() -> Herbie {
        Herbie {
            conf: None,
            initialized: false,
            subs: Vec::new(),
        }
    }

    pub fn init(&mut self) -> Result<(), InitError> {
        if self.initialized {
            return Ok(())
        }

        self.initialized = true;

        let conf = try!(conf::read_conf());
        let conn = try!(sql::Connection::open_with_flags(
            conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_ONLY
        ));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect();

        self.conf = Some(conf);

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        fn is_herbie_ignore(attr: &Attribute) -> bool {
            if let MetaItemKind::Word(ref word) = attr.node.value.node {
                word == &"herbie_ignore"
            }
            else {
                false
            }
        }

        let attrs = match cx.tcx.map.find(cx.tcx.map.get_parent(expr.id)) {
            Some(Node::NodeItem(item)) => &item.attrs,
            Some(Node::NodeTraitItem(item)) => &item.attrs,
            Some(Node::NodeImplItem(item)) => &item.attrs,
            _ => panic!("In herbie-lint: how did I get there?"),
        };

        if attrs.iter().any(is_herbie_ignore) {
            return;
        }

        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::F64) {
            return;
        }

        if let Err(err) =  self.init() {
            cx.span_lint_note(
                HERBIE,
                cx.krate.span,
                "Could not initialize Herbie-Lint",
                cx.krate.span,
                &err.to_string()
            );
            return;
        }

        let mut got_match = false;
        for &(ref cmdin, ref cmdout) in &self.subs {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                report(cx, expr, cmdout, &bindings);
                got_match = true;
            }
        }

        let conf = self.conf.as_ref().expect("Configuration should be read by now");
        if !got_match && conf.use_herbie != conf::UseHerbieConf::No {
            if let Err(err) = try_with_herbie(cx, expr, &conf) {
                cx.span_lint(HERBIE, expr.span, &err);
            }
        }
    }
}

fn try_with_herbie(cx: &LateContext, expr: &Expr, conf: &conf::Conf) -> Result<(), Cow<'static, str>> {
    let (lisp_expr, nb_ids, bindings) = match LispExpr::from_expr(expr) {
        Some(r) => r,
        // not an error, the expression might for example contain a function unknown to Herbie
        None => return Ok(()),
    };

    if lisp_expr.depth() <= 2 {
        return Ok(());
    }

    let seed: &str = &conf.herbie_seed;
    let mut command = Command::new("herbie-inout");
    let command = command
        .arg("--seed").arg(seed)
        .arg("-o").arg("rules:numerics")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
    ;

    let mut child = match command.spawn() {
        Ok(child) => child,
        Err(err) => {
            return if conf.use_herbie == conf::UseHerbieConf::Yes {
                // TODO: wiki
                Err(format!("Could not call Herbie: {}", err).into())
            }
            else {
                Ok(())
            };
        }
    };

    // TODO: link to wiki about Herbie.toml
    cx.sess().diagnostic().span_note_without_error(
        expr.span,
        "Calling Herbie on the following expression, it might take a while"
    );

    let params = (0..nb_ids).map(|id| format!("herbie{}", id)).join(" ");
    let cmdin = lisp_expr.to_lisp("herbie");
    let lisp_expr = format!("(lambda ({}) {})\n", params, cmdin);
    let lisp_expr = lisp_expr.as_bytes();
    child.stdin
        .as_mut().expect("Herbie-inout's stdin not captured")
        .write(lisp_expr).expect("Could not write on herbie-inout's stdin")
    ;

    match conf.timeout {
        Some(timeout) => {
            match child.wait_timeout_ms(timeout*1000) {
                Ok(Some(status)) if status.success() => (),
                Ok(Some(status)) => {
                    return Err(format!("herbie-inout did not return successfully: status={}", status).into());
                }
                Ok(None) => {
                    cx.sess().diagnostic().span_note_without_error(expr.span, "Herbie timed out");
                    return Ok(());
                }
                Err(err) => {
                    return Err(format!("herbie-inout did not return successfully: {}", err).into());
                }
            }
        }
        None => {
            match child.wait() {
                Ok(status) if status.success() => (),
                Ok(status) => {
                    return Err(format!("herbie-inout did not return successfully: status={}", status).into());
                }
                Err(err) => {
                    return Err(format!("herbie-inout did not return successfully: {}", err).into());
                }
            }
        }
    }

    let mut stdout = if let Some(output) = child.stdout {
        output
    }
    else {
        return Err("cannot capture herbie-inout output".into());
    };

    let mut output = String::new();
    if let Err(err) = stdout.read_to_string(&mut output) {
        return Err(format!("cannot read output: {}", err).into());
    }

    let mut output = output.lines();

    let parse_error = |s: Option<&str>| -> Option<f64> {
        match s {
            Some(s) => {
                match s.split(' ').last().map(str::parse::<f64>) {
                    Some(Ok(f)) => Some(f),
                    _ => None,
                }
            }
            _ => None,
        }
    };

    let (errin, errout, cmdout) = match (parse_error(output.next()), parse_error(output.next()), output.next()) {
        (Some(errin), Some(errout), Some(cmdout)) => {
            (errin, errout, cmdout)
        }
        _ => {
            return Err("Could not parse herbie-inout output".into())
        }
    };

    if errin <= errout {
        return Ok(());
    }

    let mut parser = lisp::Parser::new();
    let cmdout = match parser.parse(cmdout) {
        Ok(cmdout) => cmdout,
        _ => return Err("Could not understand herbie-inout cmdout".into()),
    };

    report(cx, expr, &cmdout, &bindings);
    save(conf, &cmdin, &cmdout, "", errin, errout)
        .map_err(|err| format!("Could not save database, got SQL error {}", err).into())
}

fn report(cx: &LateContext, expr: &Expr, cmdout: &LispExpr, bindings: &lisp::MatchBindings) {
    cx.struct_span_lint(HERBIE, expr.span, "Numerically unstable expression")
      .span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings))
      .emit();
}

fn save(
    conf: &conf::Conf,
    cmdin: &str, cmdout: &LispExpr,
    seed: &str,
    errin: f64, errout: f64
) -> Result<(), sql::Error> {
    let conn = try!(sql::Connection::open_with_flags(
        conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_WRITE
    ));

    try!(conn.execute("INSERT INTO HerbieResults (cmdin, cmdout, opts, errin, errout)
                       VALUES ($1, $2, $3, $4, $5)",
                       &[&cmdin, &cmdout.to_lisp("herbie"), &seed, &errin, &errout]));

    Ok(())
}
<commit_msg>Use Duration for timeout<commit_after>use conf;
use itertools::Itertools;
use lisp::LispExpr;
use lisp;
use rusqlite as sql;
use rustc::front::map::Node;
use rustc::lint::{LateContext, LintArray, LintContext, LintPass, LateLintPass};
use rustc::middle::ty::TypeVariants;
use rustc_front::hir::*;
use std::borrow::Cow;
use std::io::{Read, Write};
use std::process::{Command, Stdio};
use std::time::Duration;
use std;
use syntax::ast::MetaItemKind;
use syntax::ast::{Attribute, FloatTy};
use wait_timeout::ChildExt;

#[derive(Debug)]
pub struct Herbie {
    conf: Option<conf::Conf>,
    initialized: bool,
    subs: Vec<(LispExpr, LispExpr)>,
}

#[derive(Debug)]
pub enum InitError {
    Conf {
        error: conf::ConfError,
    },
    SQL {
        error: sql::Error,
    },
}

impl From<conf::ConfError> for InitError {
    fn from(err: conf::ConfError) -> InitError {
        InitError::Conf { error: err }
    }
}

impl From<sql::Error> for InitError {
    fn from(err: sql::Error) -> InitError {
        InitError::SQL { error: err }
    }
}

impl std::fmt::Display for InitError {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        match *self {
            InitError::Conf { ref error } => write!(f, "Configuration error: {}", error),
            InitError::SQL { ref error } => write!(f, "Got SQL error: {}", error),
        }
    }
}

impl Herbie {
    pub fn new() -> Herbie {
        Herbie {
            conf: None,
            initialized: false,
            subs: Vec::new(),
        }
    }

    pub fn init(&mut self) -> Result<(), InitError> {
        if self.initialized {
            return Ok(())
        }

        self.initialized = true;

        let conf = try!(conf::read_conf());
        let conn = try!(sql::Connection::open_with_flags(
            conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_ONLY
        ));
        let mut query = try!(conn.prepare("SELECT * FROM HerbieResults"));

        self.subs = try!(query.query(&[])).filter_map(|row| {
            match row {
                Ok(row) => {
                    let cmdin : String = row.get(1);
                    let cmdout : String = row.get(2);
                    // row.get(3) is opts ↔ Herbies options
                    let errin = row.get_checked(4).unwrap_or(0.);
                    let errout = row.get_checked(5).unwrap_or(0.);

                    if cmdin == cmdout || errin <= errout {
                        return None;
                    }

                    let mut parser = lisp::Parser::new();
                    match parser.parse(&cmdin) {
                        Ok(cmdin) => {
                            match parser.parse(&cmdout) {
                                Ok(cmdout) => {
                                    Some((cmdin, cmdout))
                                }
                                Err(..) => None,
                            }
                        }
                        Err(..) => None,
                    }
                }
                Err(..) => None,
            }
        }).collect();

        self.conf = Some(conf);

        Ok(())
    }

}

declare_lint!(pub HERBIE, Warn,
              "checks for numerical instability");

impl LintPass for Herbie {
    fn get_lints(&self) -> LintArray {
        lint_array!(HERBIE)
    }
}

impl LateLintPass for Herbie {
    fn check_expr(&mut self, cx: &LateContext, expr: &Expr) {
        fn is_herbie_ignore(attr: &Attribute) -> bool {
            if let MetaItemKind::Word(ref word) = attr.node.value.node {
                word == &"herbie_ignore"
            }
            else {
                false
            }
        }

        let attrs = match cx.tcx.map.find(cx.tcx.map.get_parent(expr.id)) {
            Some(Node::NodeItem(item)) => &item.attrs,
            Some(Node::NodeTraitItem(item)) => &item.attrs,
            Some(Node::NodeImplItem(item)) => &item.attrs,
            _ => panic!("In herbie-lint: how did I get there?"),
        };

        if attrs.iter().any(is_herbie_ignore) {
            return;
        }

        let ty = cx.tcx.expr_ty(expr);

        if ty.sty != TypeVariants::TyFloat(FloatTy::F64) {
            return;
        }

        if let Err(err) =  self.init() {
            cx.span_lint_note(
                HERBIE,
                cx.krate.span,
                "Could not initialize Herbie-Lint",
                cx.krate.span,
                &err.to_string()
            );
            return;
        }

        let mut got_match = false;
        for &(ref cmdin, ref cmdout) in &self.subs {
            if let Some(bindings) = LispExpr::match_expr(expr, cmdin) {
                report(cx, expr, cmdout, &bindings);
                got_match = true;
            }
        }

        let conf = self.conf.as_ref().expect("Configuration should be read by now");
        if !got_match && conf.use_herbie != conf::UseHerbieConf::No {
            if let Err(err) = try_with_herbie(cx, expr, &conf) {
                cx.span_lint(HERBIE, expr.span, &err);
            }
        }
    }
}

fn try_with_herbie(cx: &LateContext, expr: &Expr, conf: &conf::Conf) -> Result<(), Cow<'static, str>> {
    let (lisp_expr, nb_ids, bindings) = match LispExpr::from_expr(expr) {
        Some(r) => r,
        // not an error, the expression might for example contain a function unknown to Herbie
        None => return Ok(()),
    };

    if lisp_expr.depth() <= 2 {
        return Ok(());
    }

    let seed: &str = &conf.herbie_seed;
    let mut command = Command::new("herbie-inout");
    let command = command
        .arg("--seed").arg(seed)
        .arg("-o").arg("rules:numerics")
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped())
    ;

    let mut child = match command.spawn() {
        Ok(child) => child,
        Err(err) => {
            return if conf.use_herbie == conf::UseHerbieConf::Yes {
                // TODO: wiki
                Err(format!("Could not call Herbie: {}", err).into())
            }
            else {
                Ok(())
            };
        }
    };

    // TODO: link to wiki about Herbie.toml
    cx.sess().diagnostic().span_note_without_error(
        expr.span,
        "Calling Herbie on the following expression, it might take a while"
    );

    let params = (0..nb_ids).map(|id| format!("herbie{}", id)).join(" ");
    let cmdin = lisp_expr.to_lisp("herbie");
    let lisp_expr = format!("(lambda ({}) {})\n", params, cmdin);
    let lisp_expr = lisp_expr.as_bytes();
    child.stdin
        .as_mut().expect("Herbie-inout's stdin not captured")
        .write(lisp_expr).expect("Could not write on herbie-inout's stdin")
    ;

    match conf.timeout {
        Some(timeout) => {
            match child.wait_timeout(Duration::from_secs(timeout as u64)) {
                Ok(Some(status)) if status.success() => (),
                Ok(Some(status)) => {
                    return Err(format!("herbie-inout did not return successfully: status={}", status).into());
                }
                Ok(None) => {
                    cx.sess().diagnostic().span_note_without_error(expr.span, "Herbie timed out");
                    return Ok(());
                }
                Err(err) => {
                    return Err(format!("herbie-inout did not return successfully: {}", err).into());
                }
            }
        }
        None => {
            match child.wait() {
                Ok(status) if status.success() => (),
                Ok(status) => {
                    return Err(format!("herbie-inout did not return successfully: status={}", status).into());
                }
                Err(err) => {
                    return Err(format!("herbie-inout did not return successfully: {}", err).into());
                }
            }
        }
    }

    let mut stdout = if let Some(output) = child.stdout {
        output
    }
    else {
        return Err("cannot capture herbie-inout output".into());
    };

    let mut output = String::new();
    if let Err(err) = stdout.read_to_string(&mut output) {
        return Err(format!("cannot read output: {}", err).into());
    }

    let mut output = output.lines();

    let parse_error = |s: Option<&str>| -> Option<f64> {
        match s {
            Some(s) => {
                match s.split(' ').last().map(str::parse::<f64>) {
                    Some(Ok(f)) => Some(f),
                    _ => None,
                }
            }
            _ => None,
        }
    };

    let (errin, errout, cmdout) = match (parse_error(output.next()), parse_error(output.next()), output.next()) {
        (Some(errin), Some(errout), Some(cmdout)) => {
            (errin, errout, cmdout)
        }
        _ => {
            return Err("Could not parse herbie-inout output".into())
        }
    };

    if errin <= errout {
        return Ok(());
    }

    let mut parser = lisp::Parser::new();
    let cmdout = match parser.parse(cmdout) {
        Ok(cmdout) => cmdout,
        _ => return Err("Could not understand herbie-inout cmdout".into()),
    };

    report(cx, expr, &cmdout, &bindings);
    save(conf, &cmdin, &cmdout, "", errin, errout)
        .map_err(|err| format!("Could not save database, got SQL error {}", err).into())
}

fn report(cx: &LateContext, expr: &Expr, cmdout: &LispExpr, bindings: &lisp::MatchBindings) {
    cx.struct_span_lint(HERBIE, expr.span, "Numerically unstable expression")
      .span_suggestion(expr.span, "Try this", cmdout.to_rust(cx, &bindings))
      .emit();
}

fn save(
    conf: &conf::Conf,
    cmdin: &str, cmdout: &LispExpr,
    seed: &str,
    errin: f64, errout: f64
) -> Result<(), sql::Error> {
    let conn = try!(sql::Connection::open_with_flags(
        conf.db_path.as_ref(), sql::SQLITE_OPEN_READ_WRITE
    ));

    try!(conn.execute("INSERT INTO HerbieResults (cmdin, cmdout, opts, errin, errout)
                       VALUES ($1, $2, $3, $4, $5)",
                       &[&cmdin, &cmdout.to_lisp("herbie"), &seed, &errin, &errout]));

    Ok(())
}
<|endoftext|>


<commit_before>extern crate iron;
#[macro_use]
extern crate router;
extern crate dotenv;
extern crate sbb_telegram_bot;

use dotenv::dotenv;
use iron::prelude::*;
use sbb_telegram_bot::{route, util};
use sbb_telegram_bot::api::twitter::user_timeline;
use std::thread;
use std::time::Duration;

fn main() {
    dotenv().ok();
    thread::spawn(broadcast_loop);
    let router = router!(telegram: post "/sbb/telegram" => route::telegram);
    Iron::new(router).http("localhost:3001").unwrap();
}

fn broadcast_loop() {
    let mut last_id = 0;
    loop {
        thread::sleep(Duration::from_secs(10));
        let tweets = user_timeline("railinfo_sbb", 1).unwrap();
        let tweet = &tweets[0];
        if tweet.id != last_id {
            last_id = tweet.id;
            util::broadcast(&tweet.text).unwrap();
        }
    }
}
<commit_msg>Strip broadcastet tweet of irrelevant data<commit_after>extern crate iron;
#[macro_use]
extern crate router;
extern crate dotenv;
extern crate sbb_telegram_bot;

use dotenv::dotenv;
use iron::prelude::*;
use sbb_telegram_bot::{route, util};
use sbb_telegram_bot::api::twitter::user_timeline;
use std::thread;
use std::time::Duration;

fn main() {
    dotenv().ok();
    thread::spawn(broadcast_loop);
    let router = router!(telegram: post "/sbb/telegram" => route::telegram);
    Iron::new(router).http("localhost:3001").unwrap();
}

fn broadcast_loop() {
    let mut last_id = 0;
    loop {
        thread::sleep(Duration::from_secs(10));
        let tweets = user_timeline("railinfo_sbb", 1).unwrap();
        let tweet = &tweets[0];
        if tweet.id != last_id {
            last_id = tweet.id;
            let mut txt = tweet.text.clone();
            if let Some(pos) = txt.find("http") {
                txt = txt[..pos].to_owned();
            }
            util::broadcast(&txt).unwrap();
        }
    }
}
<|endoftext|>


<commit_before>mod words;

mod sort;
use sort::{HeapPopDrain};

use std::env;
use std::collections::{BinaryHeap};

fn matches(pat: &String, line: &str) -> bool {
    fn check<P, L>(mut pc: P, lc: L) -> bool where P: Iterator<Item=char>, L: Iterator<Item=char> {
        let mut c = pc.next();
        for outer in lc {
            match c {
                Some(ch) => {
                    if outer == ch {
                        c = pc.next();
                        if c.is_none() {
                            return true;
                        }
                    }
                    else if words::consonant(outer) {
                        return false;
                    }
                },

                None => {
                    return true;
                }
            }
        }

        false
    }

    (pat.len() <= line.len()) && (
        check(pat.chars(), line.chars()) ||
        check(pat.chars().rev(), line.chars().rev())
    )
}

fn usage(args: Vec<String>) -> ! {
    println!("Usage: {} <consonants>", args[0]);
    std::process::exit(2);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        usage(args);
    }
    if args[1].chars().any(|c| {!words::consonant(c)}) {
        usage(args);
    }

    let words = words::WORDS.lines().map(|line| {
        line.to_lowercase()
    }).filter(|line| {
        matches(&args[1], line)
    });

    let mut found = BinaryHeap::new();
    for word in words {
        found.push(sort::Length(word));
    }
    if found.len() == 0 {
        println!("No matches found.");
        return;
    }

    for word in found.pop_drain() {
        println!("{}", word.0);
    }
}
<commit_msg>Don't bother checking c.is_none() every time c gets advanced.<commit_after>mod words;

mod sort;
use sort::{HeapPopDrain};

use std::env;
use std::collections::{BinaryHeap};

fn matches(pat: &String, line: &str) -> bool {
    fn check<P, L>(mut pc: P, lc: L) -> bool where P: Iterator<Item=char>, L: Iterator<Item=char> {
        let mut c = pc.next();
        for outer in lc {
            match c {
                Some(ch) => {
                    if outer == ch {
                        c = pc.next();
                    }
                    else if words::consonant(outer) {
                        return false;
                    }
                },

                None => {
                    return true;
                }
            }
        }

        c.is_none()
    }

    (pat.len() <= line.len()) && (
        check(pat.chars(), line.chars()) ||
        check(pat.chars().rev(), line.chars().rev())
    )
}

fn usage(args: Vec<String>) -> ! {
    println!("Usage: {} <consonants>", args[0]);
    std::process::exit(2);
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() != 2 {
        usage(args);
    }
    if args[1].chars().any(|c| {!words::consonant(c)}) {
        usage(args);
    }

    let words = words::WORDS.lines().map(|line| {
        line.to_lowercase()
    }).filter(|line| {
        matches(&args[1], line)
    });

    let mut found = BinaryHeap::new();
    for word in words {
        found.push(sort::Length(word));
    }
    if found.len() == 0 {
        println!("No matches found.");
        return;
    }

    for word in found.pop_drain() {
        println!("{}", word.0);
    }
}
<|endoftext|>


<commit_before>extern crate hyper;
extern crate rustc_serialize;

mod state;

use std::io::Read;
use rustc_serialize::json;
use hyper::{Get, Post};
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
    );

fn handle_request(mut req: Request, mut res: Response) {
    let mut body: String  = "".to_string();
    req.read_to_string(&mut body);

    let path = match req.uri {
        AbsolutePath(ref path) => {
            path
        },
        _ => panic!("Unsupported URI format."),
    };

    match (req.method, path.as_ref()) {
        (Get, "/") => {
            try_return!(res.send(b"Hey, what up."));
        },
        (Get, "/state") => {
            let game = state::initial_state();
            let encoded = json::encode(&game).unwrap();
            try_return!(res.send(encoded.as_bytes()));
        },
        (Post, "/turn") => {
            try_return!(res.send(body.as_bytes()));
        },
        _ => {
            *res.status_mut() = hyper::BadRequest;
            (*res.headers_mut()).set(ContentLength(0));
        }
    }
}

fn main() {
    let server = Server::http("localhost:3001").unwrap();
    let _guard = server.handle(handle_request);
    println!("Starting server on localhost:3001");
}
<commit_msg>Remove unused HTTP API<commit_after>extern crate hyper;
extern crate rustc_serialize;

mod state;

use std::io::Read;
use rustc_serialize::json;
use hyper::{Get, Post};
use hyper::header::ContentLength;
use hyper::server::{Server, Request, Response};
use hyper::uri::RequestUri::AbsolutePath;

macro_rules! try_return(
    ($e:expr) => {{
        match $e {
            Ok(v) => v,
            Err(e) => { println!("Error: {}", e); return; }
        }
    }}
    );

fn handle_request(mut req: Request, mut res: Response) {
    let mut body: String  = "".to_string();
    req.read_to_string(&mut body);

    let path = match req.uri {
        AbsolutePath(ref path) => {
            path
        },
        _ => panic!("Unsupported URI format."),
    };

    match (req.method, path.as_ref()) {
        (Get, "/state") => {
            let game = state::initial_state();
            let encoded = json::encode(&game).unwrap();
            try_return!(res.send(encoded.as_bytes()));
        },
        (Post, "/turn") => {
            try_return!(res.send(body.as_bytes()));
        },
        _ => {
            *res.status_mut() = hyper::BadRequest;
            (*res.headers_mut()).set(ContentLength(0));
        }
    }
}

fn main() {
    let server = Server::http("localhost:3001").unwrap();
    let _guard = server.handle(handle_request);
    println!("Starting server on localhost:3001");
}
<|endoftext|>