<commit_before>//! Easy file downloading

use errors::*;
use notifications::Notification;
use sha2::Sha256;
use std::env;
use std::path::Path;
use url::Url;

pub fn download_file(url: &Url,
                     path: &Path,
                     hasher: Option<&mut Sha256>,
                     notify_handler: &Fn(Notification))
                     -> Result<()> {
    if env::var_os("RUSTUP_USE_HYPER").is_some() {
        notify_handler(Notification::UsingHyper);
        try!(self::hyper::download_file(url, path, hasher, notify_handler));
    } else {
        notify_handler(Notification::UsingCurl);
        try!(self::curl::download_file(url, path, hasher, notify_handler));
    }

    notify_handler(Notification::DownloadFinished);

    Ok(())
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
mod curl {
    use curl::easy::Easy;
    use errors::*;
    use notifications::Notification;
    use sha2::{Sha256, Digest};
    use std::cell::RefCell;
    use std::fs;
    use std::path::Path;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {
        use notifications::Notification;
        use std::io::Write;

        let mut file = try!(fs::File::create(&path).chain_err(
            || "error creating file for download"));

        // 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();

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

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

            // Fail if less than 10 bytes are transferred every 30 seconds
            try!(handle.low_speed_limit(10).chain_err(|| "failed to set low speed limit"));
            try!(handle.low_speed_time(Duration::new(30, 0)).chain_err(|| "failed to set low speed time"));

            {
                let fserr = 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.
                try!(transfer.write_function(|data| {
                    if let Some(ref mut h) = hasher {
                        h.input(data);
                    }
                    notify_handler(Notification::DownloadDataReceived(data));
                    match file.write_all(data) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *fserr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    }
                }).chain_err(|| "failed to set write"));

                // Listen for headers and parse out a `Content-Length` if it comes
                // so we know how much we're downloading.
                try!(transfer.header_function(|header| {
                    if let Ok(data) = str::from_utf8(header) {
                        let prefix = "Content-Length: ";
                        if data.starts_with(prefix) {
                            if let Ok(s) = data[prefix.len()..].trim().parse() {
                                let msg = Notification::DownloadContentLengthReceived(s);
                                notify_handler(msg);
                            }
                        }
                    }
                    true
                }).chain_err(|| "failed to set header"));

                // If an error happens check to see if we had a filesystem error up
                // in `fserr`, but we always want to punt it up.
                try!(transfer.perform().or_else(|e| {
                    match fserr.borrow_mut().take() {
                        Some(fs) => Err(fs).chain_err(|| ErrorKind::HttpError(e)),
                        None => Err(ErrorKind::HttpError(e).into())
                    }
                }));
            }

            // If we didn't get a 200 or 0 ("OK" for files) then return an error
            let code = try!(handle.response_code().chain_err(|| "failed to get response code"));
            if code != 200 && code != 0 {
                return Err(ErrorKind::HttpStatus(code).into());
            }

            Ok(())
        })
    }
}

/// Download via hyper; encrypt with the native (or OpenSSl) TLS
/// stack via native-tls
mod hyper {
    use hyper;
    use notifications::Notification;
    use sha2::{Digest, Sha256};
    use std::fs;
    use std::io;
    use std::path::Path;
    use std::time::Duration;
    use url::Url;
    use errors::*;

    fn proxy_from_env(url: &Url) -> Option<(String, u16)> {
        use std::env::var_os;

        let mut maybe_https_proxy = var_os("https_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_https_proxy.is_none() {
            maybe_https_proxy = var_os("HTTPS_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        let maybe_http_proxy = var_os("http_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        let mut maybe_all_proxy = var_os("all_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_all_proxy.is_none() {
            maybe_all_proxy = var_os("ALL_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        if let Some(url_value) = match url.scheme() {
                                     "https" => maybe_https_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
                                     "http" => maybe_http_proxy.or(maybe_all_proxy),
                                     _ => maybe_all_proxy,
                                 } {
            if let Ok(proxy_url) = Url::parse(&url_value) {
                if let Some(host) = proxy_url.host_str() {
                    let port = proxy_url.port().unwrap_or(8080);
                    return Some((host.to_string(), port));
                }
            }
        }
        None
    }

    pub fn download_file(url: &Url,
                         path: &Path,
                         mut hasher: Option<&mut Sha256>,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {

        // Short-circuit hyper for the "file:" URL scheme
        if try!(download_from_file_url(url, path, &mut hasher)) {
            return Ok(());
        }

        use hyper::client::{Client, ProxyConfig};
        use hyper::error::Result as HyperResult;
        use hyper::header::ContentLength;
        use hyper::net::{SslClient, NetworkStream, HttpsConnector};
        use native_tls;
        use std::io::Result as IoResult;
        use std::io::{Read, Write};
        use std::net::{SocketAddr, Shutdown};
        use std::sync::{Arc, Mutex};

        // The Hyper HTTP client
        let client;

        let maybe_proxy = proxy_from_env(url);
        if url.scheme() == "https" {

            // All the following is adapter code to use native_tls with hyper.

            struct NativeSslClient;

            impl<T: NetworkStream + Send + Clone> SslClient<T> for NativeSslClient {
                type Stream = NativeSslStream<T>;

                fn wrap_client(&self, stream: T, host: &str) -> HyperResult<Self::Stream> {
                    use native_tls::ClientBuilder as TlsClientBuilder;
                    use hyper::error::Error as HyperError;

                    let mut ssl_builder = try!(TlsClientBuilder::new()
                                               .map_err(|e| HyperError::Ssl(Box::new(e))));
                    let ssl_stream = try!(ssl_builder.handshake(host, stream)
                                          .map_err(|e| HyperError::Ssl(Box::new(e))));

                    Ok(NativeSslStream(Arc::new(Mutex::new(ssl_stream))))
                }
            }

            #[derive(Clone)]
            struct NativeSslStream<T>(Arc<Mutex<native_tls::TlsStream<T>>>);

            #[derive(Debug)]
            struct NativeSslPoisonError;

            impl ::std::error::Error for NativeSslPoisonError {
                fn description(&self) -> &str { "mutex poisoned during TLS operation" }
            }

            impl ::std::fmt::Display for NativeSslPoisonError {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    f.write_str(::std::error::Error::description(self))
                }
            }

            impl<T> NetworkStream for NativeSslStream<T>
                where T: NetworkStream
            {
                fn peer_addr(&mut self) -> IoResult<SocketAddr> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().peer_addr())
                }
                fn set_read_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_read_timeout(dur))
                }
                fn set_write_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_write_timeout(dur))
                }
                fn close(&mut self, how: Shutdown) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().close(how))
                }
            }

            impl<T> Read for NativeSslStream<T>
                where T: Read + Write
            {
                fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.read(buf))
                }
            }

            impl<T> Write for NativeSslStream<T>
                where T: Read + Write
            {
                fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.write(buf))
                }
                fn flush(&mut self) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.flush())
                }
            }

            maybe_init_certs();

            if maybe_proxy.is_none() {
                // Connect with hyper + native_tls
                client = Client::with_connector(HttpsConnector::new(NativeSslClient));
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_proxy_config(ProxyConfig(proxy_host_port.0, proxy_host_port.1, NativeSslClient));
            }
        } else if url.scheme() == "http" {
            if maybe_proxy.is_none() {
                client = Client::new();
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_http_proxy(proxy_host_port.0, proxy_host_port.1);
            }
        } else {
            return Err(format!("unsupported URL scheme: '{}'", url.scheme()).into());
        }

        let mut res = try!(client.get(url.clone()).send()
                           .chain_err(|| "failed to make network request"));
        if res.status != hyper::Ok {
            return Err(ErrorKind::HttpStatus(res.status.to_u16() as u32).into());
        }

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

        let mut file = try!(fs::File::create(path).chain_err(
            || "error creating file for download"));

        if let Some(len) = res.headers.get::<ContentLength>().cloned() {
            notify_handler(Notification::DownloadContentLengthReceived(len.0));
        }

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

            if bytes_read != 0 {
                if let Some(ref mut h) = hasher {
                    h.input(&buffer[0..bytes_read]);
                }
                try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read])
                     .chain_err(|| "unable to write download to disk"));
                notify_handler(Notification::DownloadDataReceived(&buffer[0..bytes_read]));
            } else {
                try!(file.sync_data().chain_err(|| "unable to sync download to disk"));
                return Ok(());
            }
        }
    }

    // Tell our statically-linked OpenSSL where to find root certs
    // cc https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/lib.rs#L1267
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    fn maybe_init_certs() {
        use std::sync::{Once, ONCE_INIT};
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| {
            ::openssl_sys::probe::init_ssl_cert_env_vars();
        });
    }

    #[cfg(any(target_os = "windows", target_os = "macos"))]
    fn maybe_init_certs() { }

    fn download_from_file_url(url: &Url,
                              path: &Path,
                              hasher: &mut Option<&mut Sha256>)
                              -> Result<bool> {
        use raw::is_file;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = try!(url.to_file_path()
                           .map_err(|_| Error::from(format!("bogus file url: '{}'", url))));
            if !is_file(&src) {
                // Because some of multirust'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::HttpStatus(hyper::status::StatusCode::NotFound.to_u16() as u32).into());
            }
            try!(fs::copy(&src, path).chain_err(|| "failure copying file"));

            if let Some(ref mut h) = *hasher {
                let ref mut f = try!(fs::File::open(path)
                                     .chain_err(|| "unable to open downloaded file"));

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

            Ok(true)
        } else {
            Ok(false)
        }
    }
}
<commit_msg>Pull hashing out of the HTTP impl<commit_after>//! Easy file downloading

use errors::*;
use notifications::Notification;
use sha2::{Sha256, Digest};
use std::cell::RefCell;
use std::env;
use std::path::Path;
use url::Url;

pub fn download_file(url: &Url,
                     path: &Path,
                     hasher: Option<&mut Sha256>,
                     notify_handler: &Fn(Notification))
                     -> Result<()> {

    let hasher = RefCell::new(hasher);
    if env::var_os("RUSTUP_USE_HYPER").is_some() {
        notify_handler(Notification::UsingHyper);
        try!(self::hyper::download_file(url, path, &|notice| {
            match notice {
                Notification::DownloadDataReceived(data) => {
                    if let Some(ref mut h) = *hasher.borrow_mut() {
                        h.input(data);
                    }
                }
                _ => ()
            }
            notify_handler(notice);
        }));
    } else {
        notify_handler(Notification::UsingCurl);
        try!(self::curl::download_file(url, path, &|notice| {
            match notice {
                Notification::DownloadDataReceived(data) => {
                    if let Some(ref mut h) = *hasher.borrow_mut() {
                        h.input(data);
                    }
                }
                _ => ()
            }
            notify_handler(notice);
        }));
    }

    notify_handler(Notification::DownloadFinished);

    Ok(())
}

/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
/// stack via libcurl
mod curl {
    use curl::easy::Easy;
    use errors::*;
    use notifications::Notification;
    use std::cell::RefCell;
    use std::fs;
    use std::path::Path;
    use std::str;
    use std::time::Duration;
    use url::Url;

    pub fn download_file(url: &Url,
                         path: &Path,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {
        use notifications::Notification;
        use std::io::Write;

        let mut file = try!(fs::File::create(&path).chain_err(
            || "error creating file for download"));

        // 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();

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

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

            // Fail if less than 10 bytes are transferred every 30 seconds
            try!(handle.low_speed_limit(10).chain_err(|| "failed to set low speed limit"));
            try!(handle.low_speed_time(Duration::new(30, 0)).chain_err(|| "failed to set low speed time"));

            {
                let fserr = 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.
                try!(transfer.write_function(|data| {
                    notify_handler(Notification::DownloadDataReceived(data));
                    match file.write_all(data) {
                        Ok(()) => Ok(data.len()),
                        Err(e) => {
                            *fserr.borrow_mut() = Some(e);
                            Ok(0)
                        }
                    }
                }).chain_err(|| "failed to set write"));

                // Listen for headers and parse out a `Content-Length` if it comes
                // so we know how much we're downloading.
                try!(transfer.header_function(|header| {
                    if let Ok(data) = str::from_utf8(header) {
                        let prefix = "Content-Length: ";
                        if data.starts_with(prefix) {
                            if let Ok(s) = data[prefix.len()..].trim().parse() {
                                let msg = Notification::DownloadContentLengthReceived(s);
                                notify_handler(msg);
                            }
                        }
                    }
                    true
                }).chain_err(|| "failed to set header"));

                // If an error happens check to see if we had a filesystem error up
                // in `fserr`, but we always want to punt it up.
                try!(transfer.perform().or_else(|e| {
                    match fserr.borrow_mut().take() {
                        Some(fs) => Err(fs).chain_err(|| ErrorKind::HttpError(e)),
                        None => Err(ErrorKind::HttpError(e).into())
                    }
                }));
            }

            // If we didn't get a 200 or 0 ("OK" for files) then return an error
            let code = try!(handle.response_code().chain_err(|| "failed to get response code"));
            if code != 200 && code != 0 {
                return Err(ErrorKind::HttpStatus(code).into());
            }

            Ok(())
        })
    }
}

/// Download via hyper; encrypt with the native (or OpenSSl) TLS
/// stack via native-tls
mod hyper {
    use hyper;
    use notifications::Notification;
    use std::fs;
    use std::io;
    use std::path::Path;
    use std::time::Duration;
    use url::Url;
    use errors::*;

    fn proxy_from_env(url: &Url) -> Option<(String, u16)> {
        use std::env::var_os;

        let mut maybe_https_proxy = var_os("https_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_https_proxy.is_none() {
            maybe_https_proxy = var_os("HTTPS_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        let maybe_http_proxy = var_os("http_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        let mut maybe_all_proxy = var_os("all_proxy").map(|ref v| v.to_str().unwrap_or("").to_string());
        if maybe_all_proxy.is_none() {
            maybe_all_proxy = var_os("ALL_PROXY").map(|ref v| v.to_str().unwrap_or("").to_string());
        }
        if let Some(url_value) = match url.scheme() {
                                     "https" => maybe_https_proxy.or(maybe_http_proxy.or(maybe_all_proxy)),
                                     "http" => maybe_http_proxy.or(maybe_all_proxy),
                                     _ => maybe_all_proxy,
                                 } {
            if let Ok(proxy_url) = Url::parse(&url_value) {
                if let Some(host) = proxy_url.host_str() {
                    let port = proxy_url.port().unwrap_or(8080);
                    return Some((host.to_string(), port));
                }
            }
        }
        None
    }

    pub fn download_file(url: &Url,
                         path: &Path,
                         notify_handler: &Fn(Notification))
                         -> Result<()> {

        // Short-circuit hyper for the "file:" URL scheme
        if try!(download_from_file_url(url, path, notify_handler)) {
            return Ok(());
        }

        use hyper::client::{Client, ProxyConfig};
        use hyper::error::Result as HyperResult;
        use hyper::header::ContentLength;
        use hyper::net::{SslClient, NetworkStream, HttpsConnector};
        use native_tls;
        use std::io::Result as IoResult;
        use std::io::{Read, Write};
        use std::net::{SocketAddr, Shutdown};
        use std::sync::{Arc, Mutex};

        // The Hyper HTTP client
        let client;

        let maybe_proxy = proxy_from_env(url);
        if url.scheme() == "https" {

            // All the following is adapter code to use native_tls with hyper.

            struct NativeSslClient;

            impl<T: NetworkStream + Send + Clone> SslClient<T> for NativeSslClient {
                type Stream = NativeSslStream<T>;

                fn wrap_client(&self, stream: T, host: &str) -> HyperResult<Self::Stream> {
                    use native_tls::ClientBuilder as TlsClientBuilder;
                    use hyper::error::Error as HyperError;

                    let mut ssl_builder = try!(TlsClientBuilder::new()
                                               .map_err(|e| HyperError::Ssl(Box::new(e))));
                    let ssl_stream = try!(ssl_builder.handshake(host, stream)
                                          .map_err(|e| HyperError::Ssl(Box::new(e))));

                    Ok(NativeSslStream(Arc::new(Mutex::new(ssl_stream))))
                }
            }

            #[derive(Clone)]
            struct NativeSslStream<T>(Arc<Mutex<native_tls::TlsStream<T>>>);

            #[derive(Debug)]
            struct NativeSslPoisonError;

            impl ::std::error::Error for NativeSslPoisonError {
                fn description(&self) -> &str { "mutex poisoned during TLS operation" }
            }

            impl ::std::fmt::Display for NativeSslPoisonError {
                fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::result::Result<(), ::std::fmt::Error> {
                    f.write_str(::std::error::Error::description(self))
                }
            }

            impl<T> NetworkStream for NativeSslStream<T>
                where T: NetworkStream
            {
                fn peer_addr(&mut self) -> IoResult<SocketAddr> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().peer_addr())
                }
                fn set_read_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_read_timeout(dur))
                }
                fn set_write_timeout(&self, dur: Option<Duration>) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|t| t.get_ref().set_write_timeout(dur))
                }
                fn close(&mut self, how: Shutdown) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.get_mut().close(how))
                }
            }

            impl<T> Read for NativeSslStream<T>
                where T: Read + Write
            {
                fn read(&mut self, buf: &mut [u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.read(buf))
                }
            }

            impl<T> Write for NativeSslStream<T>
                where T: Read + Write
            {
                fn write(&mut self, buf: &[u8]) -> IoResult<usize> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.write(buf))
                }
                fn flush(&mut self) -> IoResult<()> {
                    self.0.lock()
                        .map_err(|_| io::Error::new(io::ErrorKind::Other, NativeSslPoisonError))
                        .and_then(|mut t| t.flush())
                }
            }

            maybe_init_certs();

            if maybe_proxy.is_none() {
                // Connect with hyper + native_tls
                client = Client::with_connector(HttpsConnector::new(NativeSslClient));
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_proxy_config(ProxyConfig(proxy_host_port.0, proxy_host_port.1, NativeSslClient));
            }
        } else if url.scheme() == "http" {
            if maybe_proxy.is_none() {
                client = Client::new();
            } else {
                let proxy_host_port = maybe_proxy.unwrap();
                client = Client::with_http_proxy(proxy_host_port.0, proxy_host_port.1);
            }
        } else {
            return Err(format!("unsupported URL scheme: '{}'", url.scheme()).into());
        }

        let mut res = try!(client.get(url.clone()).send()
                           .chain_err(|| "failed to make network request"));
        if res.status != hyper::Ok {
            return Err(ErrorKind::HttpStatus(res.status.to_u16() as u32).into());
        }

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

        let mut file = try!(fs::File::create(path).chain_err(
            || "error creating file for download"));

        if let Some(len) = res.headers.get::<ContentLength>().cloned() {
            notify_handler(Notification::DownloadContentLengthReceived(len.0));
        }

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

            if bytes_read != 0 {
                try!(io::Write::write_all(&mut file, &mut buffer[0..bytes_read])
                     .chain_err(|| "unable to write download to disk"));
                notify_handler(Notification::DownloadDataReceived(&buffer[0..bytes_read]));
            } else {
                try!(file.sync_data().chain_err(|| "unable to sync download to disk"));
                return Ok(());
            }
        }
    }

    // Tell our statically-linked OpenSSL where to find root certs
    // cc https://github.com/alexcrichton/git2-rs/blob/master/libgit2-sys/lib.rs#L1267
    #[cfg(not(any(target_os = "windows", target_os = "macos")))]
    fn maybe_init_certs() {
        use std::sync::{Once, ONCE_INIT};
        static INIT: Once = ONCE_INIT;
        INIT.call_once(|| {
            ::openssl_sys::probe::init_ssl_cert_env_vars();
        });
    }

    #[cfg(any(target_os = "windows", target_os = "macos"))]
    fn maybe_init_certs() { }

    fn download_from_file_url(url: &Url,
                              path: &Path,
                              notify_handler: &Fn(Notification))
                              -> Result<bool> {
        use raw::is_file;

        // The file scheme is mostly for use by tests to mock the dist server
        if url.scheme() == "file" {
            let src = try!(url.to_file_path()
                           .map_err(|_| Error::from(format!("bogus file url: '{}'", url))));
            if !is_file(&src) {
                // Because some of multirust'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::HttpStatus(hyper::status::StatusCode::NotFound.to_u16() as u32).into());
            }
            try!(fs::copy(&src, path).chain_err(|| "failure copying file"));

            {
                let ref mut f = try!(fs::File::open(path)
                                     .chain_err(|| "unable to open downloaded file"));

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

            Ok(true)
        } else {
            Ok(false)
        }
    }
}
<|endoftext|>


<commit_before>use std::collections::HashMap;

use itertools::Itertools;

use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
use ra_ide_db::RootDatabase;
use ra_syntax::{
    algo, ast,
    ast::{Name, Path, RecordLit, RecordPat},
    AstNode, SyntaxKind, SyntaxNode,
};

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};
use ra_syntax::ast::{Expr, NameRef};

pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
    reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
}

fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
    let record = ctx.find_node_at_offset::<R>()?;
    let path = record.syntax().children().find_map(Path::cast)?;

    let ranks = compute_fields_ranks(&path, &ctx)?;

    let fields = get_fields(&record.syntax());
    let sorted_fields = sorted_by_rank(&fields, |node| {
        *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())
    });

    if sorted_fields == fields {
        return None;
    }

    ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| {
        for (old, new) in fields.iter().zip(&sorted_fields) {
            algo::diff(old, new).into_text_edit(edit.text_edit_builder());
        }
        edit.target(record.syntax().text_range())
    })
}

fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
    use SyntaxKind::*;
    match node.kind() {
        RECORD_LIT => vec![RECORD_FIELD],
        RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
        _ => vec![],
    }
}

fn get_field_name(node: &SyntaxNode) -> String {
    use SyntaxKind::*;
    match node.kind() {
        RECORD_FIELD => {
            if let Some(name) = node.children().find_map(NameRef::cast) {
                return name.to_string();
            }
            node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default()
        }
        BIND_PAT | RECORD_FIELD_PAT => {
            node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
        }
        _ => String::new(),
    }
}

fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
    let kinds = get_fields_kind(record);
    record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect()
}

fn sorted_by_rank(
    fields: &[SyntaxNode],
    get_rank: impl Fn(&SyntaxNode) -> usize,
) -> Vec<SyntaxNode> {
    fields.iter().cloned().sorted_by_key(get_rank).collect()
}

fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<Struct> {
    match sema.resolve_path(path) {
        Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s),
        _ => None,
    }
}

fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> {
    Some(
        struct_definition(path, ctx.sema)?
            .fields(ctx.db)
            .iter()
            .enumerate()
            .map(|(idx, field)| (field.name(ctx.db).to_string(), idx))
            .collect(),
    )
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn not_applicable_if_sorted() {
        check_assist_not_applicable(
            reorder_fields,
            r#"
        struct Foo {
            foo: i32,
            bar: i32,
        }

        const test: Foo = <|>Foo { foo: 0, bar: 0 };
        "#,
        )
    }

    #[test]
    fn trivial_empty_fields() {
        check_assist_not_applicable(
            reorder_fields,
            r#"
        struct Foo {};
        const test: Foo = <|>Foo {}
        "#,
        )
    }

    #[test]
    fn reorder_struct_fields() {
        check_assist(
            reorder_fields,
            r#"
        struct Foo {foo: i32, bar: i32};
        const test: Foo = <|>Foo {bar: 0, foo: 1}
        "#,
            r#"
        struct Foo {foo: i32, bar: i32};
        const test: Foo = <|>Foo {foo: 1, bar: 0}
        "#,
        )
    }

    #[test]
    fn reorder_struct_pattern() {
        check_assist(
            reorder_fields,
            r#"
        struct Foo { foo: i64, bar: i64, baz: i64 }

        fn f(f: Foo) -> {
            match f {
                <|>Foo { baz: 0, ref mut bar, .. } => (),
                _ => ()
            }
        }
        "#,
            r#"
        struct Foo { foo: i64, bar: i64, baz: i64 }

        fn f(f: Foo) -> {
            match f {
                <|>Foo { ref mut bar, baz: 0, .. } => (),
                _ => ()
            }
        }
        "#,
        )
    }

    #[test]
    fn reorder_with_extra_field() {
        check_assist(
            reorder_fields,
            r#"
            struct Foo {
                foo: String,
                bar: String,
            }

            impl Foo {
                fn new() -> Foo {
                    let foo = String::new();
                    <|>Foo {
                        bar: foo.clone(),
                        extra: "Extra field",
                        foo,
                    }
                }
            }
            "#,
            r#"
            struct Foo {
                foo: String,
                bar: String,
            }

            impl Foo {
                fn new() -> Foo {
                    let foo = String::new();
                    <|>Foo {
                        foo,
                        bar: foo.clone(),
                        extra: "Extra field",
                    }
                }
            }
            "#,
        )
    }
}
<commit_msg>Add documentation comment<commit_after>use std::collections::HashMap;

use itertools::Itertools;

use hir::{Adt, ModuleDef, PathResolution, Semantics, Struct};
use ra_ide_db::RootDatabase;
use ra_syntax::{
    algo, ast,
    ast::{Name, Path, RecordLit, RecordPat},
    AstNode, SyntaxKind, SyntaxNode,
};

use crate::{
    assist_ctx::{Assist, AssistCtx},
    AssistId,
};
use ra_syntax::ast::{Expr, NameRef};

// Assist: reorder_fields
//
// Reorder the fields of record literals and record patterns in the same order as in
// the definition.
//
// ```
// struct Foo {foo: i32, bar: i32};
// const test: Foo = <|>Foo {bar: 0, foo: 1}
// ```
// ->
// ```
// struct Foo {foo: i32, bar: i32};
// const test: Foo = <|>Foo {foo: 1, bar: 0}
// ```
//
pub(crate) fn reorder_fields(ctx: AssistCtx) -> Option<Assist> {
    reorder::<RecordLit>(ctx.clone()).or_else(|| reorder::<RecordPat>(ctx))
}

fn reorder<R: AstNode>(ctx: AssistCtx) -> Option<Assist> {
    let record = ctx.find_node_at_offset::<R>()?;
    let path = record.syntax().children().find_map(Path::cast)?;

    let ranks = compute_fields_ranks(&path, &ctx)?;

    let fields = get_fields(&record.syntax());
    let sorted_fields = sorted_by_rank(&fields, |node| {
        *ranks.get(&get_field_name(node)).unwrap_or(&usize::max_value())
    });

    if sorted_fields == fields {
        return None;
    }

    ctx.add_assist(AssistId("reorder_fields"), "Reorder record fields", |edit| {
        for (old, new) in fields.iter().zip(&sorted_fields) {
            algo::diff(old, new).into_text_edit(edit.text_edit_builder());
        }
        edit.target(record.syntax().text_range())
    })
}

fn get_fields_kind(node: &SyntaxNode) -> Vec<SyntaxKind> {
    use SyntaxKind::*;
    match node.kind() {
        RECORD_LIT => vec![RECORD_FIELD],
        RECORD_PAT => vec![RECORD_FIELD_PAT, BIND_PAT],
        _ => vec![],
    }
}

fn get_field_name(node: &SyntaxNode) -> String {
    use SyntaxKind::*;
    match node.kind() {
        RECORD_FIELD => {
            if let Some(name) = node.children().find_map(NameRef::cast) {
                return name.to_string();
            }
            node.children().find_map(Expr::cast).map(|expr| expr.to_string()).unwrap_or_default()
        }
        BIND_PAT | RECORD_FIELD_PAT => {
            node.children().find_map(Name::cast).map(|n| n.to_string()).unwrap_or_default()
        }
        _ => String::new(),
    }
}

fn get_fields(record: &SyntaxNode) -> Vec<SyntaxNode> {
    let kinds = get_fields_kind(record);
    record.children().flat_map(|n| n.children()).filter(|n| kinds.contains(&n.kind())).collect()
}

fn sorted_by_rank(
    fields: &[SyntaxNode],
    get_rank: impl Fn(&SyntaxNode) -> usize,
) -> Vec<SyntaxNode> {
    fields.iter().cloned().sorted_by_key(get_rank).collect()
}

fn struct_definition(path: &ast::Path, sema: &Semantics<RootDatabase>) -> Option<Struct> {
    match sema.resolve_path(path) {
        Some(PathResolution::Def(ModuleDef::Adt(Adt::Struct(s)))) => Some(s),
        _ => None,
    }
}

fn compute_fields_ranks(path: &Path, ctx: &AssistCtx) -> Option<HashMap<String, usize>> {
    Some(
        struct_definition(path, ctx.sema)?
            .fields(ctx.db)
            .iter()
            .enumerate()
            .map(|(idx, field)| (field.name(ctx.db).to_string(), idx))
            .collect(),
    )
}

#[cfg(test)]
mod tests {
    use crate::helpers::{check_assist, check_assist_not_applicable};

    use super::*;

    #[test]
    fn not_applicable_if_sorted() {
        check_assist_not_applicable(
            reorder_fields,
            r#"
        struct Foo {
            foo: i32,
            bar: i32,
        }

        const test: Foo = <|>Foo { foo: 0, bar: 0 };
        "#,
        )
    }

    #[test]
    fn trivial_empty_fields() {
        check_assist_not_applicable(
            reorder_fields,
            r#"
        struct Foo {};
        const test: Foo = <|>Foo {}
        "#,
        )
    }

    #[test]
    fn reorder_struct_fields() {
        check_assist(
            reorder_fields,
            r#"
        struct Foo {foo: i32, bar: i32};
        const test: Foo = <|>Foo {bar: 0, foo: 1}
        "#,
            r#"
        struct Foo {foo: i32, bar: i32};
        const test: Foo = <|>Foo {foo: 1, bar: 0}
        "#,
        )
    }

    #[test]
    fn reorder_struct_pattern() {
        check_assist(
            reorder_fields,
            r#"
        struct Foo { foo: i64, bar: i64, baz: i64 }

        fn f(f: Foo) -> {
            match f {
                <|>Foo { baz: 0, ref mut bar, .. } => (),
                _ => ()
            }
        }
        "#,
            r#"
        struct Foo { foo: i64, bar: i64, baz: i64 }

        fn f(f: Foo) -> {
            match f {
                <|>Foo { ref mut bar, baz: 0, .. } => (),
                _ => ()
            }
        }
        "#,
        )
    }

    #[test]
    fn reorder_with_extra_field() {
        check_assist(
            reorder_fields,
            r#"
            struct Foo {
                foo: String,
                bar: String,
            }

            impl Foo {
                fn new() -> Foo {
                    let foo = String::new();
                    <|>Foo {
                        bar: foo.clone(),
                        extra: "Extra field",
                        foo,
                    }
                }
            }
            "#,
            r#"
            struct Foo {
                foo: String,
                bar: String,
            }

            impl Foo {
                fn new() -> Foo {
                    let foo = String::new();
                    <|>Foo {
                        foo,
                        bar: foo.clone(),
                        extra: "Extra field",
                    }
                }
            }
            "#,
        )
    }
}
<|endoftext|>


<commit_before>use super::*;

#[derive(Default, Debug)]
pub struct BLinkMaterializer {
    pub(super) roots: Mutex<Vec<(PageID, PageID)>>,
}

impl Materializer for BLinkMaterializer {
    type PageFrag = Frag;

    // a vector of (root, prev root) for deterministic recovery
    type Recovery = Vec<(PageID, PageID)>;

    fn new(last_roots: &Option<Self::Recovery>) -> Self {
        let roots: Vec<(PageID, PageID)> =
            last_roots.clone().unwrap_or_else(|| vec![]);

        BLinkMaterializer {
            roots: Mutex::new(roots),
        }
    }

    fn merge(&self, frags: &[&Frag]) -> Frag {
        let (mut base_node, is_root) = match frags[0].clone() {
            Frag::Base(base_node, is_root) => (base_node, is_root),
            _ => panic!("non-Base in first element of frags slice"),
        };

        for &frag in &frags[1..] {
            base_node.apply(frag);
        }

        Frag::Base(base_node, is_root)
    }

    fn recover(&self, frag: &Frag) -> Option<Vec<(PageID, PageID)>> {
        match *frag {
            Frag::Base(ref node, prev_root) => {
                if let Some(prev_root) = prev_root {
                    let mut roots = self.roots.lock().unwrap();
                    if roots.contains(&(node.id, prev_root)) {
                        None
                    } else {
                        roots.push((node.id, prev_root));
                        Some(roots.clone())
                    }
                } else {
                    None
                }
            }
            _ => None,
        }
    }
}
<commit_msg>Update materializer.rs<commit_after>use super::*;

#[derive(Default, Debug)]
pub struct BLinkMaterializer {
    pub(super) roots: Mutex<Vec<(PageID, PageID)>>,
}

impl Materializer for BLinkMaterializer {
    type PageFrag = Frag;

    // a vector of (root, prev root) for deterministic recovery
    type Recovery = Vec<(PageID, PageID)>;

    fn new(last_roots: &Option<Self::Recovery>) -> Self {
        let roots: Vec<(PageID, PageID)> =
            last_roots.clone().unwrap_or_else(|| vec![]);

        BLinkMaterializer {
            roots: Mutex::new(roots),
        }
    }

    fn merge(&self, frags: &[&Frag]) -> Frag {
        let (mut base_node, is_root) = match frags[0].clone() {
            Frag::Base(base_node, is_root) => (base_node, is_root),
            _ => panic!("non-Base in first element of frags slice"),
        };

        for &frag in &frags[1..] {
            base_node.apply(frag);
        }

        Frag::Base(base_node, is_root)
    }

    fn recover(&self, frag: &Frag) -> Option<Vec<(PageID, PageID)>> {
        match *frag {
            Frag::Base(ref node, prev_root) => {
                if let Some(prev_root) = prev_root {
                    let mut roots = self.roots.lock().unwrap();
                    if !roots.contains(&(node.id, prev_root)) {
                        roots.push((node.id, prev_root));
                        return Some(roots.clone());
                    }
                }
            }
            _ => (),
        }
        None
    }
}
<|endoftext|>


<commit_before>use ide_db::{
    imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
};
use itertools::Itertools;
use stdx::format_to;
use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};

use crate::{
    utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
    AssistContext, AssistId, AssistKind, Assists,
};

// Assist: generate_new
//
// Adds a new inherent impl for a type.
//
// ```
// struct Ctx<T: Clone> {
//      data: T,$0
// }
// ```
// ->
// ```
// struct Ctx<T: Clone> {
//      data: T,
// }
//
// impl<T: Clone> Ctx<T> {
//     fn $0new(data: T) -> Self { Self { data } }
// }
// ```
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let strukt = ctx.find_node_at_offset::<ast::Struct>()?;

    // We want to only apply this to non-union structs with named fields
    let field_list = match strukt.kind() {
        StructKind::Record(named) => named,
        _ => return None,
    };

    // Return early if we've found an existing new fn
    let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;

    let current_module = ctx.sema.scope(strukt.syntax())?.module();

    let target = strukt.syntax().text_range();
    acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
        let mut buf = String::with_capacity(512);

        if impl_def.is_some() {
            buf.push('\n');
        }

        let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));

        let trivial_constructors = field_list
            .fields()
            .map(|f| {
                let ty = ctx.sema.resolve_type(&f.ty()?)?;

                let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));

                let type_path = current_module
                    .find_use_path(ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?)?;

                let expr = use_trivial_constructor(
                    &ctx.sema.db,
                    ide_db::helpers::mod_path_to_ast(&type_path),
                    &ty,
                )?;

                Some(format!("{}: {}", f.name()?.syntax(), expr))
            })
            .collect::<Vec<_>>();

        let params = field_list
            .fields()
            .enumerate()
            .filter_map(|(i, f)| {
                if trivial_constructors[i].is_none() {
                    Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))
                } else {
                    None
                }
            })
            .format(", ");

        let fields = field_list
            .fields()
            .enumerate()
            .filter_map(|(i, f)| {
                let contructor = trivial_constructors[i].clone();
                if contructor.is_some() {
                    contructor
                } else {
                    Some(f.name()?.to_string())
                }
            })
            .format(", ");

        format_to!(buf, "    {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);

        let start_offset = impl_def
            .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
            .unwrap_or_else(|| {
                buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
                strukt.syntax().text_range().end()
            });

        match ctx.config.snippet_cap {
            None => builder.insert(start_offset, buf),
            Some(cap) => {
                buf = buf.replace("fn new", "fn $0new");
                builder.insert_snippet(cap, start_offset, buf);
            }
        }
    })
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_generate_new_with_zst_fields() {
        check_assist(
            generate_new,
            r#"
struct Empty;

struct Foo { empty: Empty $0}
"#,
            r#"
struct Empty;

struct Foo { empty: Empty }

impl Foo {
    fn $0new() -> Self { Self { empty: Empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Empty;

struct Foo { baz: String, empty: Empty $0}
"#,
            r#"
struct Empty;

struct Foo { baz: String, empty: Empty }

impl Foo {
    fn $0new(baz: String) -> Self { Self { baz, empty: Empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
enum Empty { Bar }

struct Foo { empty: Empty $0}
"#,
            r#"
enum Empty { Bar }

struct Foo { empty: Empty }

impl Foo {
    fn $0new() -> Self { Self { empty: Empty::Bar } }
}
"#,
        );

        // make sure the assist only works on unit variants
        check_assist(
            generate_new,
            r#"
struct Empty {}

struct Foo { empty: Empty $0}
"#,
            r#"
struct Empty {}

struct Foo { empty: Empty }

impl Foo {
    fn $0new(empty: Empty) -> Self { Self { empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
enum Empty { Bar {} }

struct Foo { empty: Empty $0}
"#,
            r#"
enum Empty { Bar {} }

struct Foo { empty: Empty }

impl Foo {
    fn $0new(empty: Empty) -> Self { Self { empty } }
}
"#,
        );
    }

    #[test]
    fn test_generate_new() {
        check_assist(
            generate_new,
            r#"
struct Foo {$0}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo<T: Clone> {$0}
"#,
            r#"
struct Foo<T: Clone> {}

impl<T: Clone> Foo<T> {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo<'a, T: Foo<'a>> {$0}
"#,
            r#"
struct Foo<'a, T: Foo<'a>> {}

impl<'a, T: Foo<'a>> Foo<'a, T> {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo { baz: String $0}
"#,
            r#"
struct Foo { baz: String }

impl Foo {
    fn $0new(baz: String) -> Self { Self { baz } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo { baz: String, qux: Vec<i32> $0}
"#,
            r#"
struct Foo { baz: String, qux: Vec<i32> }

impl Foo {
    fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
"#,
        );
    }

    #[test]
    fn check_that_visibility_modifiers_dont_get_brought_in() {
        check_assist(
            generate_new,
            r#"
struct Foo { pub baz: String, pub qux: Vec<i32> $0}
"#,
            r#"
struct Foo { pub baz: String, pub qux: Vec<i32> }

impl Foo {
    fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
"#,
        );
    }

    #[test]
    fn check_it_reuses_existing_impls() {
        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn qux(&self) {}
}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }

    fn qux(&self) {}
}
"#,
        );

        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn qux(&self) {}
    fn baz() -> i32 {
        5
    }
}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }

    fn qux(&self) {}
    fn baz() -> i32 {
        5
    }
}
"#,
        );
    }

    #[test]
    fn check_visibility_of_new_fn_based_on_struct() {
        check_assist(
            generate_new,
            r#"
pub struct Foo {$0}
"#,
            r#"
pub struct Foo {}

impl Foo {
    pub fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
pub(crate) struct Foo {$0}
"#,
            r#"
pub(crate) struct Foo {}

impl Foo {
    pub(crate) fn $0new() -> Self { Self {  } }
}
"#,
        );
    }

    #[test]
    fn generate_new_not_applicable_if_fn_exists() {
        check_assist_not_applicable(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn new() -> Self {
        Self
    }
}
"#,
        );

        check_assist_not_applicable(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn New() -> Self {
        Self
    }
}
"#,
        );
    }

    #[test]
    fn generate_new_target() {
        check_assist_target(
            generate_new,
            r#"
struct SomeThingIrrelevant;
/// Has a lifetime parameter
struct Foo<'a, T: Foo<'a>> {$0}
struct EvenMoreIrrelevant;
"#,
            "/// Has a lifetime parameter
struct Foo<'a, T: Foo<'a>> {}",
        );
    }

    #[test]
    fn test_unrelated_new() {
        check_assist(
            generate_new,
            r#"
pub struct AstId<N: AstNode> {
    file_id: HirFileId,
    file_ast_id: FileAstId<N>,
}

impl<N: AstNode> AstId<N> {
    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
        AstId { file_id, file_ast_id }
    }
}

pub struct Source<T> {
    pub file_id: HirFileId,$0
    pub ast: T,
}

impl<T> Source<T> {
    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
        Source { file_id: self.file_id, ast: f(self.ast) }
    }
}
"#,
            r#"
pub struct AstId<N: AstNode> {
    file_id: HirFileId,
    file_ast_id: FileAstId<N>,
}

impl<N: AstNode> AstId<N> {
    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
        AstId { file_id, file_ast_id }
    }
}

pub struct Source<T> {
    pub file_id: HirFileId,
    pub ast: T,
}

impl<T> Source<T> {
    pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }

    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
        Source { file_id: self.file_id, ast: f(self.ast) }
    }
}
"#,
        );
    }
}
<commit_msg>Auto merge of #12854 - RalfJung:generate_new, r=Veykril<commit_after>use ide_db::{
    imports::import_assets::item_for_path_search, use_trivial_contructor::use_trivial_constructor,
};
use itertools::Itertools;
use stdx::format_to;
use syntax::ast::{self, AstNode, HasName, HasVisibility, StructKind};

use crate::{
    utils::{find_impl_block_start, find_struct_impl, generate_impl_text},
    AssistContext, AssistId, AssistKind, Assists,
};

// Assist: generate_new
//
// Adds a `fn new` for a type.
//
// ```
// struct Ctx<T: Clone> {
//      data: T,$0
// }
// ```
// ->
// ```
// struct Ctx<T: Clone> {
//      data: T,
// }
//
// impl<T: Clone> Ctx<T> {
//     fn $0new(data: T) -> Self { Self { data } }
// }
// ```
pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
    let strukt = ctx.find_node_at_offset::<ast::Struct>()?;

    // We want to only apply this to non-union structs with named fields
    let field_list = match strukt.kind() {
        StructKind::Record(named) => named,
        _ => return None,
    };

    // Return early if we've found an existing new fn
    let impl_def = find_struct_impl(ctx, &ast::Adt::Struct(strukt.clone()), "new")?;

    let current_module = ctx.sema.scope(strukt.syntax())?.module();

    let target = strukt.syntax().text_range();
    acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
        let mut buf = String::with_capacity(512);

        if impl_def.is_some() {
            buf.push('\n');
        }

        let vis = strukt.visibility().map_or(String::new(), |v| format!("{} ", v));

        let trivial_constructors = field_list
            .fields()
            .map(|f| {
                let ty = ctx.sema.resolve_type(&f.ty()?)?;

                let item_in_ns = hir::ItemInNs::from(hir::ModuleDef::from(ty.as_adt()?));

                let type_path = current_module
                    .find_use_path(ctx.sema.db, item_for_path_search(ctx.sema.db, item_in_ns)?)?;

                let expr = use_trivial_constructor(
                    &ctx.sema.db,
                    ide_db::helpers::mod_path_to_ast(&type_path),
                    &ty,
                )?;

                Some(format!("{}: {}", f.name()?.syntax(), expr))
            })
            .collect::<Vec<_>>();

        let params = field_list
            .fields()
            .enumerate()
            .filter_map(|(i, f)| {
                if trivial_constructors[i].is_none() {
                    Some(format!("{}: {}", f.name()?.syntax(), f.ty()?.syntax()))
                } else {
                    None
                }
            })
            .format(", ");

        let fields = field_list
            .fields()
            .enumerate()
            .filter_map(|(i, f)| {
                let contructor = trivial_constructors[i].clone();
                if contructor.is_some() {
                    contructor
                } else {
                    Some(f.name()?.to_string())
                }
            })
            .format(", ");

        format_to!(buf, "    {}fn new({}) -> Self {{ Self {{ {} }} }}", vis, params, fields);

        let start_offset = impl_def
            .and_then(|impl_def| find_impl_block_start(impl_def, &mut buf))
            .unwrap_or_else(|| {
                buf = generate_impl_text(&ast::Adt::Struct(strukt.clone()), &buf);
                strukt.syntax().text_range().end()
            });

        match ctx.config.snippet_cap {
            None => builder.insert(start_offset, buf),
            Some(cap) => {
                buf = buf.replace("fn new", "fn $0new");
                builder.insert_snippet(cap, start_offset, buf);
            }
        }
    })
}

#[cfg(test)]
mod tests {
    use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

    use super::*;

    #[test]
    fn test_generate_new_with_zst_fields() {
        check_assist(
            generate_new,
            r#"
struct Empty;

struct Foo { empty: Empty $0}
"#,
            r#"
struct Empty;

struct Foo { empty: Empty }

impl Foo {
    fn $0new() -> Self { Self { empty: Empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Empty;

struct Foo { baz: String, empty: Empty $0}
"#,
            r#"
struct Empty;

struct Foo { baz: String, empty: Empty }

impl Foo {
    fn $0new(baz: String) -> Self { Self { baz, empty: Empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
enum Empty { Bar }

struct Foo { empty: Empty $0}
"#,
            r#"
enum Empty { Bar }

struct Foo { empty: Empty }

impl Foo {
    fn $0new() -> Self { Self { empty: Empty::Bar } }
}
"#,
        );

        // make sure the assist only works on unit variants
        check_assist(
            generate_new,
            r#"
struct Empty {}

struct Foo { empty: Empty $0}
"#,
            r#"
struct Empty {}

struct Foo { empty: Empty }

impl Foo {
    fn $0new(empty: Empty) -> Self { Self { empty } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
enum Empty { Bar {} }

struct Foo { empty: Empty $0}
"#,
            r#"
enum Empty { Bar {} }

struct Foo { empty: Empty }

impl Foo {
    fn $0new(empty: Empty) -> Self { Self { empty } }
}
"#,
        );
    }

    #[test]
    fn test_generate_new() {
        check_assist(
            generate_new,
            r#"
struct Foo {$0}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo<T: Clone> {$0}
"#,
            r#"
struct Foo<T: Clone> {}

impl<T: Clone> Foo<T> {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo<'a, T: Foo<'a>> {$0}
"#,
            r#"
struct Foo<'a, T: Foo<'a>> {}

impl<'a, T: Foo<'a>> Foo<'a, T> {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo { baz: String $0}
"#,
            r#"
struct Foo { baz: String }

impl Foo {
    fn $0new(baz: String) -> Self { Self { baz } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo { baz: String, qux: Vec<i32> $0}
"#,
            r#"
struct Foo { baz: String, qux: Vec<i32> }

impl Foo {
    fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
"#,
        );
    }

    #[test]
    fn check_that_visibility_modifiers_dont_get_brought_in() {
        check_assist(
            generate_new,
            r#"
struct Foo { pub baz: String, pub qux: Vec<i32> $0}
"#,
            r#"
struct Foo { pub baz: String, pub qux: Vec<i32> }

impl Foo {
    fn $0new(baz: String, qux: Vec<i32>) -> Self { Self { baz, qux } }
}
"#,
        );
    }

    #[test]
    fn check_it_reuses_existing_impls() {
        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn qux(&self) {}
}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }

    fn qux(&self) {}
}
"#,
        );

        check_assist(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn qux(&self) {}
    fn baz() -> i32 {
        5
    }
}
"#,
            r#"
struct Foo {}

impl Foo {
    fn $0new() -> Self { Self {  } }

    fn qux(&self) {}
    fn baz() -> i32 {
        5
    }
}
"#,
        );
    }

    #[test]
    fn check_visibility_of_new_fn_based_on_struct() {
        check_assist(
            generate_new,
            r#"
pub struct Foo {$0}
"#,
            r#"
pub struct Foo {}

impl Foo {
    pub fn $0new() -> Self { Self {  } }
}
"#,
        );
        check_assist(
            generate_new,
            r#"
pub(crate) struct Foo {$0}
"#,
            r#"
pub(crate) struct Foo {}

impl Foo {
    pub(crate) fn $0new() -> Self { Self {  } }
}
"#,
        );
    }

    #[test]
    fn generate_new_not_applicable_if_fn_exists() {
        check_assist_not_applicable(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn new() -> Self {
        Self
    }
}
"#,
        );

        check_assist_not_applicable(
            generate_new,
            r#"
struct Foo {$0}

impl Foo {
    fn New() -> Self {
        Self
    }
}
"#,
        );
    }

    #[test]
    fn generate_new_target() {
        check_assist_target(
            generate_new,
            r#"
struct SomeThingIrrelevant;
/// Has a lifetime parameter
struct Foo<'a, T: Foo<'a>> {$0}
struct EvenMoreIrrelevant;
"#,
            "/// Has a lifetime parameter
struct Foo<'a, T: Foo<'a>> {}",
        );
    }

    #[test]
    fn test_unrelated_new() {
        check_assist(
            generate_new,
            r#"
pub struct AstId<N: AstNode> {
    file_id: HirFileId,
    file_ast_id: FileAstId<N>,
}

impl<N: AstNode> AstId<N> {
    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
        AstId { file_id, file_ast_id }
    }
}

pub struct Source<T> {
    pub file_id: HirFileId,$0
    pub ast: T,
}

impl<T> Source<T> {
    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
        Source { file_id: self.file_id, ast: f(self.ast) }
    }
}
"#,
            r#"
pub struct AstId<N: AstNode> {
    file_id: HirFileId,
    file_ast_id: FileAstId<N>,
}

impl<N: AstNode> AstId<N> {
    pub fn new(file_id: HirFileId, file_ast_id: FileAstId<N>) -> AstId<N> {
        AstId { file_id, file_ast_id }
    }
}

pub struct Source<T> {
    pub file_id: HirFileId,
    pub ast: T,
}

impl<T> Source<T> {
    pub fn $0new(file_id: HirFileId, ast: T) -> Self { Self { file_id, ast } }

    pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> Source<U> {
        Source { file_id: self.file_id, ast: f(self.ast) }
    }
}
"#,
        );
    }
}
<|endoftext|>


<commit_before>fn add(i: [int], m: [mut int], c: [const int]) {

    // Check that:
    //  (1) vectors of any two mutabilities can be added
    //  (2) result has mutability of lhs

   add(i + [3],
       m + [3],
       c + [3]);

   add(i + [mut 3],
       m + [mut 3],
       c + [mut 3]);

   add(i + i,
       m + i,
       c + i);

   add(i + m,
       m + m,
       c + m);

   add(i + c,
       m + c,
       c + c);

   add(m + [3], //! ERROR mismatched types
       m + [3],
       m + [3]);

   add(i + [3],
       i + [3], //! ERROR mismatched types
       i + [3]);

   add(c + [3], //! ERROR mismatched types
       c + [3], //! ERROR mismatched types
       c + [3]);

   add(m + [mut 3], //! ERROR mismatched types
       m + [mut 3],
       m + [mut 3]);

   add(i + [mut 3],
       i + [mut 3], //! ERROR mismatched types
       i + [mut 3]);

   add(c + [mut 3], //! ERROR mismatched types
       c + [mut 3], //! ERROR mismatched types
       c + [mut 3]);

   add(m + i, //! ERROR mismatched types
       m + i,
       m + i);

   add(i + i,
       i + i, //! ERROR mismatched types
       i + i);

   add(c + i, //! ERROR mismatched types
       c + i, //! ERROR mismatched types
       c + i);

   add(m + m, //! ERROR mismatched types
       m + m,
       m + m);

   add(i + m,
       i + m, //! ERROR mismatched types
       i + m);

   add(c + m, //! ERROR mismatched types
       c + m, //! ERROR mismatched types
       c + m);

   add(m + c, //! ERROR mismatched types
       m + c,
       m + c);

   add(i + c,
       i + c, //! ERROR mismatched types
       i + c);

   add(c + c, //! ERROR mismatched types
       c + c, //! ERROR mismatched types
       c + c);
}

fn main() {
}
<commit_msg>Updating errors in vec-add.rs<commit_after>fn add(i: [int], m: [mut int], c: [const int]) {

    // Check that:
    //  (1) vectors of any two mutabilities can be added
    //  (2) result has mutability of lhs

   add(i + [3],
       m + [3],
       [3]);

   add(i + [mut 3],
       m + [mut 3],
       [mut 3]);

   add(i + i,
       m + i,
       i);

   add(i + m,
       m + m,
       m);

   add(i + c,
       m + c,
       c);

   add(m + [3], //! ERROR mismatched types
       m + [3],
       m + [3]);

   add(i + [3],
       i + [3], //! ERROR mismatched types
       i + [3]);

   add(c + [3], //! ERROR mismatched types
                //!^ ERROR binary operation + cannot be applied
       c + [3], //! ERROR binary operation + cannot be applied
                //!^ mismatched types
       [3]);

   add(m + [mut 3], //! ERROR mismatched types
       m + [mut 3],
       m + [mut 3]);

   add(i + [mut 3],
       i + [mut 3], //! ERROR mismatched types
       i + [mut 3]);

   add(c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       c + [mut 3], //! ERROR binary operation + cannot be applied
                    //!^ mismatched types
       [mut 3]);

   add(m + i, //! ERROR mismatched types
       m + i,
       m + i);

   add(i + i,
       i + i, //! ERROR mismatched types
       i + i);

   add(c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + i, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       i);

   add(m + m, //! ERROR mismatched types
       m + m,
       m + m);

   add(i + m,
       i + m, //! ERROR mismatched types
       i + m);

   add(c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + m, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       m);

   add(m + c, //! ERROR mismatched types
       m + c,
       m + c);

   add(i + c,
       i + c, //! ERROR mismatched types
       i + c);

   add(c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c + c, //! ERROR binary operation + cannot be applied
              //!^ ERROR mismatched types
       c);
}

fn main() {
}
<|endoftext|>


<commit_before>extern crate idna;
extern crate unicode_normalization;

use unicode_normalization::char::is_combining_mark;

fn _to_ascii(domain: &str) -> Result<String, idna::Errors> {
    idna::Config::default()
        .verify_dns_length(true)
        .use_std3_ascii_rules(true)
        .to_ascii(domain)
}

#[test]
fn test_v5() {
    // IdnaTest:784 蔏｡𑰺
    assert!(is_combining_mark('\u{11C3A}'));
    assert!(_to_ascii("\u{11C3A}").is_err());
    assert!(_to_ascii("\u{850f}.\u{11C3A}").is_err());
    assert!(_to_ascii("\u{850f}\u{ff61}\u{11C3A}").is_err());
}

#[test]
fn test_v8_bidi_rules() {
    assert_eq!(_to_ascii("abc").unwrap(), "abc");
    assert_eq!(_to_ascii("123").unwrap(), "123");
    assert_eq!(_to_ascii("אבּג").unwrap(), "xn--kdb3bdf");
    assert_eq!(_to_ascii("ابج").unwrap(), "xn--mgbcm");
    assert_eq!(_to_ascii("abc.ابج").unwrap(), "abc.xn--mgbcm");
    assert_eq!(
        _to_ascii("אבּג.ابج").unwrap(),
        "xn--kdb3bdf.xn--mgbcm"
    );

    // Bidi domain names cannot start with digits
    assert!(_to_ascii("0a.\u{05D0}").is_err());
    assert!(_to_ascii("0à.\u{05D0}").is_err());

    // Bidi chars may be punycode-encoded
    assert!(_to_ascii("xn--0ca24w").is_err());
}
<commit_msg>[idna/tests/unit.rs] rustfmt (#615)<commit_after>extern crate idna;
extern crate unicode_normalization;

use unicode_normalization::char::is_combining_mark;

fn _to_ascii(domain: &str) -> Result<String, idna::Errors> {
    idna::Config::default()
        .verify_dns_length(true)
        .use_std3_ascii_rules(true)
        .to_ascii(domain)
}

#[test]
fn test_v5() {
    // IdnaTest:784 蔏｡𑰺
    assert!(is_combining_mark('\u{11C3A}'));
    assert!(_to_ascii("\u{11C3A}").is_err());
    assert!(_to_ascii("\u{850f}.\u{11C3A}").is_err());
    assert!(_to_ascii("\u{850f}\u{ff61}\u{11C3A}").is_err());
}

#[test]
fn test_v8_bidi_rules() {
    assert_eq!(_to_ascii("abc").unwrap(), "abc");
    assert_eq!(_to_ascii("123").unwrap(), "123");
    assert_eq!(_to_ascii("אבּג").unwrap(), "xn--kdb3bdf");
    assert_eq!(_to_ascii("ابج").unwrap(), "xn--mgbcm");
    assert_eq!(_to_ascii("abc.ابج").unwrap(), "abc.xn--mgbcm");
    assert_eq!(_to_ascii("אבּג.ابج").unwrap(), "xn--kdb3bdf.xn--mgbcm");

    // Bidi domain names cannot start with digits
    assert!(_to_ascii("0a.\u{05D0}").is_err());
    assert!(_to_ascii("0à.\u{05D0}").is_err());

    // Bidi chars may be punycode-encoded
    assert!(_to_ascii("xn--0ca24w").is_err());
}
<|endoftext|>


<commit_before>use nom::*;
use std::str;

#[derive(Debug, PartialEq)]
pub struct Paragraph<'a> {
    parts: Vec<Text<'a>>,
}

#[derive(Debug, PartialEq)]
enum Text<'a> {
    Plain(&'a str),
    Bold(Paragraph<'a>),
    Italics(Paragraph<'a>),
}

impl<'a> Paragraph<'a> {
    fn new(parts: Vec<Text>) -> Paragraph {
        Paragraph {parts: parts}
    }
    pub fn to_html(&self) -> String {
        self.parts.iter()
            .map(|text| match *text {
                Text::Plain(s)       => s.to_string(),
                Text::Bold(ref p)    => ["", &p.to_html(), ""].concat(),
                Text::Italics(ref p) => ["", &p.to_html(), ""].concat(),
            })
            .collect::<Vec<_>>()
            .concat()
    }
}

named!(pub parse_paragraph<Paragraph>,
    map!(
        many1!(alt!(parse_bold | parse_italics | parse_plain)),
        Paragraph::new
    )
);

named!(parse_only_paragraph<Paragraph>, complete!(parse_paragraph));

named!(parse_plain<Text>,
    map!(
        map_res!(is_not!("_*"), str::from_utf8),
        Text::Plain
    )
);

named!(parse_bold<Text>,
    map_res!(
        delimited!(char!('*'), is_not!("*"), char!('*')),
        |bytes| {
            match parse_only_paragraph(bytes) {
                IResult::Done(_, output) => Ok(Text::Bold(output)),
                IResult::Error(err) => Err(err),
                _ => panic!("Nom complete! macro returned an IResult::Incomplete")
            }
        }
    )
);

named!(parse_italics<Text>,
    map_res!(
        delimited!(char!('_'), is_not!("_"), char!('_')),
        |bytes| {
            match parse_only_paragraph(bytes) {
                IResult::Done(_, output) => Ok(Text::Italics(output)),
                IResult::Error(err) => Err(err),
                _ => panic!("Nom complete! macro returned an IResult::Incomplete")
            }
        }
    )
);

#[test]
fn test_plain_parse() {
    if let IResult::Done(b"*World*", output) = parse_plain(b"Hello *World*") {
        assert_eq!(output, Text::Plain("Hello "));
    } else {
        panic!(r#" Plain parse failed with "Hello *World*" "#);
    }

    match parse_plain(b"*Hello World*") {
        IResult::Error(_) => (),
        _ => panic!(r#" Plain failed with "*Hello World*" "#),
    }
}

#[test]
fn test_bold_parse() {
    if let IResult::Done(b" World", output) = parse_bold(b"*Hello* World") {
        assert_eq!(output, Text::Bold(Paragraph::new(
            vec![Text::Plain("Hello")]
        )));
    } else {
        panic!(r#" Bold failed with "*Hello* World" "#);
    }

    match parse_bold(b"Hello World") {
        IResult::Error(_) => (),
        _ => panic!(r#" Bold failed with "Hello World" "#),
    };

    match parse_bold(b"*Hello World") {
        IResult::Incomplete(_) => (),
        _ => panic!(r#" Bold failed with "*Hello World" "#),
    }
}

#[test]
fn test_paragraph_parse() {
    let data = b"This *is _a_ serious* _test *of* this_ parser";
    if let IResult::Done(remaining, output) = parse_paragraph(data) {
        assert_eq!(remaining, b"");
        println!("{:#?}", output.parts);
        assert_eq!(output.parts, vec![
            Text::Plain("This "),
            Text::Bold(Paragraph::new(vec![
                Text::Plain("is "),
                Text::Italics(Paragraph::new(vec![Text::Plain("a")])),
                Text::Plain(" serious"),
            ])),
            Text::Plain(" "),
            Text::Italics(Paragraph::new(vec![
                Text::Plain("test "),
                Text::Bold(Paragraph::new(vec![Text::Plain("of")])),
                Text::Plain(" this"),
            ])),
            Text::Plain(" parser")
        ]);
    }
}

#[test]
fn test_html1() {
    let p = Paragraph::new(vec![
        Text::Plain("plaintext "),
        Text::Bold(Paragraph::new(vec![Text::Plain("bold text")]))
    ]);

    assert!(p.to_html() == "plaintext bold text".to_owned());
}
<commit_msg>Add paragraph parsing test<commit_after>use nom::*;
use std::str;

#[derive(Debug, PartialEq)]
pub struct Paragraph<'a> {
    parts: Vec<Text<'a>>,
}

#[derive(Debug, PartialEq)]
enum Text<'a> {
    Plain(&'a str),
    Bold(Paragraph<'a>),
    Italics(Paragraph<'a>),
}

impl<'a> Paragraph<'a> {
    fn new(parts: Vec<Text>) -> Paragraph {
        Paragraph {parts: parts}
    }
    pub fn to_html(&self) -> String {
        self.parts.iter()
            .map(|text| match *text {
                Text::Plain(s)       => s.to_string(),
                Text::Bold(ref p)    => ["", &p.to_html(), ""].concat(),
                Text::Italics(ref p) => ["", &p.to_html(), ""].concat(),
            })
            .collect::<Vec<_>>()
            .concat()
    }
}

named!(pub parse_paragraph<Paragraph>,
    map!(
        many1!(alt!(parse_bold | parse_italics | parse_plain)),
        Paragraph::new
    )
);

named!(parse_only_paragraph<Paragraph>, complete!(parse_paragraph));

named!(parse_plain<Text>,
    map!(
        map_res!(is_not!("_*"), str::from_utf8),
        Text::Plain
    )
);

named!(parse_bold<Text>,
    map_res!(
        delimited!(char!('*'), is_not!("*"), char!('*')),
        |bytes| {
            match parse_only_paragraph(bytes) {
                IResult::Done(_, output) => Ok(Text::Bold(output)),
                IResult::Error(err) => Err(err),
                _ => panic!("Nom complete! macro returned an IResult::Incomplete")
            }
        }
    )
);

named!(parse_italics<Text>,
    map_res!(
        delimited!(char!('_'), is_not!("_"), char!('_')),
        |bytes| {
            match parse_only_paragraph(bytes) {
                IResult::Done(_, output) => Ok(Text::Italics(output)),
                IResult::Error(err) => Err(err),
                _ => panic!("Nom complete! macro returned an IResult::Incomplete")
            }
        }
    )
);

#[test]
fn test_plain_parse() {
    if let IResult::Done(b"*World*", output) = parse_plain(b"Hello *World*") {
        assert_eq!(output, Text::Plain("Hello "));
    } else {
        panic!(r#" Plain parse failed with "Hello *World*" "#);
    }

    match parse_plain(b"*Hello World*") {
        IResult::Error(_) => (),
        _ => panic!(r#" Plain failed with "*Hello World*" "#),
    }
}

#[test]
fn test_bold_parse() {
    if let IResult::Done(b" World", output) = parse_bold(b"*Hello* World") {
        assert_eq!(output, Text::Bold(Paragraph::new(
            vec![Text::Plain("Hello")]
        )));
    } else {
        panic!(r#" Bold failed with "*Hello* World" "#);
    }

    match parse_bold(b"Hello World") {
        IResult::Error(_) => (),
        _ => panic!(r#" Bold failed with "Hello World" "#),
    };

    match parse_bold(b"*Hello World") {
        IResult::Incomplete(_) => (),
        _ => panic!(r#" Bold failed with "*Hello World" "#),
    }
}

#[test]
fn test_paragraph_parse() {
    let data = b"This *is _a_ serious* _test *of* this_ parser";
    if let IResult::Done(remaining, output) = parse_paragraph(data) {
        assert_eq!(remaining, b"");
        println!("{:#?}", output.parts);
        assert_eq!(output.parts, vec![
            Text::Plain("This "),
            Text::Bold(Paragraph::new(vec![
                Text::Plain("is "),
                Text::Italics(Paragraph::new(vec![Text::Plain("a")])),
                Text::Plain(" serious"),
            ])),
            Text::Plain(" "),
            Text::Italics(Paragraph::new(vec![
                Text::Plain("test "),
                Text::Bold(Paragraph::new(vec![Text::Plain("of")])),
                Text::Plain(" this"),
            ])),
            Text::Plain(" parser")
        ]);
    }
}

#[test]
fn test_html1() {
    let p = Paragraph::new(vec![
        Text::Plain("plaintext "),
        Text::Bold(Paragraph::new(vec![Text::Plain("bold text")]))
    ]);

    assert!(p.to_html() == "plaintext bold text".to_owned());
}

#[test]
fn test_integration() {
    let data = b"This *is _a_ serious* _test *of* this_ parser";
    if let IResult::Done(_, p) = parse_paragraph(data) {
        assert_eq!(p.to_html(),
            "This is a serious test of this parser"
        );
    } else {
        panic!("Parse failed");
    }
}
<|endoftext|>


<commit_before>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod cli;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}
<commit_msg>src/pmcfg/mod.rs: added tests for evaluate()<commit_after>use std::collections::BTreeMap;
use std::fmt;
use std::hash::{Hash, Hasher};
use std::marker::PhantomData;

mod from_str;
// mod relabel;

pub mod cli;

/// Variable or terminal symbol in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub enum VarT<T> {
    /// `Var(i, j)` represents the `j`th component of the `i`th successor.
    /// Indexing starts from `0`.
    Var(usize, usize),
    T(T),
}

/// Composition function in an MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone, Hash)]
pub struct Composition<T> {
    pub composition: Vec<Vec<VarT<T>>>,
}

impl<T> From<Vec<Vec<VarT<T>>>> for Composition<T> {
    fn from(encapsulated_value: Vec<Vec<VarT<T>>>) -> Self {
        Composition { composition: encapsulated_value }
    }
}

/// Rule of a weighted MCFG.
#[derive(Debug, PartialOrd, Ord, Clone)]
pub struct PMCFGRule<N, T, W> {
    pub head: N,
    pub tail: Vec<N>,
    pub composition: Composition<T>,
    pub weight: W,
}

impl<N, T, W> PMCFGRule<N, T, W>
    where T: Clone,
          W: Clone,
{
    pub fn map_nonterminals<F, M>(&self, f: F) -> PMCFGRule<M, T, W>
        where F: Fn(&N) -> M,
    {
        PMCFGRule {
            head: f(&self.head),
            tail: self.tail.iter().map(f).collect(),
            composition: self.composition.clone(),
            weight: self.weight.clone()
        }
    }
}

/// A weighted MCFG.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
pub struct PMCFG<N, T, W> {
    pub _dummy: PhantomData<T>,
    pub initial: Vec<N>,
    pub rules: Vec<PMCFGRule<N, T, W>>,
}

impl<N: Hash, T: Hash, W> Hash for PMCFGRule<N, T, W> {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.head.hash(state);
        self.tail.hash(state);
        self.composition.hash(state);
    }
}

impl<N: PartialEq, T: PartialEq, W> PartialEq for PMCFGRule<N, T, W> {
    fn eq(&self, other: &Self) -> bool {
        self.head == other.head && self.tail == other.tail && self.composition == other.composition
    }
}

impl<N: Eq, T: Eq, W> Eq for PMCFGRule<N, T, W> {}

impl<T: fmt::Display> fmt::Display for Composition<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter0 = self.composition.iter().peekable();
        let mut iter1;

        buffer.push_str("[");
        while let Some(proj) = iter0.next() {
            iter1 = proj.into_iter().peekable();
            buffer.push_str("[");
            while let Some(vart) = iter1.next() {
                buffer.push_str(format!("{}", vart).as_str());
                if iter1.peek().is_some() {
                    buffer.push_str(", ");
                }
            }
            buffer.push_str("]");
            if iter0.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]");

        write!(f, "{}", buffer)
    }
}

impl<T: fmt::Display> fmt::Display for VarT<T> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            VarT::Var(i, j) => {
                write!(f, "Var {} {}", i, j)
            },
            VarT::T(ref x) => {
                write!(f, "T \"{}\"", x)
            },
        }
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFGRule<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
           let mut buffer = "".to_string();

           let mut iter = self.tail.iter().peekable();

           buffer.push_str("(");
           while let Some(nt) = iter.next() {
               buffer.push_str(format!("\"{}\"", nt).as_str());
               if iter.peek().is_some() {
                   buffer.push_str(", ");
               }
           }
           buffer.push_str(")");

        write!(f, "\"{}\" → {} {}  # {}", self.head, self.composition, buffer, self.weight)
        // write!(f, "\"{}\" → {}", self.head, self.composition)
    }
}

impl<N: fmt::Display, T: fmt::Display, W: fmt::Display> fmt::Display for PMCFG<N, T, W> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let mut buffer = "".to_string();

        let mut iter = self.initial.iter().peekable();

        buffer.push_str("initial: [");
        while let Some(nt) = iter.next() {
            buffer.push_str(format!("\"{}\"", nt).as_str());
            if iter.peek().is_some() {
                buffer.push_str(", ");
            }
        }
        buffer.push_str("]\n\n");

        for r in &self.rules {
            buffer.push_str(format!("{}\n", r).as_str());
        }

        write!(f, "{}", buffer)
    }
}

pub fn evaluate<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>) -> Composition<T> {
    evaluate_pos(term_map, vec![])
}

pub fn evaluate_pos<T: Clone + fmt::Debug>(term_map: &BTreeMap<Vec<usize>, Composition<T>>, address: Vec<usize>) -> Composition<T> {
    let unexpanded_composition = &term_map.get(&address).unwrap().composition;
    let mut expanded_nonterminals: BTreeMap<_, Vec<Vec<VarT<T>>>> = BTreeMap::new();
    let mut expanded_composition = Vec::new();

    for component in unexpanded_composition {
        let mut expanded_component = Vec::new();

        for variable in component {
            match variable {
                &VarT::Var(num_nonter, num_compon) => {
                    if !expanded_nonterminals.contains_key(&num_nonter) {
                        let mut child_address = address.clone();
                        child_address.push(num_nonter);
                        let nonter_compos = evaluate_pos(term_map, child_address).composition;
                        expanded_nonterminals.insert(num_nonter, nonter_compos);
                    }

                    let nonter_compos = expanded_nonterminals.get(&num_nonter).unwrap();

                    if let Some(compon) = nonter_compos.get(num_compon) {
                        for terminal in compon {
                            expanded_component.push(terminal.clone());
                        }
                    } else {
                        panic!("{:?}: use of {}-th component of nonterminal {} that has only {} components!",
                               unexpanded_composition, num_compon, num_nonter, nonter_compos.len());
                    }

                },
                &VarT::T(ref terminal) => {
                    expanded_component.push(VarT::T(terminal.clone()));
                },
            }
        }

        expanded_composition.push(expanded_component);
    }

    Composition::from(expanded_composition)
}

#[cfg(test)]
mod tests {
    use super::*;
    use VarT::{Var, T};

    #[test]
    fn test_evaluate() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
                vec![Var(0,0), Var(1,0), Var(0,1), Var(1,1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![0,0], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![0,0,0], Composition::from(vec![
                vec![],
                vec![]
        ]));
        term_map.insert(vec![0,0,1], Composition::from(vec![
                vec![T('a')]
        ]));
        term_map.insert(vec![0,0,2], Composition::from(vec![
                vec![T('c')]
        ]));
        term_map.insert(vec![0,1], Composition::from(vec![
                vec![T('a')]
        ]));
        term_map.insert(vec![0,2], Composition::from(vec![
                vec![T('c')]
        ]));
        term_map.insert(vec![1], Composition::from(vec![
                vec![Var(1,0), Var(0,0)],
                vec![Var(2,0), Var(0,1)]
        ]));
        term_map.insert(vec![1,0], Composition::from(vec![
                vec![],
                vec![]
        ]));
        term_map.insert(vec![1,1], Composition::from(vec![
                vec![T('b')]
        ]));
        term_map.insert(vec![1,2], Composition::from(vec![
                vec![T('d')]
        ]));

        let expanded_compos = Composition::from(vec![
            vec![T('a'), T('a'), T('b'), T('c'), T('c'), T('d')]
        ]);

        assert_eq!(expanded_compos, evaluate(&term_map));
    }

    #[test]
    #[should_panic(expected =
        "[[Var(0, 0), Var(0, 1)]]: use of 1-th component of nonterminal 0 that has only 1 components!"
    )]
    fn test_evaluate_invalid_composition() {
        let mut term_map: BTreeMap<Vec<usize>, _> = BTreeMap::new();
        term_map.insert(vec![], Composition::from(vec![
                vec![Var(0,0), Var(0,1)]
        ]));
        term_map.insert(vec![0], Composition::from(vec![
                vec![T('a')]
        ]));

        evaluate(&term_map);
    }
}
<|endoftext|>