<commit_before>//! Easy file downloading
#![deny(rust_2018_idioms)]

use std::path::Path;
use url::Url;

mod errors;
pub use crate::errors::*;

#[derive(Debug, Copy, Clone)]
pub enum Backend {
    Curl,
    Reqwest(TlsBackend),
}

#[derive(Debug, Copy, Clone)]
pub enum TlsBackend {
    Rustls,
    Default,
}

#[derive(Debug, Copy, Clone)]
pub enum Event<'a> {
    ResumingPartialDownload,
    /// Received the Content-Length of the to-be downloaded data.
    DownloadContentLengthReceived(u64),
    /// Received some data.
    DownloadDataReceived(&'a [u8]),
}

fn download_with_backend(
    backend: Backend,
    url: &Url,
    resume_from: u64,
    callback: &dyn Fn(Event<'_>) -> Result<()>,
) -> Result<()> {
    match backend {
        Backend::Curl => curl::download(url, resume_from, callback),
        Backend::Reqwest(tls) => reqwest_be::download(url, resume_from, callback, tls),
    }
}

pub fn download_to_path_with_backend(
    backend: Backend,
    url: &Url,
    path: &Path,
    resume_from_partial: bool,
    callback: Option<&dyn Fn(Event<'_>) -> Result<()>>,
) -> Result<()> {
    use std::cell::RefCell;
    use std::fs::remove_file;
    use std::fs::OpenOptions;
    use std::io::{Read, Seek, SeekFrom, Write};

    || -> Result<()> {
        let (file, resume_from) = if resume_from_partial {
            let possible_partial = OpenOptions::new().read(true).open(&path);

            let downloaded_so_far = if let Ok(mut partial) = possible_partial {
                if let Some(cb) = callback {
                    cb(Event::ResumingPartialDownload)?;

                    let mut buf = vec![0; 32768];
                    let mut downloaded_so_far = 0;
                    loop {
                        let n = partial.read(&mut buf)?;
                        downloaded_so_far += n as u64;
                        if n == 0 {
                            break;
                        }
                        cb(Event::DownloadDataReceived(&buf[..n]))?;
                    }

                    downloaded_so_far
                } else {
                    let file_info = partial.metadata()?;
                    file_info.len()
                }
            } else {
                0
            };

            let mut possible_partial = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&path)
                .chain_err(|| "error opening file for download")?;

            possible_partial.seek(SeekFrom::End(0))?;

            (possible_partial, downloaded_so_far)
        } else {
            (
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(&path)
                    .chain_err(|| "error creating file for download")?,
                0,
            )
        };

        let file = RefCell::new(file);

        download_with_backend(backend, url, resume_from, &|event| {
            if let Event::DownloadDataReceived(data) = event {
                file.borrow_mut()
                    .write_all(data)
                    .chain_err(|| "unable to write download to disk")?;
            }
            match callback {
                Some(cb) => cb(event),
                None => Ok(()),
            }
        })?;

        file.borrow_mut()
            .sync_data()
            .chain_err(|| "unable to sync download to disk")?;

        Ok(())
    }()
    .map_err(|e| {
        // TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
        remove_file(path)
            .chain_err(|| "cleaning up cached downloads")
            .unwrap();
        e
    })
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
#[cfg(feature = "curl-backend")]
pub mod curl {
    use super::Event;
    use crate::errors::*;
    use curl::easy::Easy;
    use std::cell::RefCell;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            handle
                .url(&url.to_string())
                .chain_err(|| "failed to set url")?;
            handle
                .follow_location(true)
                .chain_err(|| "failed to set follow redirects")?;

            if resume_from > 0 {
                handle
                    .resume_from(resume_from)
                    .chain_err(|| "setting the range header for download resumption")?;
            } else {
                // an error here indicates that the range header isn't supported by underlying curl,
                // so there's nothing to "clear" - safe to ignore this error.
                let _ = handle.resume_from(0);
            }

            // Take at most 30s to connect
            handle
                .connect_timeout(Duration::new(30, 0))
                .chain_err(|| "failed to set connect timeout")?;

            {
                let cberr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                transfer
                    .write_function(|data| match callback(Event::DownloadDataReceived(data)) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *cberr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    })
                    .chain_err(|| "failed to set write")?;

                // Listen for headers and parse out a `Content-Length` (case-insensitive) if it
                // comes so we know how much we're downloading.
                transfer
                    .header_function(|header| {
                        if let Ok(data) = str::from_utf8(header) {
                            let prefix = "content-length: ";
                            if data.to_ascii_lowercase().starts_with(prefix) {
                                if let Ok(s) = data[prefix.len()..].trim().parse::<u64>() {
                                    let msg = Event::DownloadContentLengthReceived(s + resume_from);
                                    match callback(msg) {
                                        Ok(()) => (),
                                        Err(e) => {
                                            *cberr.borrow_mut() = Some(e);
                                            return false;
                                        }
                                    }
                                }
                            }
                        }
                        true
                    })
                    .chain_err(|| "failed to set header")?;

                // If an error happens check to see if we had a filesystem error up
                // in `cberr`, but we always want to punt it up.
                transfer.perform().or_else(|e| {
                    // If the original error was generated by one of our
                    // callbacks, return it.
                    match cberr.borrow_mut().take() {
                        Some(cberr) => Err(cberr),
                        None => {
                            // Otherwise, return the error from curl
                            if e.is_file_couldnt_read_file() {
                                Err(e).chain_err(|| ErrorKind::FileNotFound)
                            } else {
                                Err(e).chain_err(|| "error during download")
                            }
                        }
                    }
                })?;
            }

            // If we didn't get a 20x or 0 ("OK" for files) then return an error
            let code = handle
                .response_code()
                .chain_err(|| "failed to get response code")?;
            match code {
                0 | 200..=299 => {}
                _ => {
                    return Err(ErrorKind::HttpStatus(code).into());
                }
            };

            Ok(())
        })
    }
}

#[cfg(feature = "reqwest-backend")]
pub mod reqwest_be {
    use super::Event;
    use super::TlsBackend;
    use crate::errors::*;
    use lazy_static::lazy_static;
    use reqwest::blocking::{Client, ClientBuilder, Response};
    use reqwest::{header, Proxy};
    use std::io;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
        tls: TlsBackend,
    ) -> Result<()> {
        // Short-circuit reqwest for the "file:" URL scheme
        if download_from_file_url(url, resume_from, callback)? {
            return Ok(());
        }

        let mut res =
            request(url, resume_from, tls).chain_err(|| "failed to make network request")?;

        if !res.status().is_success() {
            let code: u16 = res.status().into();
            return Err(ErrorKind::HttpStatus(u32::from(code)).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        if let Some(len) = res.headers().get(header::CONTENT_LENGTH) {
            // TODO possible issues during unwrap?
            let len = len.to_str().unwrap().parse::<u64>().unwrap() + resume_from;
            callback(Event::DownloadContentLengthReceived(len))?;
        }

        loop {
            let bytes_read =
                io::Read::read(&mut res, &mut buffer).chain_err(|| "error reading from socket")?;

            if bytes_read != 0 {
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            } else {
                return Ok(());
            }
        }
    }

    fn client_generic() -> ClientBuilder {
        Client::builder()
            .gzip(false)
            .proxy(Proxy::custom(env_proxy))
            .timeout(Duration::from_secs(30))
    }
    #[cfg(feature = "reqwest-rustls-tls")]
    lazy_static! {
        static ref CLIENT_RUSTLS_TLS: Client = {
            let catcher = || {
                client_generic().use_rustls_tls()
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }
    #[cfg(feature = "reqwest-default-tls")]
    lazy_static! {
        static ref CLIENT_DEFAULT_TLS: Client = {
            let catcher = || {
                client_generic()
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }

    fn env_proxy(url: &Url) -> Option<Url> {
        env_proxy::for_url(url).to_url()
    }

    fn request(url: &Url, resume_from: u64, backend: TlsBackend) -> reqwest::Result<Response> {
        let client: &Client = match backend {
            #[cfg(feature = "reqwest-rustls-tls")]
            TlsBackend::Rustls => &CLIENT_RUSTLS_TLS,
            #[cfg(not(feature = "reqwest-rustls-tls"))]
            TlsBackend::Default => {
                return Err(ErrorKind::BackendUnavailable("reqwest rustls").into());
            }
            #[cfg(feature = "reqwest-default-tls")]
            TlsBackend::Default => &CLIENT_DEFAULT_TLS,
            #[cfg(not(feature = "reqwest-default-tls"))]
            TlsBackend::Default => {
                return Err(ErrorKind::BackendUnavailable("reqwest default TLS").into());
            }
        };
        let mut req = client.get(url.as_str());

        if resume_from != 0 {
            req = req.header(header::RANGE, format!("bytes={}-", resume_from));
        }

        req.send()
    }

    fn download_from_file_url(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<bool> {
        use std::fs;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = url
                .to_file_path()
                .map_err(|_| Error::from(format!("bogus file url: '{}'", url)))?;
            if !src.is_file() {
                // Because some of rustup's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::FileNotFound.into());
            }

            let mut f = fs::File::open(src).chain_err(|| "unable to open downloaded file")?;
            io::Seek::seek(&mut f, io::SeekFrom::Start(resume_from))?;

            let mut buffer = vec![0u8; 0x10000];
            loop {
                let bytes_read = io::Read::read(&mut f, &mut buffer)
                    .chain_err(|| "unable to read downloaded file")?;
                if bytes_read == 0 {
                    break;
                }
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}

#[cfg(not(feature = "curl-backend"))]
pub mod curl {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("curl").into())
    }
}

#[cfg(not(feature = "reqwest-backend"))]
pub mod reqwest_be {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("reqwest").into())
    }
}
<commit_msg>Change the return type of the request function<commit_after>//! Easy file downloading
#![deny(rust_2018_idioms)]

use std::path::Path;
use url::Url;

mod errors;
pub use crate::errors::*;

#[derive(Debug, Copy, Clone)]
pub enum Backend {
    Curl,
    Reqwest(TlsBackend),
}

#[derive(Debug, Copy, Clone)]
pub enum TlsBackend {
    Rustls,
    Default,
}

#[derive(Debug, Copy, Clone)]
pub enum Event<'a> {
    ResumingPartialDownload,
    /// Received the Content-Length of the to-be downloaded data.
    DownloadContentLengthReceived(u64),
    /// Received some data.
    DownloadDataReceived(&'a [u8]),
}

fn download_with_backend(
    backend: Backend,
    url: &Url,
    resume_from: u64,
    callback: &dyn Fn(Event<'_>) -> Result<()>,
) -> Result<()> {
    match backend {
        Backend::Curl => curl::download(url, resume_from, callback),
        Backend::Reqwest(tls) => reqwest_be::download(url, resume_from, callback, tls),
    }
}

pub fn download_to_path_with_backend(
    backend: Backend,
    url: &Url,
    path: &Path,
    resume_from_partial: bool,
    callback: Option<&dyn Fn(Event<'_>) -> Result<()>>,
) -> Result<()> {
    use std::cell::RefCell;
    use std::fs::remove_file;
    use std::fs::OpenOptions;
    use std::io::{Read, Seek, SeekFrom, Write};

    || -> Result<()> {
        let (file, resume_from) = if resume_from_partial {
            let possible_partial = OpenOptions::new().read(true).open(&path);

            let downloaded_so_far = if let Ok(mut partial) = possible_partial {
                if let Some(cb) = callback {
                    cb(Event::ResumingPartialDownload)?;

                    let mut buf = vec![0; 32768];
                    let mut downloaded_so_far = 0;
                    loop {
                        let n = partial.read(&mut buf)?;
                        downloaded_so_far += n as u64;
                        if n == 0 {
                            break;
                        }
                        cb(Event::DownloadDataReceived(&buf[..n]))?;
                    }

                    downloaded_so_far
                } else {
                    let file_info = partial.metadata()?;
                    file_info.len()
                }
            } else {
                0
            };

            let mut possible_partial = OpenOptions::new()
                .write(true)
                .create(true)
                .open(&path)
                .chain_err(|| "error opening file for download")?;

            possible_partial.seek(SeekFrom::End(0))?;

            (possible_partial, downloaded_so_far)
        } else {
            (
                OpenOptions::new()
                    .write(true)
                    .create(true)
                    .open(&path)
                    .chain_err(|| "error creating file for download")?,
                0,
            )
        };

        let file = RefCell::new(file);

        download_with_backend(backend, url, resume_from, &|event| {
            if let Event::DownloadDataReceived(data) = event {
                file.borrow_mut()
                    .write_all(data)
                    .chain_err(|| "unable to write download to disk")?;
            }
            match callback {
                Some(cb) => cb(event),
                None => Ok(()),
            }
        })?;

        file.borrow_mut()
            .sync_data()
            .chain_err(|| "unable to sync download to disk")?;

        Ok(())
    }()
    .map_err(|e| {
        // TODO: We currently clear up the cached download on any error, should we restrict it to a subset?
        remove_file(path)
            .chain_err(|| "cleaning up cached downloads")
            .unwrap();
        e
    })
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
#[cfg(feature = "curl-backend")]
pub mod curl {
    use super::Event;
    use crate::errors::*;
    use curl::easy::Easy;
    use std::cell::RefCell;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        // Fetch either a cached libcurl handle (which will preserve open
        // connections) or create a new one if it isn't listed.
        //
        // Once we've acquired it, reset the lifetime from 'static to our local
        // scope.
        thread_local!(static EASY: RefCell<Easy> = RefCell::new(Easy::new()));
        EASY.with(|handle| {
            let mut handle = handle.borrow_mut();

            handle
                .url(&url.to_string())
                .chain_err(|| "failed to set url")?;
            handle
                .follow_location(true)
                .chain_err(|| "failed to set follow redirects")?;

            if resume_from > 0 {
                handle
                    .resume_from(resume_from)
                    .chain_err(|| "setting the range header for download resumption")?;
            } else {
                // an error here indicates that the range header isn't supported by underlying curl,
                // so there's nothing to "clear" - safe to ignore this error.
                let _ = handle.resume_from(0);
            }

            // Take at most 30s to connect
            handle
                .connect_timeout(Duration::new(30, 0))
                .chain_err(|| "failed to set connect timeout")?;

            {
                let cberr = RefCell::new(None);
                let mut transfer = handle.transfer();

                // Data callback for libcurl which is called with data that's
                // downloaded. We just feed it into our hasher and also write it out
                // to disk.
                transfer
                    .write_function(|data| match callback(Event::DownloadDataReceived(data)) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *cberr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    })
                    .chain_err(|| "failed to set write")?;

                // Listen for headers and parse out a `Content-Length` (case-insensitive) if it
                // comes so we know how much we're downloading.
                transfer
                    .header_function(|header| {
                        if let Ok(data) = str::from_utf8(header) {
                            let prefix = "content-length: ";
                            if data.to_ascii_lowercase().starts_with(prefix) {
                                if let Ok(s) = data[prefix.len()..].trim().parse::<u64>() {
                                    let msg = Event::DownloadContentLengthReceived(s + resume_from);
                                    match callback(msg) {
                                        Ok(()) => (),
                                        Err(e) => {
                                            *cberr.borrow_mut() = Some(e);
                                            return false;
                                        }
                                    }
                                }
                            }
                        }
                        true
                    })
                    .chain_err(|| "failed to set header")?;

                // If an error happens check to see if we had a filesystem error up
                // in `cberr`, but we always want to punt it up.
                transfer.perform().or_else(|e| {
                    // If the original error was generated by one of our
                    // callbacks, return it.
                    match cberr.borrow_mut().take() {
                        Some(cberr) => Err(cberr),
                        None => {
                            // Otherwise, return the error from curl
                            if e.is_file_couldnt_read_file() {
                                Err(e).chain_err(|| ErrorKind::FileNotFound)
                            } else {
                                Err(e).chain_err(|| "error during download")
                            }
                        }
                    }
                })?;
            }

            // If we didn't get a 20x or 0 ("OK" for files) then return an error
            let code = handle
                .response_code()
                .chain_err(|| "failed to get response code")?;
            match code {
                0 | 200..=299 => {}
                _ => {
                    return Err(ErrorKind::HttpStatus(code).into());
                }
            };

            Ok(())
        })
    }
}

#[cfg(feature = "reqwest-backend")]
pub mod reqwest_be {
    use super::Event;
    use super::TlsBackend;
    use crate::errors::*;
    use lazy_static::lazy_static;
    use reqwest::blocking::{Client, ClientBuilder, Response};
    use reqwest::{header, Proxy};
    use std::io;
    use std::time::Duration;
    use url::Url;

    pub fn download(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
        tls: TlsBackend,
    ) -> Result<()> {
        // Short-circuit reqwest for the "file:" URL scheme
        if download_from_file_url(url, resume_from, callback)? {
            return Ok(());
        }

        let mut res =
            request(url, resume_from, tls).chain_err(|| "failed to make network request")?;

        if !res.status().is_success() {
            let code: u16 = res.status().into();
            return Err(ErrorKind::HttpStatus(u32::from(code)).into());
        }

        let buffer_size = 0x10000;
        let mut buffer = vec![0u8; buffer_size];

        if let Some(len) = res.headers().get(header::CONTENT_LENGTH) {
            // TODO possible issues during unwrap?
            let len = len.to_str().unwrap().parse::<u64>().unwrap() + resume_from;
            callback(Event::DownloadContentLengthReceived(len))?;
        }

        loop {
            let bytes_read =
                io::Read::read(&mut res, &mut buffer).chain_err(|| "error reading from socket")?;

            if bytes_read != 0 {
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            } else {
                return Ok(());
            }
        }
    }

    fn client_generic() -> ClientBuilder {
        Client::builder()
            .gzip(false)
            .proxy(Proxy::custom(env_proxy))
            .timeout(Duration::from_secs(30))
    }
    #[cfg(feature = "reqwest-rustls-tls")]
    lazy_static! {
        static ref CLIENT_RUSTLS_TLS: Client = {
            let catcher = || {
                client_generic().use_rustls_tls()
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }
    #[cfg(feature = "reqwest-default-tls")]
    lazy_static! {
        static ref CLIENT_DEFAULT_TLS: Client = {
            let catcher = || {
                client_generic()
                    .build()
            };

            // woah, an unwrap?!
            // It's OK. This is the same as what is happening in curl.
            //
            // The curl::Easy::new() internally assert!s that the initialized
            // Easy is not null. Inside reqwest, the errors here would be from
            // the TLS library returning a null pointer as well.
            catcher().unwrap()
        };
    }

    fn env_proxy(url: &Url) -> Option<Url> {
        env_proxy::for_url(url).to_url()
    }

    fn request(url: &Url, resume_from: u64, backend: TlsBackend) -> Result<Response> {
        let client: &Client = match backend {
            #[cfg(feature = "reqwest-rustls-tls")]
            TlsBackend::Rustls => &CLIENT_RUSTLS_TLS,
            #[cfg(not(feature = "reqwest-rustls-tls"))]
            TlsBackend::Rustls => {
                return Err(ErrorKind::BackendUnavailable("reqwest rustls").into());
            }
            #[cfg(feature = "reqwest-default-tls")]
            TlsBackend::Default => &CLIENT_DEFAULT_TLS,
            #[cfg(not(feature = "reqwest-default-tls"))]
            TlsBackend::Default => {
                return Err(ErrorKind::BackendUnavailable("reqwest default TLS").into());
            }
        };
        let mut req = client.get(url.as_str());

        if resume_from != 0 {
            req = req.header(header::RANGE, format!("bytes={}-", resume_from));
        }

        Ok(req.send()?)
    }

    fn download_from_file_url(
        url: &Url,
        resume_from: u64,
        callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<bool> {
        use std::fs;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = url
                .to_file_path()
                .map_err(|_| Error::from(format!("bogus file url: '{}'", url)))?;
            if !src.is_file() {
                // Because some of rustup's logic depends on checking
                // the error when a downloaded file doesn't exist, make
                // the file case return the same error value as the
                // network case.
                return Err(ErrorKind::FileNotFound.into());
            }

            let mut f = fs::File::open(src).chain_err(|| "unable to open downloaded file")?;
            io::Seek::seek(&mut f, io::SeekFrom::Start(resume_from))?;

            let mut buffer = vec![0u8; 0x10000];
            loop {
                let bytes_read = io::Read::read(&mut f, &mut buffer)
                    .chain_err(|| "unable to read downloaded file")?;
                if bytes_read == 0 {
                    break;
                }
                callback(Event::DownloadDataReceived(&buffer[0..bytes_read]))?;
            }

            Ok(true)
        } else {
            Ok(false)
        }
    }
}

#[cfg(not(feature = "curl-backend"))]
pub mod curl {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("curl").into())
    }
}

#[cfg(not(feature = "reqwest-backend"))]
pub mod reqwest_be {

    use super::Event;
    use crate::errors::*;
    use url::Url;

    pub fn download(
        _url: &Url,
        _resume_from: u64,
        _callback: &dyn Fn(Event<'_>) -> Result<()>,
    ) -> Result<()> {
        Err(ErrorKind::BackendUnavailable("reqwest").into())
    }
}
<|endoftext|>


<commit_before>use Parameter;
use fa::{VFunction, Projection};
use agents::PredictionAgent;
use agents::memory::Trace;
use geometry::Space;

use std::marker::PhantomData;

pub struct TD<S: Space, V: VFunction<S>>
{
    v_func: V,

    alpha: Parameter,
    gamma: Parameter,

    phantom: PhantomData<S>,
}

impl<S: Space, V> TD<S, V>
    where V: VFunction<S>
{
    pub fn new<T1, T2>(v_func: V, alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        TD {
            v_func: v_func,

            alpha: alpha.into(),
            gamma: gamma.into(),

            phantom: PhantomData,
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for TD<S, V>
    where V: VFunction<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let v = self.v_func.evaluate(s);
        let nv = self.v_func.evaluate(ns);

        let td_error = r + self.gamma*nv - v;
        self.v_func.update(&s, self.alpha*td_error);

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

pub struct TDLambda<S: Space, V: VFunction<S> + Projection<S>>
{
    v_func: V,
    trace: Trace,

    alpha: Parameter,
    gamma: Parameter,

    phantom: PhantomData<S>,
}

impl<S: Space, V> TDLambda<S, V>
    where V: VFunction<S> + Projection<S>
{
    pub fn new<T1, T2>(v_func: V, trace: Trace, alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        TDLambda {
            v_func: v_func,
            trace: trace,

            alpha: alpha.into(),
            gamma: gamma.into(),

            phantom: PhantomData,
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for TDLambda<S, V>
    where V: VFunction<S> + Projection<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.project(s);
        let phi_ns = self.v_func.project(ns);

        self.trace.decay(self.gamma.value());
        self.trace.update(&phi_s);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);

        self.v_func.update_phi(self.trace.get(), self.alpha*td_error);

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();

        self.trace.decay(0.0);
    }
}
<commit_msg>Replace type parameters in prediction/td.rs<commit_after>use Parameter;
use fa::{VFunction, Projection, Linear};
use agents::PredictionAgent;
use agents::memory::Trace;
use geometry::Space;

use std::marker::PhantomData;

pub struct TD<S: Space, V: VFunction<S>>
{
    v_func: V,

    alpha: Parameter,
    gamma: Parameter,

    phantom: PhantomData<S>,
}

impl<S: Space, V> TD<S, V>
    where V: VFunction<S>
{
    pub fn new<T1, T2>(v_func: V, alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        TD {
            v_func: v_func,

            alpha: alpha.into(),
            gamma: gamma.into(),

            phantom: PhantomData,
        }
    }
}

impl<S: Space, V> PredictionAgent<S> for TD<S, V>
    where V: VFunction<S>
{
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let v = self.v_func.evaluate(s);
        let nv = self.v_func.evaluate(ns);

        let td_error = r + self.gamma*nv - v;
        self.v_func.update(&s, self.alpha*td_error);

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();
    }
}

pub struct TDLambda<S: Space, P: Projection<S>>
{
    trace: Trace,
    v_func: Linear<S, P>,

    alpha: Parameter,
    gamma: Parameter,
}

impl<S: Space, P: Projection<S>> TDLambda<S, P> {
    pub fn new<T1, T2>(trace: Trace, v_func: Linear<S, P>,
                       alpha: T1, gamma: T2) -> Self
        where T1: Into<Parameter>,
              T2: Into<Parameter>
    {
        TDLambda {
            trace: trace,
            v_func: v_func,

            alpha: alpha.into(),
            gamma: gamma.into(),
        }
    }
}

impl<S: Space, P: Projection<S>> PredictionAgent<S> for TDLambda<S, P> {
    fn handle_transition(&mut self, s: &S::Repr, ns: &S::Repr, r: f64) -> Option<f64> {
        let phi_s = self.v_func.project(s);
        let phi_ns = self.v_func.project(ns);

        self.trace.decay(self.gamma.value());
        self.trace.update(&phi_s);

        let td_error = r + self.gamma*self.v_func.evaluate_phi(&phi_ns) -
            self.v_func.evaluate_phi(&phi_s);

        self.v_func.update_phi(self.trace.get(), self.alpha*td_error);

        Some(td_error)
    }

    fn handle_terminal(&mut self, _: &S::Repr) {
        self.alpha = self.alpha.step();
        self.gamma = self.gamma.step();

        self.trace.decay(0.0);
    }
}
<|endoftext|>


<commit_before>native "cdecl" mod rustrt1 = "rustrt" {
    fn pin_task();
}

native "cdecl" mod rustrt2 = "rustrt" {
    fn pin_task();
}

fn main() {
    rustrt1::pin_task();
    rustrt2::pin_task();
}
<commit_msg>xfail-fast run-pass/native-dupe.rs<commit_after>// xfail-fast - Somehow causes check-fast to livelock?? Probably because we're
// calling pin_task and that's having wierd side-effects.

native "cdecl" mod rustrt1 = "rustrt" {
    fn pin_task();
}

native "cdecl" mod rustrt2 = "rustrt" {
    fn pin_task();
}

fn main() {
    rustrt1::pin_task();
    rustrt2::pin_task();
}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(clippy)]

const ONE : i64 = 1;
const NEG_ONE : i64 = -1;
const ZERO : i64 = 0;

#[deny(identity_op)]
fn main() {
    let x = 0;

    x + 0;        //~ERROR the operation is ineffective
    0 + x;        //~ERROR the operation is ineffective
    x - ZERO;     //no error, as we skip lookups (for now)
    x | (0);      //~ERROR the operation is ineffective
    ((ZERO)) | x; //no error, as we skip lookups (for now)

    x * 1;        //~ERROR the operation is ineffective
    1 * x;        //~ERROR the operation is ineffective
    x / ONE;      //no error, as we skip lookups (for now)

    x & NEG_ONE;  //no error, as we skip lookups (for now)
    -1 & x;       //~ERROR the operation is ineffective
}
<commit_msg>added regression test for #189<commit_after>#![feature(plugin)]
#![plugin(clippy)]

const ONE : i64 = 1;
const NEG_ONE : i64 = -1;
const ZERO : i64 = 0;

#[deny(identity_op)]
fn main() {
    let x = 0;

    x + 0;        //~ERROR the operation is ineffective
    x + (1 - 1);  //~ERROR the operation is ineffective
    x + 1;
    0 + x;        //~ERROR the operation is ineffective
    1 + x;
    x - ZERO;     //no error, as we skip lookups (for now)
    x | (0);      //~ERROR the operation is ineffective
    ((ZERO)) | x; //no error, as we skip lookups (for now)

    x * 1;        //~ERROR the operation is ineffective
    1 * x;        //~ERROR the operation is ineffective
    x / ONE;      //no error, as we skip lookups (for now)

    x / 2;        //no false positive

    x & NEG_ONE;  //no error, as we skip lookups (for now)
    -1 & x;       //~ERROR the operation is ineffective
}
<|endoftext|>


<commit_before>#![deny(warnings)]

extern crate cargo_registry;
extern crate postgres;
extern crate semver;
extern crate time;

use std::env;
use std::collections::HashMap;
use time::Duration;

use cargo_registry::{VersionDownload, Version, Model};

static LIMIT: i64 = 1000;

#[allow(dead_code)] // dead in tests
fn main() {
    let daemon = env::args().nth(1).as_ref().map(|s| &s[..])
                    == Some("daemon");
    let sleep = env::args().nth(2).map(|s| s.parse::<u32>().unwrap());
    loop {
        let conn = postgres::Connection::connect(&env("DATABASE_URL")[..],
                                                 &postgres::SslMode::None).unwrap();
        update(&conn).unwrap();
        drop(conn);
        if daemon {
            std::thread::sleep_ms(sleep.unwrap() * 1000);
        } else {
            break
        }
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn update(conn: &postgres::GenericConnection) -> postgres::Result<()> {
    let mut max = 0;
    loop {
        let tx = try!(conn.transaction());
        {
            let stmt = try!(tx.prepare("SELECT * FROM version_downloads \
                                        WHERE processed = FALSE AND id > $1
                                        ORDER BY id ASC
                                        LIMIT $2"));
            let mut rows = try!(stmt.query(&[&max, &LIMIT]));
            match try!(collect(&tx, &mut rows)) {
                None => break,
                Some(m) => max = m,
            }
        }
        tx.set_commit();
        try!(tx.finish());
    }
    Ok(())
}

fn collect(tx: &postgres::Transaction,
           rows: &mut postgres::rows::Rows) -> postgres::Result<Option<i32>> {

    // Anything older than 24 hours ago will be frozen and will not be queried
    // against again.
    let cutoff = time::now_utc().to_timespec();
    let cutoff = cutoff + Duration::days(-1);

    let mut map = HashMap::new();
    for row in rows.iter() {
        let download: VersionDownload = Model::from_row(&row);
        assert!(map.insert(download.id, download).is_none());
    }
    println!("updating {} versions (cutoff {})", map.len(),
             time::at(cutoff).rfc822());
    if map.len() == 0 { return Ok(None) }

    let mut max = 0;
    let mut total = 0;
    for (id, download) in map.iter() {
        if *id > max { max = *id; }
        if download.date > cutoff && download.counted == download.downloads {
            continue
        }
        let amt = download.downloads - download.counted;

        let crate_id = Version::find(tx, download.version_id).unwrap().crate_id;

        // Update the total number of version downloads
        try!(tx.execute("UPDATE versions
                         SET downloads = downloads + $1
                         WHERE id = $2",
                        &[&amt, &download.version_id]));
        // Update the total number of crate downloads
        try!(tx.execute("UPDATE crates SET downloads = downloads + $1
                         WHERE id = $2", &[&amt, &crate_id]));

        // Update the total number of crate downloads for today
        let cnt = try!(tx.execute("UPDATE crate_downloads
                                   SET downloads = downloads + $2
                                   WHERE crate_id = $1 AND date = date($3)",
                                  &[&crate_id, &amt, &download.date]));
        if cnt == 0 {
            try!(tx.execute("INSERT INTO crate_downloads
                             (crate_id, downloads, date)
                             VALUES ($1, $2, $3)",
                            &[&crate_id, &amt, &download.date]));
        }

        // Flag this row as having been processed if we're passed the cutoff,
        // and unconditionally increment the number of counted downloads.
        try!(tx.execute("UPDATE version_downloads
                         SET processed = $2, counted = counted + $3
                         WHERE id = $1",
                        &[id, &(download.date < cutoff), &amt]));
        total += amt as i64;
    }

    // After everything else is done, update the global counter of total
    // downloads.
    try!(tx.execute("UPDATE metadata SET total_downloads = total_downloads + $1",
                    &[&total]));

    Ok(Some(max))
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use postgres;
    use semver;

    use cargo_registry::{Version, Crate, User};

    fn conn() -> postgres::Connection {
        postgres::Connection::connect(&::env("TEST_DATABASE_URL")[..],
                                      &postgres::SslMode::None).unwrap()
    }

    fn user(conn: &postgres::Transaction) -> User{
        User::find_or_insert(conn, "login", None, None, None,
                             "access_token", "api_token").unwrap()
    }

    fn crate_downloads(tx: &postgres::Transaction, id: i32, expected: usize) {
        let stmt = tx.prepare("SELECT * FROM crate_downloads
                               WHERE crate_id = $1").unwrap();
        let dl: i32 = stmt.query(&[&id]).unwrap().iter()
                          .next().unwrap().get("downloads");
        assert_eq!(dl, expected as i32);
    }

    #[test]
    fn increment() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None, &None,
                                          &None, &None, &[], &None, &None,
                                          &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, true)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 1);
        crate_downloads(&tx, krate.id, 1);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
    }

    #[test]
    fn set_processed_true() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 2, current_date - interval '2 days', false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        let stmt = tx.prepare("SELECT processed FROM version_downloads
                               WHERE version_id = $1").unwrap();
        let processed: bool = stmt.query(&[&version.id]).unwrap().iter()
                                  .next().unwrap().get("processed");
        assert!(processed);
    }

    #[test]
    fn increment_a_little() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 1, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 2);
        crate_downloads(&tx, krate.id, 2);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
    }
}
<commit_msg>Fix compile on nightly<commit_after>#![deny(warnings)]

extern crate cargo_registry;
extern crate postgres;
extern crate semver;
extern crate time;

use std::env;
use std::collections::HashMap;
use time::Duration;

use cargo_registry::{VersionDownload, Version, Model};

static LIMIT: i64 = 1000;

#[allow(dead_code)] // dead in tests
fn main() {
    let daemon = env::args().nth(1).as_ref().map(|s| &s[..])
                    == Some("daemon");
    let sleep = env::args().nth(2).map(|s| s.parse::<u32>().unwrap());
    loop {
        let conn = postgres::Connection::connect(&env("DATABASE_URL")[..],
                                                 &postgres::SslMode::None).unwrap();
        update(&conn).unwrap();
        drop(conn);
        if daemon {
            std::thread::sleep_ms(sleep.unwrap() * 1000);
        } else {
            break
        }
    }
}

fn env(s: &str) -> String {
    match env::var(s).ok() {
        Some(s) => s,
        None => panic!("must have `{}` defined", s),
    }
}

fn update(conn: &postgres::GenericConnection) -> postgres::Result<()> {
    let mut max = 0;
    loop {
        // FIXME(rust-lang/rust#27401): weird declaration to make sure this
        // variable gets dropped.
        let tx; tx = try!(conn.transaction());
        {
            let stmt = try!(tx.prepare("SELECT * FROM version_downloads \
                                        WHERE processed = FALSE AND id > $1
                                        ORDER BY id ASC
                                        LIMIT $2"));
            let mut rows = try!(stmt.query(&[&max, &LIMIT]));
            match try!(collect(&tx, &mut rows)) {
                None => break,
                Some(m) => max = m,
            }
        }
        tx.set_commit();
        try!(tx.finish());
    }
    Ok(())
}

fn collect(tx: &postgres::Transaction,
           rows: &mut postgres::rows::Rows) -> postgres::Result<Option<i32>> {

    // Anything older than 24 hours ago will be frozen and will not be queried
    // against again.
    let cutoff = time::now_utc().to_timespec();
    let cutoff = cutoff + Duration::days(-1);

    let mut map = HashMap::new();
    for row in rows.iter() {
        let download: VersionDownload = Model::from_row(&row);
        assert!(map.insert(download.id, download).is_none());
    }
    println!("updating {} versions (cutoff {})", map.len(),
             time::at(cutoff).rfc822());
    if map.len() == 0 { return Ok(None) }

    let mut max = 0;
    let mut total = 0;
    for (id, download) in map.iter() {
        if *id > max { max = *id; }
        if download.date > cutoff && download.counted == download.downloads {
            continue
        }
        let amt = download.downloads - download.counted;

        let crate_id = Version::find(tx, download.version_id).unwrap().crate_id;

        // Update the total number of version downloads
        try!(tx.execute("UPDATE versions
                         SET downloads = downloads + $1
                         WHERE id = $2",
                        &[&amt, &download.version_id]));
        // Update the total number of crate downloads
        try!(tx.execute("UPDATE crates SET downloads = downloads + $1
                         WHERE id = $2", &[&amt, &crate_id]));

        // Update the total number of crate downloads for today
        let cnt = try!(tx.execute("UPDATE crate_downloads
                                   SET downloads = downloads + $2
                                   WHERE crate_id = $1 AND date = date($3)",
                                  &[&crate_id, &amt, &download.date]));
        if cnt == 0 {
            try!(tx.execute("INSERT INTO crate_downloads
                             (crate_id, downloads, date)
                             VALUES ($1, $2, $3)",
                            &[&crate_id, &amt, &download.date]));
        }

        // Flag this row as having been processed if we're passed the cutoff,
        // and unconditionally increment the number of counted downloads.
        try!(tx.execute("UPDATE version_downloads
                         SET processed = $2, counted = counted + $3
                         WHERE id = $1",
                        &[id, &(download.date < cutoff), &amt]));
        total += amt as i64;
    }

    // After everything else is done, update the global counter of total
    // downloads.
    try!(tx.execute("UPDATE metadata SET total_downloads = total_downloads + $1",
                    &[&total]));

    Ok(Some(max))
}

#[cfg(test)]
mod test {
    use std::collections::HashMap;

    use postgres;
    use semver;

    use cargo_registry::{Version, Crate, User};

    fn conn() -> postgres::Connection {
        postgres::Connection::connect(&::env("TEST_DATABASE_URL")[..],
                                      &postgres::SslMode::None).unwrap()
    }

    fn user(conn: &postgres::Transaction) -> User{
        User::find_or_insert(conn, "login", None, None, None,
                             "access_token", "api_token").unwrap()
    }

    fn crate_downloads(tx: &postgres::Transaction, id: i32, expected: usize) {
        let stmt = tx.prepare("SELECT * FROM crate_downloads
                               WHERE crate_id = $1").unwrap();
        let dl: i32 = stmt.query(&[&id]).unwrap().iter()
                          .next().unwrap().get("downloads");
        assert_eq!(dl, expected as i32);
    }

    #[test]
    fn increment() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None, &None,
                                          &None, &None, &[], &None, &None,
                                          &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, true)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 1);
        crate_downloads(&tx, krate.id, 1);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 1);
    }

    #[test]
    fn set_processed_true() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 2, current_date - interval '2 days', false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        let stmt = tx.prepare("SELECT processed FROM version_downloads
                               WHERE version_id = $1").unwrap();
        let processed: bool = stmt.query(&[&version.id]).unwrap().iter()
                                  .next().unwrap().get("processed");
        assert!(processed);
    }

    #[test]
    fn increment_a_little() {
        let conn = conn();
        let tx = conn.transaction().unwrap();
        let user = user(&tx);
        let krate = Crate::find_or_insert(&tx, "foo", user.id, &None,
                                          &None, &None, &None, &[], &None,
                                          &None, &None).unwrap();
        let version = Version::insert(&tx, krate.id,
                                      &semver::Version::parse("1.0.0").unwrap(),
                                      &HashMap::new(), &[]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 2, 1, current_date, false)",
                   &[&version.id]).unwrap();
        tx.execute("INSERT INTO version_downloads \
                    (version_id, downloads, counted, date, processed)
                    VALUES ($1, 1, 0, current_date, false)",
                   &[&version.id]).unwrap();
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
        assert_eq!(Crate::find(&tx, krate.id).unwrap().downloads, 2);
        crate_downloads(&tx, krate.id, 2);
        ::update(&tx).unwrap();
        assert_eq!(Version::find(&tx, version.id).unwrap().downloads, 2);
    }
}
<|endoftext|>


<commit_before>//! OpenGL back-end for Piston-Graphics.

// External crates.
use std::ffi::CString;
use shader_version::{ OpenGL, Shaders };
use shader_version::glsl::GLSL;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::color::gamma_srgb_to_linear;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};

// Local crate.
use draw_state;
use Texture;
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

struct Colored {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: GLint,
}

impl Drop for Colored {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
            gl::DeleteProgram(self.program);
            gl::DeleteShader(self.vertex_shader);
            gl::DeleteShader(self.fragment_shader);
        }
    }
}

impl Colored {
    fn new(glsl: GLSL) -> Self {
        use shaders::colored;

        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };

        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            Shaders::new().set(GLSL::V1_20, src(colored::VERTEX_GLSL_120))
                          .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            Shaders::new().set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120))
                          .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };

        let program;
        unsafe {
            program = gl::CreateProgram();
            gl::AttachShader(program, vertex_shader);
            gl::AttachShader(program, fragment_shader);

            let c_o_color = CString::new("o_Color").unwrap();
            gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
            drop(c_o_color);
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
            gl::LinkProgram(program);
        }
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let c_color = CString::new("color").unwrap();
        let color = unsafe {
                gl::GetUniformLocation(program, c_color.as_ptr())
            };
        drop(c_color);
        if color == -1 {
            panic!("Could not find uniform `color`");
        }
        Colored {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct Textured {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    color: GLint,
    pos: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for Textured {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
            gl::DeleteProgram(self.program);
            gl::DeleteShader(self.vertex_shader);
            gl::DeleteShader(self.fragment_shader);
        }
    }
}

impl Textured {
    fn new(glsl: GLSL) -> Self {
        use shaders::textured;

        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };

        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            Shaders::new().set(GLSL::V1_20, src(textured::VERTEX_GLSL_120))
                          .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            Shaders::new().set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120))
                          .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };

        let program;
        unsafe {
            program = gl::CreateProgram();
            gl::AttachShader(program, vertex_shader);
            gl::AttachShader(program, fragment_shader);

            let c_o_color = CString::new("o_Color").unwrap();
            gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
            drop(c_o_color);
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
            gl::LinkProgram(program);
        }
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let c_color = CString::new("color").unwrap();
        let color = unsafe {
                gl::GetUniformLocation(program, c_color.as_ptr())
            };
        drop(c_color);
        if color == -1 {
            panic!("Could not find uniform `color`");
        }
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        Textured {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

// Newlines and indents for cleaner panic message.
const GL_FUNC_NOT_LOADED: &'static str = "
    OpenGL function pointers must be loaded before creating the `Gl` backend!
    For more info, see the following issue on GitHub:
    https://github.com/PistonDevelopers/opengl_graphics/issues/103
";

/// Contains OpenGL data.
pub struct GlGraphics {
    colored: Colored,
    textured: Textured,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
    // Keeps track of the current draw state.
    current_draw_state: Option<DrawState>,
}

impl<'a> GlGraphics {
    /// Creates a new OpenGL back-end.
    ///
    /// # Panics
    /// If the OpenGL function pointers have not been loaded yet.
    /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info.
    pub fn new(opengl: OpenGL) -> Self {
        assert!(gl::Enable::is_loaded(), GL_FUNC_NOT_LOADED);

        let glsl = opengl.to_glsl();
        // Load the vertices, color and texture coord buffers.
        GlGraphics {
            colored: Colored::new(glsl),
            textured: Textured::new(glsl),
            current_program: None,
            current_draw_state: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        unsafe {
            gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
        }
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return }
            },
        }

        unsafe {
            gl::UseProgram(program);
        }
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }

    /// Sets the current draw state, by detecting changes.
    pub fn use_draw_state(&mut self, draw_state: &DrawState) {
        match self.current_draw_state {
            None => {
                draw_state::bind_scissor(draw_state.scissor);
                draw_state::bind_stencil(draw_state.stencil);
                draw_state::bind_blend(draw_state.blend);
            }
            Some(ref old_state) => {
                draw_state::bind_state(old_state, draw_state);
            }
        }
        self.current_draw_state = Some(*draw_state);
    }

    /// Unsets the current draw state.
    ///
    /// This forces the current draw state to be set on next drawing call.
    pub fn clear_draw_state(&mut self) {
        self.current_draw_state = None;
    }

    /// Draws graphics.
    pub fn draw<F>(&mut self, viewport: Viewport, f: F)
        where
            F: FnOnce(Context, &mut Self)
    {
        let rect = viewport.rect;
        let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
        self.viewport(x, y, w, h);
        self.clear_program();
        unsafe { gl::Enable(gl::FRAMEBUFFER_SRGB); }
        let c = Context::new_viewport(viewport);
        f(c, self);
    }

    /// Assume all textures has alpha channel for now.
    pub fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }
}

impl Graphics for GlGraphics {
    type Texture = Texture;

    fn clear_color(&mut self, color: [f32; 4]) {
        let color = gamma_srgb_to_linear(color);
        unsafe {
            let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
            gl::ClearColor(r, g, b, a);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        }
    }

    fn clear_stencil(&mut self, value: u8) {
        unsafe {
            gl::ClearStencil(value as i32);
        }
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let color = gamma_srgb_to_linear(*color);
        {
            // Set shader program and draw state.
            let shader_program = self.colored.program;
            self.use_program(shader_program);
            self.use_draw_state(draw_state);
        }
        let ref mut shader = self.colored;

        unsafe {
            gl::BindVertexArray(shader.vao);
            gl::Uniform4f(shader.color, color[0], color[1], color[2], color[3]);
            // Render triangles whether they are facing
            // clockwise or counter clockwise.
            gl::Disable(gl::CULL_FACE);
        }

        f(&mut |vertices: &[f32]| {
            // xy makes two floats.
            let size_vertices: i32 = 2;
            let items: i32 = vertices.len() as i32 / size_vertices;

            unsafe {
                shader.pos.set(vertices);
                gl::DrawArrays(gl::TRIANGLES, 0, items);
            }
        });

        unsafe {
            gl::BindVertexArray(0);
        }
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let color = gamma_srgb_to_linear(*color);
        {
            // Set shader program and draw state.
            let shader_program = self.textured.program;
            self.use_program(shader_program);
            self.use_draw_state(draw_state);
        }
        let ref mut shader = self.textured;

        let texture = texture.get_id();
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, texture);
            // Render triangles whether they are facing
            // clockwise or counter clockwise.
            gl::Disable(gl::CULL_FACE);
            gl::BindVertexArray(shader.vao);
            gl::Uniform4f(shader.color, color[0], color[1], color[2], color[3]);
        }

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            let size_vertices: i32 = 2;
            let items: i32 = vertices.len() as i32 / size_vertices;

            unsafe {
                shader.pos.set(vertices);
                shader.uv.set(texture_coords);
                gl::DrawArrays(gl::TRIANGLES, 0, items);
            }
        });

        unsafe {
            gl::BindVertexArray(0);
        }
    }
}

// Might not fail if previous tests loaded functions.
#[test]
#[should_panic]
fn test_gl_loaded() {
    GlGraphics::new(OpenGL::V3_2);
}
<commit_msg>Made `GlGraphics::draw` return generic type<commit_after>//! OpenGL back-end for Piston-Graphics.

// External crates.
use std::ffi::CString;
use shader_version::{ OpenGL, Shaders };
use shader_version::glsl::GLSL;
use graphics::{ Context, DrawState, Graphics, Viewport };
use graphics::color::gamma_srgb_to_linear;
use gl;
use gl::types::{
    GLint,
    GLsizei,
    GLuint,
};

// Local crate.
use draw_state;
use Texture;
use shader_utils::{
    compile_shader,
    DynamicAttribute,
};

struct Colored {
    vao: GLuint,
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    pos: DynamicAttribute,
    color: GLint,
}

impl Drop for Colored {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
            gl::DeleteProgram(self.program);
            gl::DeleteShader(self.vertex_shader);
            gl::DeleteShader(self.fragment_shader);
        }
    }
}

impl Colored {
    fn new(glsl: GLSL) -> Self {
        use shaders::colored;

        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };

        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            Shaders::new().set(GLSL::V1_20, src(colored::VERTEX_GLSL_120))
                          .set(GLSL::V1_50, src(colored::VERTEX_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            Shaders::new().set(GLSL::V1_20, src(colored::FRAGMENT_GLSL_120))
                          .set(GLSL::V1_50, src(colored::FRAGMENT_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };

        let program;
        unsafe {
            program = gl::CreateProgram();
            gl::AttachShader(program, vertex_shader);
            gl::AttachShader(program, fragment_shader);

            let c_o_color = CString::new("o_Color").unwrap();
            gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
            drop(c_o_color);
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
            gl::LinkProgram(program);
        }
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let c_color = CString::new("color").unwrap();
        let color = unsafe {
                gl::GetUniformLocation(program, c_color.as_ptr())
            };
        drop(c_color);
        if color == -1 {
            panic!("Could not find uniform `color`");
        }
        Colored {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
        }
    }
}

struct Textured {
    vertex_shader: GLuint,
    fragment_shader: GLuint,
    program: GLuint,
    vao: GLuint,
    color: GLint,
    pos: DynamicAttribute,
    uv: DynamicAttribute,
}

impl Drop for Textured {
    fn drop(&mut self) {
        unsafe {
            gl::DeleteVertexArrays(1, &self.vao);
            gl::DeleteProgram(self.program);
            gl::DeleteShader(self.vertex_shader);
            gl::DeleteShader(self.fragment_shader);
        }
    }
}

impl Textured {
    fn new(glsl: GLSL) -> Self {
        use shaders::textured;

        let src = |bytes| unsafe { ::std::str::from_utf8_unchecked(bytes) };

        let vertex_shader = match compile_shader(
            gl::VERTEX_SHADER,                  // shader type
            Shaders::new().set(GLSL::V1_20, src(textured::VERTEX_GLSL_120))
                          .set(GLSL::V1_50, src(textured::VERTEX_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };
        let fragment_shader = match compile_shader(
            gl::FRAGMENT_SHADER,                // shader type
            Shaders::new().set(GLSL::V1_20, src(textured::FRAGMENT_GLSL_120))
                          .set(GLSL::V1_50, src(textured::FRAGMENT_GLSL_150_CORE))
                          .get(glsl).unwrap()
        ) {
            Ok(id) => id,
            Err(s) => panic!("compile_shader: {}", s)
        };

        let program;
        unsafe {
            program = gl::CreateProgram();
            gl::AttachShader(program, vertex_shader);
            gl::AttachShader(program, fragment_shader);

            let c_o_color = CString::new("o_Color").unwrap();
            gl::BindFragDataLocation(program, 0, c_o_color.as_ptr());
            drop(c_o_color);
        }

        let mut vao = 0;
        unsafe {
            gl::GenVertexArrays(1, &mut vao);
            gl::LinkProgram(program);
        }
        let pos = DynamicAttribute::xy(
                program,
                "pos",
                vao
            ).unwrap();
        let c_color = CString::new("color").unwrap();
        let color = unsafe {
                gl::GetUniformLocation(program, c_color.as_ptr())
            };
        drop(c_color);
        if color == -1 {
            panic!("Could not find uniform `color`");
        }
        let uv = DynamicAttribute::uv(
                program,
                "uv",
                vao
            ).unwrap();
        Textured {
            vao: vao,
            vertex_shader: vertex_shader,
            fragment_shader: fragment_shader,
            program: program,
            pos: pos,
            color: color,
            uv: uv,
        }
    }
}

// Newlines and indents for cleaner panic message.
const GL_FUNC_NOT_LOADED: &'static str = "
    OpenGL function pointers must be loaded before creating the `Gl` backend!
    For more info, see the following issue on GitHub:
    https://github.com/PistonDevelopers/opengl_graphics/issues/103
";

/// Contains OpenGL data.
pub struct GlGraphics {
    colored: Colored,
    textured: Textured,
    // Keeps track of the current shader program.
    current_program: Option<GLuint>,
    // Keeps track of the current draw state.
    current_draw_state: Option<DrawState>,
}

impl<'a> GlGraphics {
    /// Creates a new OpenGL back-end.
    ///
    /// # Panics
    /// If the OpenGL function pointers have not been loaded yet.
    /// See https://github.com/PistonDevelopers/opengl_graphics/issues/103 for more info.
    pub fn new(opengl: OpenGL) -> Self {
        assert!(gl::Enable::is_loaded(), GL_FUNC_NOT_LOADED);

        let glsl = opengl.to_glsl();
        // Load the vertices, color and texture coord buffers.
        GlGraphics {
            colored: Colored::new(glsl),
            textured: Textured::new(glsl),
            current_program: None,
            current_draw_state: None,
       }
    }

    /// Sets viewport with normalized coordinates and center as origin.
    pub fn viewport(
        &mut self,
        x: i32,
        y: i32,
        w: i32,
        h: i32
    ) {
        unsafe {
            gl::Viewport(x as GLint, y as GLint, w as GLsizei, h as GLsizei);
        }
    }

    /// Sets the current program only if the program is not in use.
    pub fn use_program(&mut self, program: GLuint) {
        match self.current_program {
            None => {},
            Some(current_program) => {
                if program == current_program { return }
            },
        }

        unsafe {
            gl::UseProgram(program);
        }
        self.current_program = Some(program);
    }

    /// Unset the current program.
    ///
    /// This forces the current program to be set on next drawing call.
    pub fn clear_program(&mut self) {
        self.current_program = None
    }

    /// Sets the current draw state, by detecting changes.
    pub fn use_draw_state(&mut self, draw_state: &DrawState) {
        match self.current_draw_state {
            None => {
                draw_state::bind_scissor(draw_state.scissor);
                draw_state::bind_stencil(draw_state.stencil);
                draw_state::bind_blend(draw_state.blend);
            }
            Some(ref old_state) => {
                draw_state::bind_state(old_state, draw_state);
            }
        }
        self.current_draw_state = Some(*draw_state);
    }

    /// Unsets the current draw state.
    ///
    /// This forces the current draw state to be set on next drawing call.
    pub fn clear_draw_state(&mut self) {
        self.current_draw_state = None;
    }

    /// Draws graphics.
    pub fn draw<F, U>(&mut self, viewport: Viewport, f: F) -> U
        where
            F: FnOnce(Context, &mut Self) -> U
    {
        let rect = viewport.rect;
        let (x, y, w, h) = (rect[0], rect[1], rect[2], rect[3]);
        self.viewport(x, y, w, h);
        self.clear_program();
        unsafe { gl::Enable(gl::FRAMEBUFFER_SRGB); }
        let c = Context::new_viewport(viewport);
        f(c, self)
    }

    /// Assume all textures has alpha channel for now.
    pub fn has_texture_alpha(&self, _texture: &Texture) -> bool { true }
}

impl Graphics for GlGraphics {
    type Texture = Texture;

    fn clear_color(&mut self, color: [f32; 4]) {
        let color = gamma_srgb_to_linear(color);
        unsafe {
            let (r, g, b, a) = (color[0], color[1], color[2], color[3]);
            gl::ClearColor(r, g, b, a);
            gl::Clear(gl::COLOR_BUFFER_BIT | gl::DEPTH_BUFFER_BIT);
        }
    }

    fn clear_stencil(&mut self, value: u8) {
        unsafe {
            gl::ClearStencil(value as i32);
        }
    }

    fn tri_list<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32]))
    {
        let color = gamma_srgb_to_linear(*color);
        {
            // Set shader program and draw state.
            let shader_program = self.colored.program;
            self.use_program(shader_program);
            self.use_draw_state(draw_state);
        }
        let ref mut shader = self.colored;

        unsafe {
            gl::BindVertexArray(shader.vao);
            gl::Uniform4f(shader.color, color[0], color[1], color[2], color[3]);
            // Render triangles whether they are facing
            // clockwise or counter clockwise.
            gl::Disable(gl::CULL_FACE);
        }

        f(&mut |vertices: &[f32]| {
            // xy makes two floats.
            let size_vertices: i32 = 2;
            let items: i32 = vertices.len() as i32 / size_vertices;

            unsafe {
                shader.pos.set(vertices);
                gl::DrawArrays(gl::TRIANGLES, 0, items);
            }
        });

        unsafe {
            gl::BindVertexArray(0);
        }
    }

    fn tri_list_uv<F>(
        &mut self,
        draw_state: &DrawState,
        color: &[f32; 4],
        texture: &Texture,
        mut f: F
    )
        where F: FnMut(&mut FnMut(&[f32], &[f32]))
    {
        let color = gamma_srgb_to_linear(*color);
        {
            // Set shader program and draw state.
            let shader_program = self.textured.program;
            self.use_program(shader_program);
            self.use_draw_state(draw_state);
        }
        let ref mut shader = self.textured;

        let texture = texture.get_id();
        unsafe {
            gl::BindTexture(gl::TEXTURE_2D, texture);
            // Render triangles whether they are facing
            // clockwise or counter clockwise.
            gl::Disable(gl::CULL_FACE);
            gl::BindVertexArray(shader.vao);
            gl::Uniform4f(shader.color, color[0], color[1], color[2], color[3]);
        }

        f(&mut |vertices: &[f32], texture_coords: &[f32]| {
            let size_vertices: i32 = 2;
            let items: i32 = vertices.len() as i32 / size_vertices;

            unsafe {
                shader.pos.set(vertices);
                shader.uv.set(texture_coords);
                gl::DrawArrays(gl::TRIANGLES, 0, items);
            }
        });

        unsafe {
            gl::BindVertexArray(0);
        }
    }
}

// Might not fail if previous tests loaded functions.
#[test]
#[should_panic]
fn test_gl_loaded() {
    GlGraphics::new(OpenGL::V3_2);
}
<|endoftext|>


<commit_before>extern crate glium;
extern crate image;
extern crate zip;
extern crate cgmath;
extern crate notify;

use std::io::Read;
use std::fs::File;
use std::io::Cursor;

use glium::backend::Facade;
use glium::texture::{UncompressedUintFormat, MipmapsOption};
use glium::texture::pixel_buffer::PixelBuffer;
use glium::texture::unsigned_texture2d_array::UnsignedTexture2dArray;

pub struct Lightfield {
    pub tex: UnsignedTexture2dArray,
}

impl Lightfield {
    #![allow(dead_code)]
    pub fn new<F: Facade>(facade: &F, zip_filename: &str) -> Lightfield {
        let zipfile = File::open(zip_filename).unwrap();
        let mut archive = zip::ZipArchive::new(zipfile).unwrap();

        // FIXME n's and sizes
        let nx = 2;
        let ny = 2;
        let width = 1400;
        let height = 800;

        let n = nx*ny;
        let tex = UnsignedTexture2dArray::empty_with_format(
            facade,
            UncompressedUintFormat::U8U8U8U8,
            MipmapsOption::NoMipmap,
            width, height, n).unwrap();

        for i in 0..archive.len()
        {
            let mut file = &mut archive.by_index(i).unwrap();
            let name = String::from(file.name());
            println!("loading {}", name);
            let parts: Vec<&str> = name.split("_").collect();
            let ix:u32 = parts[1].parse().unwrap();
            let iy:u32 = parts[2].parse().unwrap();
            assert!(parts[0] == "out");
            let mut contents = Vec::new();
            file.read_to_end(&mut contents).unwrap();

            let image = image::load(Cursor::new(contents), image::JPEG).unwrap().to_rgba();
            let image_dimensions = image.dimensions();
            assert!(image_dimensions == (width, height));
            //let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
            let layer = iy*nx+ix;
            println!("loading layer {}", layer);
            let size = width * height * 4;
            let buffer = PixelBuffer::new_empty(facade, size as usize);
            buffer.write(&image.into_raw());
            assert!(layer<n);
            tex.main_level().raw_upload_from_pixel_buffer(buffer.as_slice(),
            0..width,
            0..height,
            layer..layer+1);
            break; // XXX debug
        }
        Lightfield {tex: tex}
    }
}
<commit_msg>fast lightfield loader shit, only load 1 layer<commit_after>extern crate glium;
extern crate image;
extern crate zip;
extern crate cgmath;
extern crate notify;

use std::io::Read;
use std::fs::File;
use std::io::Cursor;

use glium::backend::Facade;
use glium::texture::{UncompressedUintFormat, MipmapsOption};
use glium::texture::pixel_buffer::PixelBuffer;
use glium::texture::unsigned_texture2d_array::UnsignedTexture2dArray;

pub struct Lightfield {
    pub tex: UnsignedTexture2dArray,
}

impl Lightfield {
    #![allow(dead_code)]
    pub fn new<F: Facade>(facade: &F, zip_filename: &str) -> Lightfield {
        let zipfile = File::open(zip_filename).unwrap();
        let mut archive = zip::ZipArchive::new(zipfile).unwrap();

        // FIXME n's and sizes
        let nx = 1;
        let ny = 1;
        let width = 1400;
        let height = 800;

        let n = nx*ny;
        let tex = UnsignedTexture2dArray::empty_with_format(
            facade,
            UncompressedUintFormat::U8U8U8U8,
            MipmapsOption::NoMipmap,
            width, height, n).unwrap();

        for i in 0..archive.len()
        {
            let mut file = &mut archive.by_index(i).unwrap();
            let name = String::from(file.name());
            println!("loading {}", name);
            let parts: Vec<&str> = name.split("_").collect();
            let ix:u32 = parts[1].parse().unwrap();
            let iy:u32 = parts[2].parse().unwrap();
            assert!(parts[0] == "out");
            let mut contents = Vec::new();
            file.read_to_end(&mut contents).unwrap();

            let image = image::load(Cursor::new(contents), image::JPEG).unwrap().to_rgba();
            let image_dimensions = image.dimensions();
            assert!(image_dimensions == (width, height));
            //let image = glium::texture::RawImage2d::from_raw_rgba_reversed(image.into_raw(), image_dimensions);
            let layer = 0; // iy*nx+ix;
            println!("loading layer {}", layer);
            let size = width * height * 4;
            let buffer = PixelBuffer::new_empty(facade, size as usize);
            buffer.write(&image.into_raw());
            assert!(layer<n);
            tex.main_level().raw_upload_from_pixel_buffer(buffer.as_slice(),
            0..width,
            0..height,
            layer..layer+1);
            break; // XXX debug
        }
        Lightfield {tex: tex}
    }
}
<|endoftext|>


<commit_before>pub trait GerberCode {
    fn to_code(&self) -> String;
}

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

impl GerberCode for InterpolationMode {
    fn to_code(&self) -> String {
        match *self {
            InterpolationMode::Linear => "G01*",
            InterpolationMode::ClockwiseCircular => "G02*",
            InterpolationMode::CounterclockwiseCircular => "G03*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

impl GerberCode for QuadrantMode {
    fn to_code(&self) -> String {
        match *self {
            QuadrantMode::Single => "G74*",
            QuadrantMode::Multi => "G75*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match *self {
            GCode::InterpolationMode(ref mode) => mode.to_code(),
            GCode::RegionMode(enabled) => {
                match enabled {
                    true => "G36*",
                    false => "G37*",
                }.to_string()
            },
            GCode::QuadrantMode(ref mode) => mode.to_code(),
            GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for MCode {
    fn to_code(&self) -> String {
        match *self {
            MCode::EndOfFile => "M02*",
        }.to_string()
    }
}

/// Implement GerberCode for Vectors of types that implement GerberCode.
impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode, DCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::{MCode};
    use super::GerberCode;

    #[test]
    fn test_debug() {
        //! The debug representation should work properly.
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        //! The to_code method of the GerberCode trait should generate strings.
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        //! A `Vec<T: GerberCode>` should also implement `GerberCode`.
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

    #[test]
    fn test_end_of_file() {
        let c = MCode::EndOfFile;
        assert_eq!(c.to_code(), "M02*".to_string());
    }

}
<commit_msg>Simplify GerberCode impl of RegionMode<commit_after>pub trait GerberCode {
    fn to_code(&self) -> String;
}

#[derive(Debug)]
pub enum Command {
    FunctionCode(FunctionCode),
    ExtendedCode(ExtendedCode),
}

#[derive(Debug)]
pub enum FunctionCode {
    DCode(DCode),
    GCode(GCode),
    MCode(MCode),
}

#[derive(Debug)]
pub enum ExtendedCode {
    FS,
    MO,
    AD,
    AM,
    SR,
    LP,
    TF,
    TA,
    TD,
}

#[derive(Debug)]
pub enum DCode {
    Operation,
    SelectAperture,
}

#[derive(Debug)]
pub enum InterpolationMode {
    Linear,
    ClockwiseCircular,
    CounterclockwiseCircular,
}

impl GerberCode for InterpolationMode {
    fn to_code(&self) -> String {
        match *self {
            InterpolationMode::Linear => "G01*",
            InterpolationMode::ClockwiseCircular => "G02*",
            InterpolationMode::CounterclockwiseCircular => "G03*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum QuadrantMode {
    Single,
    Multi,
}

impl GerberCode for QuadrantMode {
    fn to_code(&self) -> String {
        match *self {
            QuadrantMode::Single => "G74*",
            QuadrantMode::Multi => "G75*",
        }.to_string()
    }
}

#[derive(Debug)]
pub enum GCode {
    InterpolationMode(InterpolationMode),
    RegionMode(bool),
    QuadrantMode(QuadrantMode),
    Comment(String),
}

impl GerberCode for GCode {
    fn to_code(&self) -> String {
        match *self {
            GCode::InterpolationMode(ref mode) => mode.to_code(),
            GCode::RegionMode(enabled) => if enabled { "G36*".to_string() } else { "G37*".to_string() },
            GCode::QuadrantMode(ref mode) => mode.to_code(),
            GCode::Comment(ref comment) => format!("G04 {} *", comment),
        }
    }
}

#[derive(Debug)]
pub enum MCode {
    EndOfFile,
}

impl GerberCode for MCode {
    fn to_code(&self) -> String {
        match *self {
            MCode::EndOfFile => "M02*",
        }.to_string()
    }
}

/// Implement GerberCode for Vectors of types that implement GerberCode.
impl<T: GerberCode> GerberCode for Vec<T> {
    fn to_code(&self) -> String {
        self.iter()
            .map(|g| g.to_code())
            .collect::<Vec<String>>()
            .join("\n")
    }
}

#[cfg(test)]
mod test {
    use super::{Command, FunctionCode, DCode};
    use super::{GCode, InterpolationMode, QuadrantMode};
    use super::{MCode};
    use super::GerberCode;

    #[test]
    fn test_debug() {
        //! The debug representation should work properly.
        let c = Command::FunctionCode(FunctionCode::GCode(GCode::Comment("test".to_string())));
        let debug = format!("{:?}", c);
        assert_eq!(debug, "FunctionCode(GCode(Comment(\"test\")))");
    }

    #[test]
    fn test_to_code() {
        //! The to_code method of the GerberCode trait should generate strings.
        let comment = GCode::Comment("testcomment".to_string());
        assert_eq!(comment.to_code(), "G04 testcomment *".to_string());
    }

    #[test]
    fn test_vec_to_code() {
        //! A `Vec<T: GerberCode>` should also implement `GerberCode`.
        let mut v = Vec::new();
        v.push(GCode::Comment("comment 1".to_string()));
        v.push(GCode::Comment("another one".to_string()));
        assert_eq!(v.to_code(), "G04 comment 1 *\nG04 another one *".to_string());
    }

    #[test]
    fn test_interpolation_mode() {
        let mut commands = Vec::new();
        let c1 = GCode::InterpolationMode(InterpolationMode::Linear);
        let c2 = GCode::InterpolationMode(InterpolationMode::ClockwiseCircular);
        let c3 = GCode::InterpolationMode(InterpolationMode::CounterclockwiseCircular);
        commands.push(c1);
        commands.push(c2);
        commands.push(c3);
        assert_eq!(commands.to_code(), "G01*\nG02*\nG03*".to_string());
    }

    #[test]
    fn test_region_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::RegionMode(true));
        commands.push(GCode::RegionMode(false));
        assert_eq!(commands.to_code(), "G36*\nG37*".to_string());
    }

    #[test]
    fn test_quadrant_mode() {
        let mut commands = Vec::new();
        commands.push(GCode::QuadrantMode(QuadrantMode::Single));
        commands.push(GCode::QuadrantMode(QuadrantMode::Multi));
        assert_eq!(commands.to_code(), "G74*\nG75*".to_string());
    }

    #[test]
    fn test_end_of_file() {
        let c = MCode::EndOfFile;
        assert_eq!(c.to_code(), "M02*".to_string());
    }

}
<|endoftext|>