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

use std::collections::HashMap;
use std::time::Duration;
use std::vec::Vec;

use async_std::io::{self, stderr, Read, Result as IoResult, Write};
use async_std::net::{SocketAddr, TcpStream};
use async_std::prelude::*;
use log::Level::Info;

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

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

#[derive(Debug, PartialEq)]
enum TransferEncodingStatus {
    ReadingHeader,
    ChunkHeader,
    ReadingBody(usize),
}

fn drain_buffer<T>(buffer: &mut Vec<T>, size: usize) -> Vec<T> {
    if buffer.len() >= size {
        buffer.drain(size..).collect()
    } else {
        error!("invalid size in drained buffer");
        buffer.clear();
        buffer.drain(..).collect()
    }
}

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> {
    reader: &'a mut (dyn Read + Unpin), // tls require Write
    writer: &'a mut (dyn Write + Unpin),
    buffer: Vec<u8>,
    transfer_encoding: TransferEncoding,
    transfer_encoding_status: TransferEncodingStatus,
    read_timeout: Duration,
}

impl<'a> HttpDecoder<'a> {
    fn new(
        writer: &'a mut (dyn Write + Unpin),
        reader: &'a mut (dyn Read + Unpin),
        read_timeout: u64,
    ) -> Self {
        HttpDecoder {
            writer,
            reader,
            buffer: Vec::with_capacity(constants::BUFFER_PAGE_SIZE),
            transfer_encoding: TransferEncoding::None,
            transfer_encoding_status: TransferEncodingStatus::ReadingHeader,
            read_timeout: Duration::from_millis(read_timeout),
        }
    }

    async fn chunk_read(&mut self) -> IoResult<usize> {
        let ret = io::timeout(self.read_timeout, async {
            let mut buf = [0; constants::BUFFER_PAGE_SIZE];
            let ret = self.reader.read(&mut buf[..]).await;
            if let Ok(count) = ret {
                if count > 0 {
                    self.buffer.extend_from_slice(&buf[..count]);
                }
            }
            ret
        });
        ret.await.map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => io::Error::new(err.kind(), "Read Timeout".to_owned()),
            _ => err,
        })
    }
    async fn read_write_headers(&mut self) -> IoResult<()> {
        info!("Reading headers");
        loop {
            let _count = self.chunk_read().await?;
            if let Some(_) = self.process_headers().await {
                break;
            }
        }
        Ok(())
    }

    async fn read_write_no_transfer_encoding(&mut self) -> IoResult<()> {
        loop {
            self.writer.write(self.buffer.as_slice()).await.unwrap();
            self.buffer.clear();
            let cnt = self.chunk_read().await?;
            if cnt == 0 {
                break;
            }
        }

        Ok(())
    }

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

            if read_count >= size {
                break;
            }
            read_count = read_count + self.chunk_read().await?;
            warn!("< {}", read_count);
        }
        Ok(())
    }

    async fn read_write_chunk(&mut self) -> IoResult<()> {
        loop {
            let done = self.process_chunk().await?;
            if done {
                break;
            }
            let cnt = self.chunk_read().await?;
            if cnt == 0 {
                break;
            }
        }

        Ok(())
    }

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

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

        Ok(())
    }

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

    async 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).await.unwrap();
                Some(resp + 4) // + CRLF CRLF
            } else {
                None
            }
        };
        if let Some(to_drain) = ret {
            self.buffer = drain_buffer(&mut self.buffer, to_drain);
            info!("End of Headers readched");
            debug!("Transfer encoding: {:?}", self.transfer_encoding);
            debug!("{:?}", String::from_utf8_lossy(self.buffer.as_slice()));
        }
        ret
    }
    async fn process_chunk(&mut self) -> IoResult<bool> {
        debug!(
            "transfer_encoding_status: {:?}",
            self.transfer_encoding_status
        );
        if self.buffer.len() == 0 {
            return Ok(false);
        }
        let mut body_chunk_size = 0;
        let mut header_len: usize;
        loop {
            header_len = 0;
            if self.transfer_encoding_status == TransferEncodingStatus::ReadingHeader {
                debug!("Reading header in Transfer-Encoding chunked");
                // 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();
                            body_chunk_size = usize::from_str_radix(size.as_str(), 16).unwrap();
                            debug!("Chunk Size to read: {}", body_chunk_size);
                            self.transfer_encoding_status = TransferEncodingStatus::ChunkHeader;
                        } else {
                            error!("Chunk Header is invalid");
                        }
                    } else {
                        error!("Chunk Header has improper size");
                        // else return Error
                        // break;
                    }
                } else {
                    debug!(
                        "Chunked not complete: {}",
                        String::from_utf8_lossy(self.buffer.as_slice())
                    );
                }
            }

            if self.transfer_encoding_status == TransferEncodingStatus::ChunkHeader {
                if body_chunk_size == 0 {
                    debug!(
                        "0 chunked size received: {}",
                        String::from_utf8_lossy(self.buffer.as_slice())
                    );
                    self.buffer.clear(); // should we check that it is '0\r\n' ?
                    return Ok(true);
                }

                debug!(
                    "Before header cleanup: {}",
                    String::from_utf8_lossy(self.buffer.as_slice())
                );
                self.buffer = drain_buffer(&mut self.buffer, header_len);
                debug!(
                    "After header cleanup: {}",
                    String::from_utf8_lossy(self.buffer.as_slice())
                );
                self.transfer_encoding_status =
                    TransferEncodingStatus::ReadingBody(body_chunk_size);
            }

            if let TransferEncodingStatus::ReadingBody(buf_size) = self.transfer_encoding_status {
                if buf_size >= constants::BUFFER_PAGE_SIZE {
                    let buf_size = buf_size - self.buffer.len();
                    self.writer.write(self.buffer.as_slice()).await?;
                    self.buffer.clear();
                    self.transfer_encoding_status = TransferEncodingStatus::ReadingBody(buf_size);
                    break;
                }
                if self.buffer.len() > (buf_size + 2) {
                    let mut buffer: Vec<u8> = self.buffer.drain(buf_size..).collect();
                    self.writer.write(self.buffer.as_slice()).await?;
                    self.buffer = drain_buffer(&mut buffer, 2); // CRLF
                    self.transfer_encoding_status = TransferEncodingStatus::ReadingHeader;
                    body_chunk_size = 0;
                    if self.buffer.len() < 4 {
                        break;
                    }
                } else {
                    break;
                }
            } else {
                break;
            }
        }
        return Ok(false);
    }
}

async 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).await.unwrap();
        }
        if bodylen > 0 {
            writeln!(&mut stderr(), "> [{} bytes]", bodylen)
                .await
                .unwrap();
        }
        writeln!(&mut stderr(), ">").await.unwrap();
    }
}

async fn from_http(
    request: &Request,
    client: &mut TcpStream,
    out: &mut (dyn Write + Unpin),
    verbose: bool,
    read_timeout: u64,
    request_timeout: u64,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose).await;

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

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

    let mut http_decoder = HttpDecoder::new(out, client, read_timeout);

    if request_timeout > 0 {
        io::timeout(Duration::from_millis(request_timeout), async {
            http_decoder.read_write().await
        })
        .await
        .map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => {
                if err.to_string() == "Read Timeout" {
                    err
                } else {
                    io::Error::new(err.kind(), "Request Timeout".to_owned())
                }
            }
            _ => err,
        })?;
    } else {
        http_decoder.read_write().await?;
    }
    Ok(())
}

async fn from_https(
    request: &Request,
    client: &mut TcpStream,
    out: &mut (dyn Write + Unpin),
    verbose: bool,
    read_timeout: u64,
    request_timeout: u64,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose).await;

    let mut tls_client = TLSStream::new(client, request.host())?;
    tls_client.starttls().await?;

    debug!("Sending request...");
    tls_client.write_all(&raw_request).await?;
    debug!("Request sent");

    debug!("Decoding response...");
    let mut http_decoder = HttpDecoder::new(out, &mut tls_client, read_timeout);

    if request_timeout > 0 {
        io::timeout(Duration::from_millis(request_timeout), async {
            http_decoder.read_write().await
        })
        .await
        .map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => {
                if err.to_string() == "Read Timeout" {
                    err
                } else {
                    io::Error::new(err.kind(), "Request Timeout".to_owned())
                }
            }
            _ => err,
        })?;
    } else {
        http_decoder.read_write().await?;
    }

    Ok(())
}

pub async fn http_query(
    request: &Request,
    mut out: &mut (dyn Write + Unpin),
    authorities: &HashMap<String, SocketAddr>,
    verbose: bool,
    ipv4: bool,
    ipv6: bool,
    dns_timeout: u64,
    connect_timeout: u64,
    read_timeout: u64,
    request_timeout: u64,
) -> 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, dns_timeout)
                .await?
        }
    };

    info!("Connecting to {}", addr);
    let mut client = io::timeout(Duration::from_millis(connect_timeout), async {
        TcpStream::connect(addr).await
    })
    .await
    .map_err(|err| match err.kind() {
        io::ErrorKind::TimedOut => io::Error::new(err.kind(), "Connection Timeout".to_owned()),
        _ => err,
    })?;

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

    out.flush().await.unwrap();

    Ok(())
}
<commit_msg>Fix chunk process end of transfer<commit_after>//! Low level and internal http and https implementation.

use std::collections::HashMap;
use std::time::Duration;
use std::vec::Vec;

use async_std::io::{self, stderr, Read, Result as IoResult, Write};
use async_std::net::{SocketAddr, TcpStream};
use async_std::prelude::*;
use log::Level::Info;

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

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

#[derive(Debug, PartialEq)]
enum TransferEncodingStatus {
    ReadingHeader,
    ChunkHeader,
    ReadingBody(usize),
}

fn drain_buffer<T>(buffer: &mut Vec<T>, size: usize) -> Vec<T> {
    if buffer.len() >= size {
        buffer.drain(size..).collect()
    } else {
        error!("invalid size in drained buffer");
        buffer.clear();
        buffer.drain(..).collect()
    }
}

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> {
    reader: &'a mut (dyn Read + Unpin), // tls require Write
    writer: &'a mut (dyn Write + Unpin),
    buffer: Vec<u8>,
    transfer_encoding: TransferEncoding,
    transfer_encoding_status: TransferEncodingStatus,
    read_timeout: Duration,
}

impl<'a> HttpDecoder<'a> {
    fn new(
        writer: &'a mut (dyn Write + Unpin),
        reader: &'a mut (dyn Read + Unpin),
        read_timeout: u64,
    ) -> Self {
        HttpDecoder {
            writer,
            reader,
            buffer: Vec::with_capacity(constants::BUFFER_PAGE_SIZE),
            transfer_encoding: TransferEncoding::None,
            transfer_encoding_status: TransferEncodingStatus::ReadingHeader,
            read_timeout: Duration::from_millis(read_timeout),
        }
    }

    async fn chunk_read(&mut self) -> IoResult<usize> {
        let ret = io::timeout(self.read_timeout, async {
            let mut buf = [0; constants::BUFFER_PAGE_SIZE];
            let ret = self.reader.read(&mut buf[..]).await;
            if let Ok(count) = ret {
                if count > 0 {
                    self.buffer.extend_from_slice(&buf[..count]);
                }
            }
            ret
        });
        ret.await.map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => io::Error::new(err.kind(), "Read Timeout".to_owned()),
            _ => err,
        })
    }
    async fn read_write_headers(&mut self) -> IoResult<()> {
        info!("Reading headers");
        loop {
            let _count = self.chunk_read().await?;
            if let Some(_) = self.process_headers().await {
                break;
            }
        }
        Ok(())
    }

    async fn read_write_no_transfer_encoding(&mut self) -> IoResult<()> {
        loop {
            self.writer.write(self.buffer.as_slice()).await.unwrap();
            self.buffer.clear();
            let cnt = self.chunk_read().await?;
            if cnt == 0 {
                break;
            }
        }

        Ok(())
    }

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

            if read_count >= size {
                break;
            }
            read_count = read_count + self.chunk_read().await?;
            warn!("< {}", read_count);
        }
        Ok(())
    }

    async fn read_write_chunk(&mut self) -> IoResult<()> {
        loop {
            // we have data in the buffer while reading the headers
            let done = self.process_chunk().await?;
            if done {
                break;
            }
            let cnt = self.chunk_read().await?;
            if cnt == 0 {
                debug!("No more chunk data to read");
            }
        }

        Ok(())
    }

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

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

        Ok(())
    }

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

    async 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).await.unwrap();
                Some(resp + 4) // + CRLF CRLF
            } else {
                None
            }
        };
        if let Some(to_drain) = ret {
            self.buffer = drain_buffer(&mut self.buffer, to_drain);
            info!("End of Headers readched");
            debug!("Transfer encoding: {:?}", self.transfer_encoding);
            debug!("{:?}", String::from_utf8_lossy(self.buffer.as_slice()));
        }
        ret
    }
    async fn process_chunk(&mut self) -> IoResult<bool> {
        debug!(
            "transfer_encoding_status: {:?}",
            self.transfer_encoding_status
        );
        if self.buffer.len() == 0 {
            return Ok(false);
        }
        let mut body_chunk_size = 0;
        let mut header_len: usize;
        loop {
            header_len = 0;
            if self.transfer_encoding_status == TransferEncodingStatus::ReadingHeader {
                debug!("Reading header in Transfer-Encoding chunked");
                // 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();
                            body_chunk_size = usize::from_str_radix(size.as_str(), 16).unwrap();
                            debug!("Chunk Size to read: {}", body_chunk_size);
                            self.transfer_encoding_status = TransferEncodingStatus::ChunkHeader;
                        } else {
                            error!("Chunk Header is invalid");
                        }
                    } else {
                        error!("Chunk Header has improper size");
                        // else return Error
                        // break;
                    }
                } else {
                    debug!(
                        "Chunked not complete: {}",
                        String::from_utf8_lossy(self.buffer.as_slice())
                    );
                }
            }

            if self.transfer_encoding_status == TransferEncodingStatus::ChunkHeader {
                if body_chunk_size == 0 {
                    debug!(
                        "0 chunked size received: {}",
                        String::from_utf8_lossy(self.buffer.as_slice())
                    );
                    self.buffer.clear(); // should we check that it is '0\r\n' ?
                    return Ok(true);
                }

                debug!(
                    "Before header cleanup: {}",
                    String::from_utf8_lossy(self.buffer.as_slice())
                );
                self.buffer = drain_buffer(&mut self.buffer, header_len);
                debug!(
                    "After header cleanup: {}",
                    String::from_utf8_lossy(self.buffer.as_slice())
                );
                self.transfer_encoding_status =
                    TransferEncodingStatus::ReadingBody(body_chunk_size);
            }

            if let TransferEncodingStatus::ReadingBody(buf_size) = self.transfer_encoding_status {
                if buf_size >= constants::BUFFER_PAGE_SIZE {
                    let buf_size = buf_size - self.buffer.len();
                    self.writer.write(self.buffer.as_slice()).await?;
                    self.buffer.clear();
                    self.transfer_encoding_status = TransferEncodingStatus::ReadingBody(buf_size);
                    break;
                }
                if self.buffer.len() > (buf_size + 2) {
                    let mut buffer: Vec<u8> = self.buffer.drain(buf_size..).collect();
                    self.writer.write(self.buffer.as_slice()).await?;
                    self.buffer = drain_buffer(&mut buffer, 2); // CRLF
                    self.transfer_encoding_status = TransferEncodingStatus::ReadingHeader;
                    body_chunk_size = 0;
                    if self.buffer.len() < 4 {
                        break;
                    }
                } else {
                    break;
                }
            } else {
                break;
            }
        }
        return Ok(false);
    }
}

async 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).await.unwrap();
        }
        if bodylen > 0 {
            writeln!(&mut stderr(), "> [{} bytes]", bodylen)
                .await
                .unwrap();
        }
        writeln!(&mut stderr(), ">").await.unwrap();
    }
}

async fn from_http(
    request: &Request,
    client: &mut TcpStream,
    out: &mut (dyn Write + Unpin),
    verbose: bool,
    read_timeout: u64,
    request_timeout: u64,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose).await;

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

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

    let mut http_decoder = HttpDecoder::new(out, client, read_timeout);

    if request_timeout > 0 {
        io::timeout(Duration::from_millis(request_timeout), async {
            http_decoder.read_write().await
        })
        .await
        .map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => {
                if err.to_string() == "Read Timeout" {
                    err
                } else {
                    io::Error::new(err.kind(), "Request Timeout".to_owned())
                }
            }
            _ => err,
        })?;
    } else {
        http_decoder.read_write().await?;
    }
    Ok(())
}

async fn from_https(
    request: &Request,
    client: &mut TcpStream,
    out: &mut (dyn Write + Unpin),
    verbose: bool,
    read_timeout: u64,
    request_timeout: u64,
) -> CabotResult<()> {
    let request_bytes = request.to_bytes();
    let raw_request = request_bytes.as_slice();
    log_request(&raw_request, verbose).await;

    let mut tls_client = TLSStream::new(client, request.host())?;
    tls_client.starttls().await?;

    debug!("Sending request...");
    tls_client.write_all(&raw_request).await?;
    debug!("Request sent");

    debug!("Decoding response...");
    let mut http_decoder = HttpDecoder::new(out, &mut tls_client, read_timeout);

    if request_timeout > 0 {
        io::timeout(Duration::from_millis(request_timeout), async {
            http_decoder.read_write().await
        })
        .await
        .map_err(|err| match err.kind() {
            io::ErrorKind::TimedOut => {
                if err.to_string() == "Read Timeout" {
                    err
                } else {
                    io::Error::new(err.kind(), "Request Timeout".to_owned())
                }
            }
            _ => err,
        })?;
    } else {
        http_decoder.read_write().await?;
    }

    Ok(())
}

pub async fn http_query(
    request: &Request,
    mut out: &mut (dyn Write + Unpin),
    authorities: &HashMap<String, SocketAddr>,
    verbose: bool,
    ipv4: bool,
    ipv6: bool,
    dns_timeout: u64,
    connect_timeout: u64,
    read_timeout: u64,
    request_timeout: u64,
) -> 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, dns_timeout)
                .await?
        }
    };

    info!("Connecting to {}", addr);
    let mut client = io::timeout(Duration::from_millis(connect_timeout), async {
        TcpStream::connect(addr).await
    })
    .await
    .map_err(|err| match err.kind() {
        io::ErrorKind::TimedOut => io::Error::new(err.kind(), "Connection Timeout".to_owned()),
        _ => err,
    })?;

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

    out.flush().await.unwrap();

    Ok(())
}
<|endoftext|>


<commit_before>//! The module defines structures and protocols for asynchronous MPD communication
//!
//! The MPD supports very simple protocol for asynchronous client notifications about
//! different player events. First user issues `idle` command with optional argument
//! to filter events by source subsystem (like "database", "player", "mixer" etc.)
//!
//! Once in "idle" mode, client connection timeout is disabled, and MPD will notify
//! client about next event when one occurs (if originated from one of designated
//! subsystems, if specified).
//!
//! (Actually MPD notifies only about general subsystem source of event, e.g.
//! if user changed volume, client will get `mixer` event in idle mode, so
//! it should issue `status` command then and check for any mixer-related field
//! changes.)
//!
//! Once some such event occurs, and client is notified about it, idle mode is interrupted,
//! and client must issue another `idle` command to continue listening for interesting
//! events.
//!
//! While in "idle" mode, client can't issue any commands, except for special `noidle`
//! command, which interrupts "idle" mode, and provides a list queued events
//! since last `idle` command, if they occurred.
//!
//! The module describes subsystems enum only, but the main workflow is determined by
//! [`IdleGuard`](struct.IdleGuard.html) struct, which catches mutable reference
//! to original `Client` struct, thus enforcing MPD contract in regards of (im)possibility
//! to send commands while in "idle" mode.

use client::Client;

use error::{Error, ParseError};
use proto::Proto;
use std::fmt;
use std::io::{Read, Write};
use std::mem::forget;
use std::str::FromStr;

/// Subsystems for `idle` command
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
pub enum Subsystem {
    /// database: the song database has been modified after update.
    Database,
    /// update: a database update has started or finished.
    /// If the database was modified during the update, the database event is also emitted.
    Update,
    /// stored_playlist: a stored playlist has been modified, renamed, created or deleted
    Playlist,
    /// playlist: the current playlist has been modified
    Queue,
    /// player: the player has been started, stopped or seeked
    Player,
    /// mixer: the volume has been changed
    Mixer,
    /// output: an audio output has been enabled or disabled
    Output,
    /// options: options like repeat, random, crossfade, replay gain
    Options,
    /// sticker: the sticker database has been modified.
    Sticker,
    /// subscription: a client has subscribed or unsubscribed to a channel
    Subscription,
    /// message: a message was received on a channel this client is subscribed to; this event is only emitted when the queue is empty
    Message,
}

impl FromStr for Subsystem {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<Subsystem, ParseError> {
        use self::Subsystem::*;
        match s {
            "database" => Ok(Database),
            "update" => Ok(Update),
            "stored_playlist" => Ok(Playlist),
            "playlist" => Ok(Queue),
            "player" => Ok(Player),
            "mixer" => Ok(Mixer),
            "output" => Ok(Output),
            "options" => Ok(Options),
            "sticker" => Ok(Sticker),
            "subscription" => Ok(Subscription),
            "message" => Ok(Message),
            _ => Err(ParseError::BadValue(s.to_owned())),
        }
    }
}

impl Subsystem {
    fn to_str(self) -> &'static str {
        use self::Subsystem::*;
        match self {
            Database => "database",
            Update => "update",
            Playlist => "stored_playlist",
            Queue => "playlist",
            Player => "player",
            Mixer => "mixer",
            Output => "output",
            Options => "options",
            Sticker => "sticker",
            Subscription => "subscription",
            Message => "message",
        }
    }
}

impl fmt::Display for Subsystem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

use std::result::Result as StdResult;
impl<'a> ::proto::ToArguments for Subsystem {
    fn to_arguments<F, E>(&self, f: &mut F) -> StdResult<(), E>
        where F: FnMut(&str) -> StdResult<(), E>
    {
        f(self.to_str())
    }
}

/// "Idle" mode guard enforcing MPD asynchronous events protocol
pub struct IdleGuard<'a, S: 'a + Read + Write>(&'a mut Client<S>);

impl<'a, S: 'a + Read + Write> IdleGuard<'a, S> {
    /// Get list of subsystems with new events, interrupting idle mode in process
    pub fn get(self) -> Result<Vec<Subsystem>, Error> {
        let result = self.0
            .read_pairs()
            .filter(|r| {
                r.as_ref()
                    .map(|&(ref a, _)| *a == "changed")
                    .unwrap_or(true)
            })
            .map(|r| r.and_then(|(_, b)| b.parse().map_err(From::from)))
            .collect();
        forget(self);
        result
    }
}

impl<'a, S: 'a + Read + Write> Drop for IdleGuard<'a, S> {
    fn drop(&mut self) {
        let _ = self.0.run_command("noidle", ()).map(|_| self.0.drain());
    }
}

/// This trait implements `idle` command of MPD protocol
///
/// See module's documentation for details.
pub trait Idle {
    /// Stream type of a client
    type Stream: Read + Write;

    /// Start listening for events from a set of subsystems
    ///
    /// If empty subsystems slice is given, wait for all event from any subsystem.
    ///
    /// This method returns `IdleGuard`, which takes mutable reference of an initial client,
    /// thus disallowing any operations on this mpd connection.
    ///
    /// You can call `.get()` method of this struct to stop waiting and get all queued events
    /// matching given subsystems filter. This call consumes a guard, stops waiting
    /// and releases client object.
    ///
    /// If the guard goes out of scope, wait lock is released as well, but all queued events
    /// will be silently ignored.
    fn idle<'a>(&'a mut self, subsystems: &[Subsystem]) -> Result<IdleGuard<'a, Self::Stream>, Error>;

    /// Wait for events from a set of subsystems and return list of affected subsystems
    ///
    /// This is a blocking operation. If empty subsystems slice is given,
    /// wait for all event from any subsystem.
    fn wait(&mut self, subsystems: &[Subsystem]) -> Result<Vec<Subsystem>, Error> {
        self.idle(subsystems).and_then(IdleGuard::get)
    }
}

impl<S: Read + Write> Idle for Client<S> {
    type Stream = S;
    fn idle<'a>(&'a mut self, subsystems: &[Subsystem]) -> Result<IdleGuard<'a, S>, Error> {
        self.run_command("idle", subsystems)?;
        Ok(IdleGuard(self))
    }
}
<commit_msg>Get rid of `read_pairs` in idle.<commit_after>//! The module defines structures and protocols for asynchronous MPD communication
//!
//! The MPD supports very simple protocol for asynchronous client notifications about
//! different player events. First user issues `idle` command with optional argument
//! to filter events by source subsystem (like "database", "player", "mixer" etc.)
//!
//! Once in "idle" mode, client connection timeout is disabled, and MPD will notify
//! client about next event when one occurs (if originated from one of designated
//! subsystems, if specified).
//!
//! (Actually MPD notifies only about general subsystem source of event, e.g.
//! if user changed volume, client will get `mixer` event in idle mode, so
//! it should issue `status` command then and check for any mixer-related field
//! changes.)
//!
//! Once some such event occurs, and client is notified about it, idle mode is interrupted,
//! and client must issue another `idle` command to continue listening for interesting
//! events.
//!
//! While in "idle" mode, client can't issue any commands, except for special `noidle`
//! command, which interrupts "idle" mode, and provides a list queued events
//! since last `idle` command, if they occurred.
//!
//! The module describes subsystems enum only, but the main workflow is determined by
//! [`IdleGuard`](struct.IdleGuard.html) struct, which catches mutable reference
//! to original `Client` struct, thus enforcing MPD contract in regards of (im)possibility
//! to send commands while in "idle" mode.

use client::Client;

use error::{Error, ParseError};
use proto::Proto;
use std::fmt;
use std::io::{Read, Write};
use std::mem::forget;
use std::str::FromStr;

/// Subsystems for `idle` command
#[derive(Clone, Copy, Debug, PartialEq, RustcEncodable)]
pub enum Subsystem {
    /// database: the song database has been modified after update.
    Database,
    /// update: a database update has started or finished.
    /// If the database was modified during the update, the database event is also emitted.
    Update,
    /// stored_playlist: a stored playlist has been modified, renamed, created or deleted
    Playlist,
    /// playlist: the current playlist has been modified
    Queue,
    /// player: the player has been started, stopped or seeked
    Player,
    /// mixer: the volume has been changed
    Mixer,
    /// output: an audio output has been enabled or disabled
    Output,
    /// options: options like repeat, random, crossfade, replay gain
    Options,
    /// sticker: the sticker database has been modified.
    Sticker,
    /// subscription: a client has subscribed or unsubscribed to a channel
    Subscription,
    /// message: a message was received on a channel this client is subscribed to; this event is only emitted when the queue is empty
    Message,
}

impl FromStr for Subsystem {
    type Err = ParseError;
    fn from_str(s: &str) -> Result<Subsystem, ParseError> {
        use self::Subsystem::*;
        match s {
            "database" => Ok(Database),
            "update" => Ok(Update),
            "stored_playlist" => Ok(Playlist),
            "playlist" => Ok(Queue),
            "player" => Ok(Player),
            "mixer" => Ok(Mixer),
            "output" => Ok(Output),
            "options" => Ok(Options),
            "sticker" => Ok(Sticker),
            "subscription" => Ok(Subscription),
            "message" => Ok(Message),
            _ => Err(ParseError::BadValue(s.to_owned())),
        }
    }
}

impl Subsystem {
    fn to_str(self) -> &'static str {
        use self::Subsystem::*;
        match self {
            Database => "database",
            Update => "update",
            Playlist => "stored_playlist",
            Queue => "playlist",
            Player => "player",
            Mixer => "mixer",
            Output => "output",
            Options => "options",
            Sticker => "sticker",
            Subscription => "subscription",
            Message => "message",
        }
    }
}

impl fmt::Display for Subsystem {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str(self.to_str())
    }
}

use std::result::Result as StdResult;
impl<'a> ::proto::ToArguments for Subsystem {
    fn to_arguments<F, E>(&self, f: &mut F) -> StdResult<(), E>
        where F: FnMut(&str) -> StdResult<(), E>
    {
        f(self.to_str())
    }
}

/// "Idle" mode guard enforcing MPD asynchronous events protocol
pub struct IdleGuard<'a, S: 'a + Read + Write>(&'a mut Client<S>);

impl<'a, S: 'a + Read + Write> IdleGuard<'a, S> {
    /// Get list of subsystems with new events, interrupting idle mode in process
    pub fn get(self) -> Result<Vec<Subsystem>, Error> {
        let result = self.0
            .read_list("changed")
            .and_then(|v| v.into_iter().map(|b| b.parse().map_err(From::from)).collect());
        forget(self);
        result
    }
}

impl<'a, S: 'a + Read + Write> Drop for IdleGuard<'a, S> {
    fn drop(&mut self) {
        let _ = self.0.run_command("noidle", ()).map(|_| self.0.drain());
    }
}

/// This trait implements `idle` command of MPD protocol
///
/// See module's documentation for details.
pub trait Idle {
    /// Stream type of a client
    type Stream: Read + Write;

    /// Start listening for events from a set of subsystems
    ///
    /// If empty subsystems slice is given, wait for all event from any subsystem.
    ///
    /// This method returns `IdleGuard`, which takes mutable reference of an initial client,
    /// thus disallowing any operations on this mpd connection.
    ///
    /// You can call `.get()` method of this struct to stop waiting and get all queued events
    /// matching given subsystems filter. This call consumes a guard, stops waiting
    /// and releases client object.
    ///
    /// If the guard goes out of scope, wait lock is released as well, but all queued events
    /// will be silently ignored.
    fn idle<'a>(&'a mut self, subsystems: &[Subsystem]) -> Result<IdleGuard<'a, Self::Stream>, Error>;

    /// Wait for events from a set of subsystems and return list of affected subsystems
    ///
    /// This is a blocking operation. If empty subsystems slice is given,
    /// wait for all event from any subsystem.
    fn wait(&mut self, subsystems: &[Subsystem]) -> Result<Vec<Subsystem>, Error> {
        self.idle(subsystems).and_then(IdleGuard::get)
    }
}

impl<S: Read + Write> Idle for Client<S> {
    type Stream = S;
    fn idle<'a>(&'a mut self, subsystems: &[Subsystem]) -> Result<IdleGuard<'a, S>, Error> {
        self.run_command("idle", subsystems)?;
        Ok(IdleGuard(self))
    }
}
<|endoftext|>


<commit_before>// Copyright 2013 The noise-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An example of using perlin noise

// Console noise rendering based off rust/src/test/bench/noise.rs

extern mod noise;

use std::uint::range;
use noise::perlin::*;

static WIDTH:  uint = 100;
static HEIGHT: uint = 100;

static GRADIENT: [&'static str, ..6] = [" ", "░", "▒", "▓", "█", "█"];

fn main() {
    let mut cells = [[0f32, ..WIDTH], ..HEIGHT];

    let ctx = PerlinContext::new::<f32>();

    for range(0, HEIGHT) |y| {
        for range(0, WIDTH) |x| {
            cells[y][x] = [x as f32 * 0.1f32,
                           y as f32 * 0.1f32].perlin(&ctx) * 0.5f32 + 0.5f32;
        };
    };

    for range(0, HEIGHT) |y| {
        for range(0, WIDTH) |x| {
            print(GRADIENT[cells[y][x] / 0.2f32 as int]);
        }
        println("");
    }
}
<commit_msg>Simplify perlin noise example<commit_after>// Copyright 2013 The noise-rs developers. For a full listing of the authors,
// refer to the AUTHORS file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! An example of using perlin noise

extern mod noise;

use std::uint::range;
use noise::perlin::*;

static WIDTH: uint = 100;
static HEIGHT: uint = 100;

static GRADIENT: [&'static str, ..6] = [" ", "░", "▒", "▓", "█", "█"];

fn main() {
    let ctx = PerlinContext::new::<f32>();

    for range(0, HEIGHT) |y| {
        for range(0, WIDTH) |x| {
            let val = [x as f32 * 0.1f32,
                       y as f32 * 0.1f32].perlin(&ctx) * 0.5f32 + 0.5f32;
            print(GRADIENT[val / 0.2f32 as int]);
        }
        println("");
    }
}
<|endoftext|>


<commit_before>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?
/*
CSVSample.rs:38:4: 38:21 error: macro undefined: 'ProvideCSV_labels'
CSVSample.rs:38    ProvideCSV_labels!("MyCSV", "./sample1.txt", "Verse");
                   ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
 */

// rustc 0.11.0-pre (732e057 2014-06-06 01:21:54 -0700)
// host: x86_64-apple-darwin

#![feature(globs, macro_registrar, macro_rules, quote, managed_boxes)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        ExtCtxt,
                        MacResult,
                        MacPat,
                        MacItem,
                        DummyResult,
                        MacExpr,
                        NormalTT};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("ProvideCSV_labels"),
             NormalTT(box BasicMacroExpander {
                 expander: provide_csv_given_labels,
                 span: None,
             },
             None));
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: @Expr
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name");
   let path   = entries.shift().expect("should be given a CSV file path");
   let labels = entries.shift().expect("should be given column labels");

   println!("provide_csv_given_labels: name:   {}", name.str);
   println!("provide_csv_given_labels: path:   {}", path.str);
   println!("provide_csv_given_labels: labels: {}", labels.str);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/*
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   let fortyfour = 44;
   return MacPat::new(quote_stmt!(cx, {
      struct MyCSV {
         data: Vec<(String)>,
      }

      impl MyCSV {
         fn new(&self) {
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["one".to_string(), "two".to_string(), "three".to_string()]),
            };
         }
      }
   }));
}

<commit_msg>PENDING: more...<commit_after>#![crate_id="CSVProvider#0.1-pre"]
#![crate_type="dylib"]

// why doesn't "lib" work?
/*
CSVSample.rs:38:4: 38:21 error: macro undefined: 'ProvideCSV_labels'
CSVSample.rs:38    ProvideCSV_labels!("MyCSV", "./sample1.txt", "Verse");
                   ^~~~~~~~~~~~~~~~~
error: aborting due to previous error
 */

// rustc 0.11.0-pre (732e057 2014-06-06 01:21:54 -0700)
// host: x86_64-apple-darwin

#![feature(globs, macro_registrar, macro_rules, quote, managed_boxes)]

#![allow(unused_imports)]
#![allow(unused_variable)]

extern crate syntax;

use syntax::ast::{Name,
                  TokenTree,
                  Expr,
                  ExprLit,
                  ExprVec,
                  LitStr,
                  DUMMY_NODE_ID,
                  MutImmutable};
use syntax::codemap::Span;
use syntax::ext::base::{SyntaxExtension,
                        BasicMacroExpander,
                        ExtCtxt,
                        MacResult,
                        MacPat,
                        MacItem,
                        DummyResult,
                        MacExpr,
                        NormalTT};
use syntax::parse;
use syntax::parse::token;
use syntax::parse::token::{InternedString, COMMA, EOF};

use std::io;
use std::io::fs;
use std::os;
use std::str;
use std::io::Process;

#[macro_registrar]
pub fn macro_registrar(register: |Name, SyntaxExtension|) {
    register(token::intern("ProvideCSV_labels"),
             NormalTT(box BasicMacroExpander {
                 expander: provide_csv_given_labels,
                 span: None,
             },
             None));
}

#[deriving(Clone)]
struct Entry {
    str: InternedString,
    expr: @Expr
}

// see https://github.com/sfackler/syntax-ext-talk/blob/gh-pages/simple-ext/lib.rs
fn parse_entries(cx: &mut ExtCtxt, tts: &[TokenTree]) -> Option<Vec<Entry>> {
    let mut parser = parse::new_parser_from_tts(
         cx.parse_sess(),
         cx.cfg(),
         tts.iter()
            .map(|x| (*x).clone())
            .collect()
    );

    let mut entries: Vec<Entry> = Vec::new();

    let mut error = false;
    while parser.token != EOF {
        let entry = parser.parse_expr();

        let entry_str = match entry.node {
            ExprLit(lit) => {
                match lit.node {
                    LitStr(ref s, _) => s.clone(),
                    _ => {
                        cx.span_err(entry.span, "expected string literal");
                        error = true;
                        InternedString::new("")
                    }
                }
            }
            _ => {
                cx.span_err(entry.span, "expected string literal");
                error = true;
                InternedString::new("")
            }
        };

        entries.push(Entry { str: entry_str, expr: entry });

        if !parser.eat(&COMMA) && parser.token != EOF {
            cx.span_err(parser.span, "expected `,`");
            return None;
        }
    }

    if error {
        return None;
    }

    Some(entries)
}

fn provide_csv_given_labels(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult> {
   let mut entries = match parse_entries(cx, tts) {
      Some(entries) => entries,
      None => return DummyResult::expr(sp),
   };

   let name   = entries.shift().expect("should be given a type name");
   let path   = entries.shift().expect("should be given a CSV file path");
   let labels = entries.shift().expect("should be given column labels");

   println!("provide_csv_given_labels: name:   {}", name.str);
   println!("provide_csv_given_labels: path:   {}", path.str);
   println!("provide_csv_given_labels: labels: {}", labels.str);

   // PENDING
   // read the CSV and try to get the type of data in it
   // create a type with
   //  * this name,
   //  * these labeled columns,
   //  * discovered type of data
   //  * a constructor which reads the whole file

/*
   let banana = "YAY! BANANA!";
   return MacExpr::new(quote_expr!(cx, {let x: $banana = 44}));
 */

   let fortyfour = 44;
   return MacPat::new(quote_stmt!(cx, {
      struct MyCSV {
         data: Vec<(String)>,
      }

      impl MyCSV {
         fn new(&self) {
            println!("HMMMMMM.");
            return MyCSV {
               data: (vec!["one".to_string(), "two".to_string(), "three".to_string()]),
            };
         }
      }
   }));
}

<|endoftext|>


<commit_before>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },

                Type => ok("types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        rust_type = rust_type.map_any(|s| format!("Option<{}>", s));
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Bitfield(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    };
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<commit_msg>Add Fundamental::Filename support -- same as Utf8<commit_after>use std::result;

use env::Env;
use library::{self, Nullable};
use nameutil::crate_name;
use traits::*;

pub type Result = result::Result<String, String>;

impl AsStr for Result {
    #[inline]
    fn as_str(&self) -> &str {
        self.as_ref().unwrap_or_else(|s| s)
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), false)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, by_ref: bool) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            let ok = |s: &str| Ok(s.into());
            let err = |s: &str| Err(s.into());
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                Utf8 => if by_ref { ok("str") } else { ok("String") },
                Filename => if by_ref { ok("str") } else { ok("String") },

                Type => ok("types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },

        Enumeration(ref enum_) => Ok(enum_.name.clone()),
        Bitfield(ref bitfield) => Ok(bitfield.name.clone()),
        Interface(ref interface) => Ok(interface.name.clone()),
        Class(ref klass) => Ok(klass.name.clone()),
        List(inner_tid) => {
            skip_option = true;
            rust_type(env, inner_tid)
                .map_any(|s| if by_ref { format!("[{}]", s) } else { format!("Vec<{}>", s) })
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE {
        let rust_type_with_prefix = rust_type.map(|s| format!("{}::{}",
            crate_name(&env.library.namespace(type_id.ns_id).name), s));
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(rust_type_with_prefix.as_str().into());
        } else {
            rust_type = rust_type_with_prefix;
        }
    }
    if by_ref {
        rust_type = rust_type.map_any(|s| format!("&{}", s));
    }
    if *nullable && !skip_option {
        rust_type = rust_type.map_any(|s| format!("Option<{}>", s));
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Bitfield(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        _ => Err("Don't need use".into()),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
        direction: library::ParameterDirection, nullable: Nullable) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let by_ref = match *type_ {
        Fundamental(library::Fundamental::Utf8) |
            Fundamental(library::Fundamental::Filename) |
            Class(..) |
            List(..) => direction == library::ParameterDirection::In,
        _ => false,
    };
    let rust_type = rust_type_full(env, type_id, nullable, by_ref);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
                }
            } else {
                format_parameter(rust_type, direction)
            }
        },

        Enumeration(..) |
            Bitfield(..) => format_parameter(rust_type, direction),

        Class(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }

        List(..) => {
            match direction {
                library::ParameterDirection::Return => rust_type,
                _ => Err(format!("/*Unimplemented*/{}", rust_type.as_str())),
            }
        }
        _ => Err(format!("Unknown rust type: {:?}", type_.get_name())),
        //TODO: check usage library::Type::get_name() when no _ in this
    }
}

#[inline]
fn format_parameter(rust_type: Result, direction: library::ParameterDirection) -> Result {
    if direction.is_out() {
        rust_type.map(|s| format!("&mut {}", s))
    } else {
        rust_type
    }
}
<|endoftext|>


<commit_before>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() { ok("VariantTy") } else { ok("VariantType") }
        }
        Enumeration(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Union(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    match &*type_id.full_name(library) {
        "GLib.Error" => return true,
        _ => (),
    }
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        "Gdk.Rectangle" => true,
        _ => false,
    }
}
<commit_msg>Remove usage Gdk.Rectangle as local Rectangle<commit_after>use std::result;

use analysis::ref_mode::RefMode;
use env::Env;
use library::{self, Nullable};
use super::conversion_type::ConversionType;
use traits::*;

#[derive(Clone, Debug, PartialEq, Eq)]
pub enum TypeError {
    Ignored(String),
    Mismatch(String),
    Unimplemented(String),
}

pub type Result = result::Result<String, TypeError>;

fn into_inner(res: Result) -> String {
    use self::TypeError::*;
    match res {
        Ok(s) |
            Err(Ignored(s)) |
            Err(Mismatch(s)) |
            Err(Unimplemented(s)) => s,
    }
}

impl IntoString for Result {
    fn into_string(self) -> String {
        use self::TypeError::*;
        match self {
            Ok(s) => s,
            Err(Ignored(s)) => format!("/*Ignored*/{}", s),
            Err(Mismatch(s)) => format!("/*Metadata mismatch*/{}", s),
            Err(Unimplemented(s)) => format!("/*Unimplemented*/{}", s),
        }
    }
}

impl MapAny<String> for Result {
    fn map_any<F: FnOnce(String) -> String>(self, op: F) -> Result {
        use self::TypeError::*;
        match self {
            Ok(s) => Ok(op(s)),
            Err(Ignored(s)) => Err(Ignored(op(s))),
            Err(Mismatch(s)) => Err(Mismatch(op(s))),
            Err(Unimplemented(s)) => Err(Unimplemented(op(s))),
        }
    }
}

pub fn rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::None)
}

pub fn bounds_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    rust_type_full(env, type_id, Nullable(false), RefMode::ByRefFake)
}

fn rust_type_full(env: &Env, type_id: library::TypeId, nullable: Nullable, ref_mode: RefMode) -> Result {
    use library::Type::*;
    use library::Fundamental::*;
    let ok = |s: &str| Ok(s.into());
    let err = |s: &str| Err(TypeError::Unimplemented(s.into()));
    let mut skip_option = false;
    let type_ = env.library.type_(type_id);
    let mut rust_type = match *type_ {
        Fundamental(fund) => {
            match fund {
                None => err("()"),
                Boolean => ok("bool"),
                Int8 => ok("i8"),
                UInt8 => ok("u8"),
                Int16 => ok("i16"),
                UInt16 => ok("u16"),
                Int32 => ok("i32"),
                UInt32 => ok("u32"),
                Int64 => ok("i64"),
                UInt64 => ok("u64"),

                Int => ok("i32"),      //maybe dependent on target system
                UInt => ok("u32"),     //maybe dependent on target system

                Float => ok("f32"),
                Double => ok("f64"),

                UniChar => ok("char"),
                Utf8 => if ref_mode.is_ref() { ok("str") } else { ok("String") },
                Filename => {
                    if ref_mode.is_ref() {
                        ok("std::path::Path")
                    }
                    else {
                        ok("std::path::PathBuf")
                    }
                }
                Type => ok("glib::types::Type"),
                Unsupported => err("Unsupported"),
                _ => err(&format!("Fundamental: {:?}", fund)),
            }
        },
        Alias(ref alias) => {
            rust_type_full(env, alias.typ, nullable, ref_mode)
                .map_any(|_| alias.name.clone())
        }
        Record(library::Record { ref c_type, .. }) if c_type == "GVariantType" => {
            if ref_mode.is_ref() { ok("VariantTy") } else { ok("VariantType") }
        }
        Enumeration(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Interface(..) => {
            let name = type_.get_name().to_owned();
            if env.type_status(&type_id.full_name(&env.library)).ignored() {
                Err(TypeError::Ignored(name))
            }
            else {
                Ok(name)
            }
        }
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid)
            if ConversionType::of(&env.library, inner_tid) == ConversionType::Pointer => {
            skip_option = true;
            let inner_ref_mode = match *env.library.type_(inner_tid) {
                Class(..) |
                    Interface(..) => RefMode::None,
                _ => ref_mode,
            };
            rust_type_full(env, inner_tid, Nullable(false), inner_ref_mode)
                .map_any(|s| if ref_mode.is_ref() {
                    format!("[{}]", s)
                } else {
                    format!("Vec<{}>", s)
                })
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    };

    if type_id.ns_id != library::MAIN_NAMESPACE && type_id.ns_id != library::INTERNAL_NAMESPACE
        && !implemented_in_main_namespace(&env.library, type_id) {
        if env.type_status(&type_id.full_name(&env.library)).ignored() {
            rust_type = Err(TypeError::Ignored(into_inner(rust_type)));
        }
        rust_type = rust_type.map_any(|s| format!("{}::{}",
            env.namespaces[type_id.ns_id].higher_crate_name, s));
    }

    match ref_mode {
        RefMode::None | RefMode::ByRefFake => {}
        RefMode::ByRef | RefMode::ByRefImmut => rust_type = rust_type.map_any(|s| format!("&{}", s)),
        RefMode::ByRefMut => rust_type = rust_type.map_any(|s| format!("&mut {}", s)),
    }
    if *nullable && !skip_option {
        match ConversionType::of(&env.library, type_id) {
            ConversionType::Pointer
                | ConversionType::Scalar => {
                rust_type = rust_type.map_any(|s| format!("Option<{}>", s))
            }
            _ => (),
        }
    }

    rust_type
}

pub fn used_rust_type(env: &Env, type_id: library::TypeId) -> Result {
    use library::Type::*;
    match *env.library.type_(type_id) {
        Fundamental(library::Fundamental::Type) |
            Alias(..) |
            Bitfield(..) |
            Record(..) |
            Union(..) |
            Class(..) |
            Enumeration(..) |
            Interface(..) => rust_type(env, type_id),
        List(inner_tid) |
            SList(inner_tid) |
            CArray(inner_tid) => used_rust_type(env, inner_tid),
        _ => Err(TypeError::Ignored("Don't need use".to_owned())),
    }
}

pub fn parameter_rust_type(env: &Env, type_id:library::TypeId,
                           direction: library::ParameterDirection, nullable: Nullable,
                           ref_mode: RefMode) -> Result {
    use library::Type::*;
    let type_ = env.library.type_(type_id);
    let rust_type = rust_type_full(env, type_id, nullable, ref_mode);
    match *type_ {
        Fundamental(fund) => {
            if fund == library::Fundamental::Utf8 || fund == library::Fundamental::Filename {
                match direction {
                    library::ParameterDirection::In |
                        library::ParameterDirection::Return => rust_type,
                    _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
                }
            } else {
                rust_type.map_any(|s| format_parameter(s, direction))
            }
        }
        Alias(ref alias) => {
            rust_type.and_then(|s| {
                parameter_rust_type(env, alias.typ, direction, nullable, ref_mode)
                    .map_any(|_| s)
            })
                .map_any(|s| format_parameter(s, direction))
        }

        Enumeration(..) |
            Union(..) |
            Bitfield(..) => rust_type.map_any(|s| format_parameter(s, direction)),

        Record(..) => {
            if direction == library::ParameterDirection::InOut {
                Err(TypeError::Unimplemented(into_inner(rust_type)))
            } else {
                rust_type
            }
        }

        Class(..) |
            Interface (..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Out |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }

        List(..) |
            SList(..) |
            CArray(..) => {
            match direction {
                library::ParameterDirection::In |
                    library::ParameterDirection::Return => rust_type,
                _ => Err(TypeError::Unimplemented(into_inner(rust_type))),
            }
        }
        _ => Err(TypeError::Unimplemented(type_.get_name().to_owned())),
    }
}

#[inline]
fn format_parameter(rust_type: String, direction: library::ParameterDirection) -> String {
    if direction.is_out() {
        format!("&mut {}", rust_type)
    } else {
        rust_type
    }
}

//TODO: remove
fn implemented_in_main_namespace(library: &library::Library, type_id: library::TypeId) -> bool {
    match &*type_id.full_name(library) {
        "GLib.Error" => return true,
        _ => (),
    }
    if library.namespace(library::MAIN_NAMESPACE).name != "Gtk" {
        return false;
    }
    match &*type_id.full_name(library) {
        _ => false,
    }
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Defines `ArrowError` for representing failures in various Arrow operations
use std::error::Error;

use csv as csv_crate;

/// Many different operations in the `arrow` crate return this error type
#[derive(Debug, Clone, PartialEq)]
pub enum ArrowError {
    MemoryError(String),
    ParseError(String),
    ComputeError(String),
    DivideByZero,
    CsvError(String),
    JsonError(String),
    IoError(String),
    InvalidArgumentError(String),
    ParquetError(String),
}

impl From<::std::io::Error> for ArrowError {
    fn from(error: ::std::io::Error) -> Self {
        ArrowError::IoError(error.description().to_string())
    }
}

impl From<csv_crate::Error> for ArrowError {
    fn from(error: csv_crate::Error) -> Self {
        match error.kind() {
            csv_crate::ErrorKind::Io(error) => {
                ArrowError::CsvError(error.description().to_string())
            }
            csv_crate::ErrorKind::Utf8 { pos: _, err } => ArrowError::CsvError(format!(
                "Encountered UTF-8 error while reading CSV file: {:?}",
                err.description()
            )),
            csv_crate::ErrorKind::UnequalLengths {
                pos: _,
                expected_len,
                len,
            } => ArrowError::CsvError(format!(
                "Encountered unequal lengths between records on CSV file. Expected {} \
                 records, found {} records",
                len, expected_len
            )),
            _ => ArrowError::CsvError("Error reading CSV file".to_string()),
        }
    }
}

impl From<::std::string::FromUtf8Error> for ArrowError {
    fn from(error: ::std::string::FromUtf8Error) -> Self {
        ArrowError::ParseError(error.description().to_string())
    }
}

pub type Result<T> = ::std::result::Result<T, ArrowError>;
<commit_msg>ARROW-7312: [Rust] Implement std::error::Error for ArrowError.<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

//! Defines `ArrowError` for representing failures in various Arrow operations
use std::error::Error;
use std::fmt::{Display, Formatter};

use csv as csv_crate;

/// Many different operations in the `arrow` crate return this error type
#[derive(Debug, Clone, PartialEq)]
pub enum ArrowError {
    MemoryError(String),
    ParseError(String),
    ComputeError(String),
    DivideByZero,
    CsvError(String),
    JsonError(String),
    IoError(String),
    InvalidArgumentError(String),
    ParquetError(String),
}

impl From<::std::io::Error> for ArrowError {
    fn from(error: ::std::io::Error) -> Self {
        ArrowError::IoError(error.description().to_string())
    }
}

impl From<csv_crate::Error> for ArrowError {
    fn from(error: csv_crate::Error) -> Self {
        match error.kind() {
            csv_crate::ErrorKind::Io(error) => {
                ArrowError::CsvError(error.description().to_string())
            }
            csv_crate::ErrorKind::Utf8 { pos: _, err } => ArrowError::CsvError(format!(
                "Encountered UTF-8 error while reading CSV file: {:?}",
                err.description()
            )),
            csv_crate::ErrorKind::UnequalLengths {
                pos: _,
                expected_len,
                len,
            } => ArrowError::CsvError(format!(
                "Encountered unequal lengths between records on CSV file. Expected {} \
                 records, found {} records",
                len, expected_len
            )),
            _ => ArrowError::CsvError("Error reading CSV file".to_string()),
        }
    }
}

impl From<::std::string::FromUtf8Error> for ArrowError {
    fn from(error: ::std::string::FromUtf8Error) -> Self {
        ArrowError::ParseError(error.description().to_string())
    }
}

impl Display for ArrowError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            &ArrowError::MemoryError(ref desc) => write!(f, "Memory error: {}", desc),
            &ArrowError::ParseError(ref desc) => write!(f, "Parser error: {}", desc),
            &ArrowError::ComputeError(ref desc) => write!(f, "Compute error: {}", desc),
            &ArrowError::DivideByZero => write!(f, "Divide by zero error"),
            &ArrowError::CsvError(ref desc) => write!(f, "Csv error: {}", desc),
            &ArrowError::JsonError(ref desc) => write!(f, "Json error: {}", desc),
            &ArrowError::IoError(ref desc) => write!(f, "Io error: {}", desc),
            &ArrowError::InvalidArgumentError(ref desc) => {
                write!(f, "Invalid argument error: {}", desc)
            }
            &ArrowError::ParquetError(ref desc) => {
                write!(f, "Parquet argument error: {}", desc)
            }
        }
    }
}

impl Error for ArrowError {}

pub type Result<T> = ::std::result::Result<T, ArrowError>;
<|endoftext|>