<commit_before>//! build command
use clap::{App, Arg, SubCommand};

#[cfg(feature = "self-updates")]
mod internal {
    extern crate flate2;
    extern crate futures;
    extern crate hyper;
    extern crate hyper_rustls;
    extern crate same_file;
    extern crate tar;
    extern crate tokio_core;

    use self::flate2::FlateReadExt;
    use self::futures::future::{err, ok};
    use self::futures::{Future, Stream};
    use self::hyper::{Client, Method, Request, Uri};
    use self::tar::Archive;
    use self::tokio_core::reactor::{Core, Handle};
    use VERSION;
    use clap::ArgMatches;
    use config_env::ConfigEnv;
    use core::errors::{Error, Result};
    use core::{Context, Version};
    use std::env;
    use std::fs::{self, File};
    use std::io::{self, Cursor};
    use std::path::Path;
    use std::rc::Rc;
    use url::Url;

    #[cfg(target_arch = "x86_64")]
    const ARCH: Option<&str> = Some("x86_64");

    #[cfg(not(target_arch = "x86_64"))]
    const ARCH: Option<&str> = None;

    #[cfg(target_os = "macos")]
    const PLATFORM: Option<&str> = Some("osx");

    #[cfg(target_os = "linux")]
    const PLATFORM: Option<&str> = Some("linux");

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    const PLATFORM: Option<&str> = None;

    const DEFAULT_URL: &str = "https://storage.googleapis.com/reproto-releases/";

    pub fn entry(_: Rc<Context>, m: &ArgMatches) -> Result<()> {
        let config =
            ConfigEnv::new()?.ok_or_else(|| format!("could not setup the reproto environment"))?;

        check_path(&config)?;

        let force = m.is_present("force");
        let prerelease = m.is_present("prerelease");

        let arch = ARCH.clone().or(m.value_of("arch")).ok_or_else(|| {
            format!("Architecture could not be detected, and is not specified with `--arch`")
        })?;

        let platform = PLATFORM.clone().or(m.value_of("platform")).ok_or_else(|| {
            format!("Architecture could not be detected, and is not specified with `--platform`")
        })?;

        let current = Version::parse(VERSION)?;

        let url = m.value_of("url").unwrap_or(DEFAULT_URL);
        let url = Url::parse(url)?;

        let mut core = Core::new()?;
        let handle = core.handle();

        let mut client = UpdateClient::new(handle, url)?;

        let mut releases = core.run(client.get_releases())?;

        releases.sort();

        let releases = match prerelease {
            true => releases,
            false => releases
                .into_iter()
                .filter(|r| !r.is_prerelease())
                .collect(),
        };

        let candidate = if !force {
            releases.into_iter().filter(|r| r > &current).last()
        } else {
            releases.into_iter().last()
        };

        let version = match candidate {
            Some(version) => version,
            None => {
                info!("reproto is up-to-date!");
                return Ok(());
            }
        };

        let archived = config
            .releases_dir
            .join(format!("reproto-{}-{}-{}", version, platform, arch));

        if !archived.is_file() || force {
            let release = core.run(client.get_release(&version, platform, arch))
                .map_err(|e| format!("failed to download archive: {}", e.display()))?;

            download_archive(release, &archived).map_err(|e| {
                format!(
                    "failed to download archive to: {}: {}",
                    archived.display(),
                    e.display()
                )
            })?;
        }

        let bin = config.bin_dir.join("reproto");

        setup_symlink(&archived, &bin).map_err(|e| {
            format!(
                "failed to setup symlink to: {}: {}",
                bin.display(),
                e.display()
            )
        })?;

        return Ok(());

        /// Checks that bin_dir is in PATH, or warns otherwise.
        fn check_path(config: &ConfigEnv) -> Result<()> {
            let mut bin_in_path = false;

            if let Some(paths) = env::var_os("PATH") {
                for path in env::split_paths(&paths) {
                    if same_file::is_same_file(path, &config.bin_dir)? {
                        bin_in_path = true;
                    }
                }
            }

            if !bin_in_path {
                warn!(
                    "{}: is not in your PATH. This is required for reproto to work!",
                    config.bin_dir.display()
                );
            }

            Ok(())
        }

        fn download_archive(release: Vec<u8>, out: &Path) -> Result<()> {
            let mut archive = Archive::new(Cursor::new(release).gz_decode()?);

            for file in archive.entries()? {
                let mut file = file?;

                {
                    let path_bytes = file.header().path_bytes();
                    let path = ::std::str::from_utf8(path_bytes.as_ref())?;

                    match path {
                        "reproto" => {}
                        path => {
                            warn!("got unexpected file in archive: {}", path);
                            continue;
                        }
                    }
                }

                if let Some(parent) = out.parent() {
                    if !parent.is_dir() {
                        info!("creating directory: {}", parent.display());
                        fs::create_dir_all(parent)?;
                    }
                }

                info!("writing: {}", out.display());
                let mut w = File::create(out)?;
                io::copy(&mut file, &mut w)?;

                let metadata = w.metadata()?;
                let mut perm = metadata.permissions();
                set_executable(&mut perm);
                w.set_permissions(perm)?;
                break;
            }

            return Ok(());

            #[cfg(any(target_os = "linux", target_os = "macos"))]
            fn set_executable(p: &mut fs::Permissions) {
                use std::os::unix::fs::PermissionsExt;
                p.set_mode(0755);
            }

            #[cfg(not(any(target_os = "linux", target_os = "macos")))]
            fn set_executable(p: &mut fs::Permissions) {
                warn!("cannot update permissions on this platform");
            }
        }

        fn setup_symlink(archived: &Path, bin: &Path) -> Result<()> {
            if let Some(parent) = bin.parent() {
                if !parent.is_dir() {
                    info!("creating directory: {}", parent.display());
                    fs::create_dir_all(parent)?;
                }
            }

            info!("creating symlink: {}", bin.display());

            if bin.is_file() {
                fs::remove_file(bin)?;
            }

            symlink(archived, bin)?;
            return Ok(());

            #[cfg(not(target_os = "windows"))]
            fn symlink(archived: &Path, bin: &Path) -> Result<()> {
                use std::os::unix;
                unix::fs::symlink(archived, bin)?;
                Ok(())
            }

            #[cfg(target_os = "windows")]
            fn symlink(archived: &Path, bin: &Path) -> Result<()> {
                use std::os::windows;
                windows::fs::symlink_file(archived, bin)?;
                Ok(())
            }
        }
    }

    struct UpdateClient {
        client: Client<hyper_rustls::HttpsConnector>,
        url: Url,
    }

    impl UpdateClient {
        pub fn new(handle: Handle, url: Url) -> Result<Self> {
            let client = Client::configure()
                .connector(hyper_rustls::HttpsConnector::new(4, &handle))
                .build(&handle);

            Ok(Self { client, url })
        }

        fn request(&mut self, request: Request) -> impl Future<Item = Vec<u8>, Error = Error> {
            self.client
                .request(request)
                .map_err(|e| Error::from(e))
                .and_then(|res| {
                    let status = res.status().clone();

                    res.body()
                        .map_err(|e| Error::from(e))
                        .fold(Vec::new(), |mut out: Vec<u8>, chunk| {
                            out.extend(chunk.as_ref());
                            ok::<_, Error>(out)
                        })
                        .map(move |body| (body, status))
                })
                .and_then(|(body, status)| {
                    if !status.is_success() {
                        if let Ok(body) = String::from_utf8(body) {
                            return err(format!("bad response: {}: {}", status, body).into());
                        }

                        return err(format!("bad response: {}", status).into());
                    }

                    ok(body)
                })
        }

        pub fn get_releases(&mut self) -> Box<Future<Item = Vec<Version>, Error = Error>> {
            let url = match self.url.join("releases") {
                Err(e) => return Box::new(err(e.into())),
                Ok(url) => url,
            };

            let uri = match url.as_ref().parse::<Uri>() {
                Err(e) => return Box::new(err(e.into())),
                Ok(uri) => uri,
            };

            let request = Request::new(Method::Get, uri);

            let url = url.clone();

            let future = self.request(request)
                .and_then(|body| {
                    let body = match String::from_utf8(body) {
                        Err(e) => return err(format!("body is not utf-8: {}", e).into()),
                        Ok(body) => body,
                    };

                    let mut out = Vec::new();

                    for line in body.split('\n') {
                        let line = line.trim();

                        if line.is_empty() {
                            continue;
                        }

                        let version = match Version::parse(line) {
                            Err(e) => return err(e.into()),
                            Ok(version) => version,
                        };

                        out.push(version);
                    }

                    ok(out)
                })
                .map_err(move |e| format!("request to `{}` failed: {}", url, e.display()).into());

            Box::new(future)
        }

        pub fn get_release(
            &mut self,
            version: &Version,
            platform: &str,
            arch: &str,
        ) -> Box<Future<Item = Vec<u8>, Error = Error>> {
            let url = match self.url
                .join(&format!("reproto-{}-{}-{}.tar.gz", version, platform, arch))
            {
                Err(e) => return Box::new(err(e.into())),
                Ok(url) => url,
            };

            let uri = match url.as_ref().parse::<Uri>() {
                Err(e) => return Box::new(err(e.into())),
                Ok(uri) => uri,
            };

            let request = Request::new(Method::Get, uri);

            let url = url.clone();

            let future = self.request(request)
                .map_err(move |e| format!("request to `{}` failed: {}", url, e.display()).into());

            Box::new(future)
        }
    }
}

#[cfg(not(feature = "self-updates"))]
mod internal {
    use clap::ArgMatches;
    use core::Context;
    use core::errors::Result;
    use std::rc::Rc;

    pub fn entry(_: Rc<Context>, _: &ArgMatches) -> Result<()> {
        return Err("support for self-updates is not enabled".into());
    }
}

pub use self::internal::entry;

pub fn options<'a, 'b>() -> App<'a, 'b> {
    let out = SubCommand::with_name("self-update").about("Update reproto");

    let out = out.arg(
        Arg::with_name("url")
            .long("url")
            .takes_value(true)
            .help("URL to download updates from."),
    );

    let out = out.arg(
        Arg::with_name("arch")
            .long("arch")
            .takes_value(true)
            .help("Architecture to update for"),
    );

    let out = out.arg(
        Arg::with_name("platform")
            .long("platform")
            .takes_value(true)
            .help("Architecture to update for"),
    );

    let out = out.arg(
        Arg::with_name("force")
            .short("f")
            .long("force")
            .help("Force downloading the latest release, even though it is already installed"),
    );

    let out = out.arg(
        Arg::with_name("prerelease")
            .long("pre")
            .help("Download a pre-release if available"),
    );

    out
}
<commit_msg>fix compile on stable<commit_after>//! build command
use clap::{App, Arg, SubCommand};

#[cfg(feature = "self-updates")]
mod internal {
    extern crate flate2;
    extern crate futures;
    extern crate hyper;
    extern crate hyper_rustls;
    extern crate same_file;
    extern crate tar;
    extern crate tokio_core;

    use self::flate2::FlateReadExt;
    use self::futures::future::{err, ok};
    use self::futures::{Future, Stream};
    use self::hyper::{Client, Method, Request, Uri};
    use self::tar::Archive;
    use self::tokio_core::reactor::{Core, Handle};
    use VERSION;
    use clap::ArgMatches;
    use config_env::ConfigEnv;
    use core::errors::{Error, Result};
    use core::{Context, Version};
    use std::env;
    use std::fs::{self, File};
    use std::io::{self, Cursor};
    use std::path::Path;
    use std::rc::Rc;
    use url::Url;

    #[cfg(target_arch = "x86_64")]
    const ARCH: Option<&str> = Some("x86_64");

    #[cfg(not(target_arch = "x86_64"))]
    const ARCH: Option<&str> = None;

    #[cfg(target_os = "macos")]
    const PLATFORM: Option<&str> = Some("osx");

    #[cfg(target_os = "linux")]
    const PLATFORM: Option<&str> = Some("linux");

    #[cfg(not(any(target_os = "linux", target_os = "macos")))]
    const PLATFORM: Option<&str> = None;

    const DEFAULT_URL: &str = "https://storage.googleapis.com/reproto-releases/";

    pub fn entry(_: Rc<Context>, m: &ArgMatches) -> Result<()> {
        let config =
            ConfigEnv::new()?.ok_or_else(|| format!("could not setup the reproto environment"))?;

        check_path(&config)?;

        let force = m.is_present("force");
        let prerelease = m.is_present("prerelease");

        let arch = ARCH.clone().or(m.value_of("arch")).ok_or_else(|| {
            format!("Architecture could not be detected, and is not specified with `--arch`")
        })?;

        let platform = PLATFORM.clone().or(m.value_of("platform")).ok_or_else(|| {
            format!("Architecture could not be detected, and is not specified with `--platform`")
        })?;

        let current = Version::parse(VERSION)?;

        let url = m.value_of("url").unwrap_or(DEFAULT_URL);
        let url = Url::parse(url)?;

        let mut core = Core::new()?;
        let handle = core.handle();

        let mut client = UpdateClient::new(handle, url)?;

        let mut releases = core.run(client.get_releases())?;

        releases.sort();

        let releases = match prerelease {
            true => releases,
            false => releases
                .into_iter()
                .filter(|r| !r.is_prerelease())
                .collect(),
        };

        let candidate = if !force {
            releases.into_iter().filter(|r| r > &current).last()
        } else {
            releases.into_iter().last()
        };

        let version = match candidate {
            Some(version) => version,
            None => {
                info!("reproto is up-to-date!");
                return Ok(());
            }
        };

        let archived = config
            .releases_dir
            .join(format!("reproto-{}-{}-{}", version, platform, arch));

        if !archived.is_file() || force {
            let release = core.run(client.get_release(&version, platform, arch))
                .map_err(|e| format!("failed to download archive: {}", e.display()))?;

            download_archive(release, &archived).map_err(|e| {
                format!(
                    "failed to download archive to: {}: {}",
                    archived.display(),
                    e.display()
                )
            })?;
        }

        let bin = config.bin_dir.join("reproto");

        setup_symlink(&archived, &bin).map_err(|e| {
            format!(
                "failed to setup symlink to: {}: {}",
                bin.display(),
                e.display()
            )
        })?;

        return Ok(());

        /// Checks that bin_dir is in PATH, or warns otherwise.
        fn check_path(config: &ConfigEnv) -> Result<()> {
            let mut bin_in_path = false;

            if let Some(paths) = env::var_os("PATH") {
                for path in env::split_paths(&paths) {
                    if same_file::is_same_file(path, &config.bin_dir)? {
                        bin_in_path = true;
                    }
                }
            }

            if !bin_in_path {
                warn!(
                    "{}: is not in your PATH. This is required for reproto to work!",
                    config.bin_dir.display()
                );
            }

            Ok(())
        }

        fn download_archive(release: Vec<u8>, out: &Path) -> Result<()> {
            let mut archive = Archive::new(Cursor::new(release).gz_decode()?);

            for file in archive.entries()? {
                let mut file = file?;

                {
                    let path_bytes = file.header().path_bytes();
                    let path = ::std::str::from_utf8(path_bytes.as_ref())?;

                    match path {
                        "reproto" => {}
                        path => {
                            warn!("got unexpected file in archive: {}", path);
                            continue;
                        }
                    }
                }

                if let Some(parent) = out.parent() {
                    if !parent.is_dir() {
                        info!("creating directory: {}", parent.display());
                        fs::create_dir_all(parent)?;
                    }
                }

                info!("writing: {}", out.display());
                let mut w = File::create(out)?;
                io::copy(&mut file, &mut w)?;

                let metadata = w.metadata()?;
                let mut perm = metadata.permissions();
                set_executable(&mut perm);
                w.set_permissions(perm)?;
                break;
            }

            return Ok(());

            #[cfg(any(target_os = "linux", target_os = "macos"))]
            fn set_executable(p: &mut fs::Permissions) {
                use std::os::unix::fs::PermissionsExt;
                p.set_mode(0755);
            }

            #[cfg(not(any(target_os = "linux", target_os = "macos")))]
            fn set_executable(p: &mut fs::Permissions) {
                warn!("cannot update permissions on this platform");
            }
        }

        fn setup_symlink(archived: &Path, bin: &Path) -> Result<()> {
            if let Some(parent) = bin.parent() {
                if !parent.is_dir() {
                    info!("creating directory: {}", parent.display());
                    fs::create_dir_all(parent)?;
                }
            }

            info!("creating symlink: {}", bin.display());

            if bin.is_file() {
                fs::remove_file(bin)?;
            }

            symlink(archived, bin)?;
            return Ok(());

            #[cfg(not(target_os = "windows"))]
            fn symlink(archived: &Path, bin: &Path) -> Result<()> {
                use std::os::unix;
                unix::fs::symlink(archived, bin)?;
                Ok(())
            }

            #[cfg(target_os = "windows")]
            fn symlink(archived: &Path, bin: &Path) -> Result<()> {
                use std::os::windows;
                windows::fs::symlink_file(archived, bin)?;
                Ok(())
            }
        }
    }

    struct UpdateClient {
        client: Client<hyper_rustls::HttpsConnector>,
        url: Url,
    }

    impl UpdateClient {
        pub fn new(handle: Handle, url: Url) -> Result<Self> {
            let client = Client::configure()
                .connector(hyper_rustls::HttpsConnector::new(4, &handle))
                .build(&handle);

            Ok(Self { client, url })
        }

        fn request(&mut self, request: Request) -> Box<Future<Item = Vec<u8>, Error = Error>> {
            Box::new(
                self.client
                    .request(request)
                    .map_err(|e| Error::from(e))
                    .and_then(|res| {
                        let status = res.status().clone();

                        res.body()
                            .map_err(|e| Error::from(e))
                            .fold(Vec::new(), |mut out: Vec<u8>, chunk| {
                                out.extend(chunk.as_ref());
                                ok::<_, Error>(out)
                            })
                            .map(move |body| (body, status))
                    })
                    .and_then(|(body, status)| {
                        if !status.is_success() {
                            if let Ok(body) = String::from_utf8(body) {
                                return err(format!("bad response: {}: {}", status, body).into());
                            }

                            return err(format!("bad response: {}", status).into());
                        }

                        ok(body)
                    }),
            )
        }

        pub fn get_releases(&mut self) -> Box<Future<Item = Vec<Version>, Error = Error>> {
            let url = match self.url.join("releases") {
                Err(e) => return Box::new(err(e.into())),
                Ok(url) => url,
            };

            let uri = match url.as_ref().parse::<Uri>() {
                Err(e) => return Box::new(err(e.into())),
                Ok(uri) => uri,
            };

            let request = Request::new(Method::Get, uri);

            let url = url.clone();

            let future = self.request(request)
                .and_then(|body| {
                    let body = match String::from_utf8(body) {
                        Err(e) => return err(format!("body is not utf-8: {}", e).into()),
                        Ok(body) => body,
                    };

                    let mut out = Vec::new();

                    for line in body.split('\n') {
                        let line = line.trim();

                        if line.is_empty() {
                            continue;
                        }

                        let version = match Version::parse(line) {
                            Err(e) => return err(e.into()),
                            Ok(version) => version,
                        };

                        out.push(version);
                    }

                    ok(out)
                })
                .map_err(move |e| format!("request to `{}` failed: {}", url, e.display()).into());

            Box::new(future)
        }

        pub fn get_release(
            &mut self,
            version: &Version,
            platform: &str,
            arch: &str,
        ) -> Box<Future<Item = Vec<u8>, Error = Error>> {
            let url = match self.url
                .join(&format!("reproto-{}-{}-{}.tar.gz", version, platform, arch))
            {
                Err(e) => return Box::new(err(e.into())),
                Ok(url) => url,
            };

            let uri = match url.as_ref().parse::<Uri>() {
                Err(e) => return Box::new(err(e.into())),
                Ok(uri) => uri,
            };

            let request = Request::new(Method::Get, uri);

            let url = url.clone();

            let future = self.request(request)
                .map_err(move |e| format!("request to `{}` failed: {}", url, e.display()).into());

            Box::new(future)
        }
    }
}

#[cfg(not(feature = "self-updates"))]
mod internal {
    use clap::ArgMatches;
    use core::Context;
    use core::errors::Result;
    use std::rc::Rc;

    pub fn entry(_: Rc<Context>, _: &ArgMatches) -> Result<()> {
        return Err("support for self-updates is not enabled".into());
    }
}

pub use self::internal::entry;

pub fn options<'a, 'b>() -> App<'a, 'b> {
    let out = SubCommand::with_name("self-update").about("Update reproto");

    let out = out.arg(
        Arg::with_name("url")
            .long("url")
            .takes_value(true)
            .help("URL to download updates from."),
    );

    let out = out.arg(
        Arg::with_name("arch")
            .long("arch")
            .takes_value(true)
            .help("Architecture to update for"),
    );

    let out = out.arg(
        Arg::with_name("platform")
            .long("platform")
            .takes_value(true)
            .help("Architecture to update for"),
    );

    let out = out.arg(
        Arg::with_name("force")
            .short("f")
            .long("force")
            .help("Force downloading the latest release, even though it is already installed"),
    );

    let out = out.arg(
        Arg::with_name("prerelease")
            .long("pre")
            .help("Download a pre-release if available"),
    );

    out
}
<|endoftext|>


<commit_before>use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use analysis::Analyzer;
use analysis::tokenizers::TokenizerSpec;
use analysis::filters::FilterSpec;

#[derive(Debug)]
pub struct AnalyzerRegistry {
    analyzers: HashMap<String, Analyzer>,
    tokenizers: HashMap<String, TokenizerSpec>,
    filters: HashMap<String, FilterSpec>,
}

impl AnalyzerRegistry {
    pub fn new() -> AnalyzerRegistry {
        AnalyzerRegistry {
            analyzers: HashMap::new(),
            tokenizers: HashMap::new(),
            filters: HashMap::new(),
        }
    }
}

impl Deref for AnalyzerRegistry {
    type Target = HashMap<String, Analyzer>;

    fn deref(&self) -> &HashMap<String, Analyzer> {
        &self.analyzers
    }
}

impl DerefMut for AnalyzerRegistry {
    fn deref_mut(&mut self) -> &mut HashMap<String, Analyzer> {
        &mut self.analyzers
    }
}
<commit_msg>Use new AnalyzerSpec in AnalyzerRegistry<commit_after>use std::collections::HashMap;
use std::ops::{Deref, DerefMut};

use analysis::AnalyzerSpec;
use analysis::tokenizers::TokenizerSpec;
use analysis::filters::FilterSpec;

#[derive(Debug)]
pub struct AnalyzerRegistry {
    analyzers: HashMap<String, AnalyzerSpec>,
    tokenizers: HashMap<String, TokenizerSpec>,
    filters: HashMap<String, FilterSpec>,
}

impl AnalyzerRegistry {
    pub fn new() -> AnalyzerRegistry {
        AnalyzerRegistry {
            analyzers: HashMap::new(),
            tokenizers: HashMap::new(),
            filters: HashMap::new(),
        }
    }
}

impl Deref for AnalyzerRegistry {
    type Target = HashMap<String, AnalyzerSpec>;

    fn deref(&self) -> &HashMap<String, AnalyzerSpec> {
        &self.analyzers
    }
}

impl DerefMut for AnalyzerRegistry {
    fn deref_mut(&mut self) -> &mut HashMap<String, AnalyzerSpec> {
        &mut self.analyzers
    }
}
<|endoftext|>


<commit_before><commit_msg>fill_match_arms bind pattern implementation<commit_after><|endoftext|>


<commit_before>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
#![allow(dead_code)]
#![allow(unused_variables)]
#![allow(unused_must_use)]

include!("bindings.rs");

use std::process;
//use std::os::raw::c_uint;
//use std::os::raw::c_int;
use std::os::raw::c_char;

#[derive(PartialEq)]
enum PrintState {
    OUT,
    IN,
    DISPLAY
}

fn print_ptr_rec(p: ptr, state: PrintState) {
    let x = p as u32;
    if  (x & fx_mask) == fx_tag {
        print!("{}", (x as i32) >> fx_shift);
    } else if x == bool_f {
        print!("#f");
    } else if x == bool_t {
        print!("#t");
    } else if x == list_nil {
        print!("()");
    } else if x == eof_obj {
        print!("#!eof");
    } else if (x & char_mask) == char_tag {
        let c = std::char::from_u32(x >> char_shift).
            expect("a char");
        if state == PrintState::DISPLAY {
            print!("{}", c);
        } else {
            if      c == '\t' { print!("#\\tab"); }
            else if c == '\n' { print!("#\\newline"); }
            else if c == '\r' { print!("#\\return"); }
            else if c == ' '  { print!("#\\space"); }
            else              { print!("#\\{}", c); }
        }
    } else if (x & obj_mask) == pair_tag {
        if state == PrintState::OUT { print!("("); }
        let cell = unsafe { *((x-pair_tag) as *const cell) };
        let car = cell.car;
        print_ptr_rec(car, PrintState::OUT);
        let cdr = cell.cdr;
        if cdr != list_nil {
            if (cdr & obj_mask) != pair_tag {
                print!(" . ");
                print_ptr_rec(cdr, PrintState::OUT);
            } else {
                print!(" ");
                print_ptr_rec(cdr, PrintState::IN);
            }
        }
        if state == PrintState::OUT { print!(")"); }
    } else if (x & obj_mask) == vector_tag {
        print!("#(");
        unsafe {
        let p = &*((x-vector_tag) as *const vector);
        let n = (p.length as i32) >> fx_shift;
        for i in 0..n {
            if i > 0 { print!(" "); }
            print_ptr_rec(
                *((p.buf.as_ptr()).offset(i as isize)),
                PrintState::OUT)
        }}
        print!(")");
    } else if (x & obj_mask) == string_tag {
        if state == PrintState::OUT { print!("\""); }
        unsafe {
        let p = &*((x-string_tag) as *const string);
        let n = (p.length as i32) >> fx_shift;
        for i in 0..n {
            let c = std::char::from_u32(*((p.buf.as_ptr()).offset(i as isize)) as u32)
                .expect("char");
            if c == '"'       { print!("\\\""); }
            else if c == '\\' { print!("\\\\"); }
            else              { print!("{}", c); }
        }}
        if state == PrintState::OUT { print!("\""); }
    } else if (x & obj_mask) == symbol_tag {
        print_ptr_rec((x - symbol_tag) | string_tag, PrintState::IN);
    } else if (x & obj_mask) == closure_tag {
        print!("#<procedure>");
    } else {
        print!("#<unknown 0x{:x}>", x);
    }
}
fn print_ptr(x: ptr) {
    print_ptr_rec(x, PrintState::OUT);
    println!("");
}

#[no_mangle]
pub extern "C" fn ik_log(msg: ptr) {
}
#[no_mangle]
pub extern "C" fn ik_error(x: ptr) {
    process::exit(0);
}
#[no_mangle]
pub extern "C" fn s_write(fd: ptr, str: ptr, len: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_open_write(fname: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_fflush(fd: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn scheme_write(fd: ptr, x: ptr, opt: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_open_read(fname: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_read_char(fd: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_close(fd: ptr) -> ptr {
    0
}

#[no_mangle]
pub extern "C" fn heap_alloc(mem: *mut memory, stack: *mut c_char, size: usize) -> *mut c_char {
    let heap_next = unsafe { (*mem).heap_next };
    let heap_new = unsafe { heap_next.offset(size as isize) };
    if heap_new >= unsafe { (*mem).heap_top } {
        eprintln!("Exception: overflow");
        std::process::exit(0);
    }
    unsafe {(*mem).heap_next = heap_new};
    return heap_next;
}

fn allocate_protected_space(size: usize) -> *mut c_char {
    let mut v = Vec::with_capacity(size);
    let ptr = v.as_mut_ptr();
    std::mem::forget(v);
    ptr
}

fn deallocate_protected_space(p: *mut c_char, size: usize) {
    unsafe { std::mem::drop(Vec::from_raw_parts(p, 0, size)) };
}

fn main() {
    let stack_size = 16 * 4096;
    let heap_size = 4 * 16 * 4096;
    let global_size = 16 * 4096;
    let scratch_size = 16 * 4096;

    let stack_top = allocate_protected_space(stack_size);
    let stack_base = unsafe { stack_top.offset(stack_size as isize) };

    let heap = allocate_protected_space(heap_size);
    let global = allocate_protected_space(global_size);
    let scratch = allocate_protected_space(scratch_size);

    let uninit = 0 as (*mut std::os::raw::c_void);

    let mut ctxt = context {
        eax : uninit,
        ebx : uninit,
        ecx : uninit,
        edx : uninit,
        esi : uninit,
        edi : uninit,
        ebp : uninit,
        esp : uninit,
    };

    let heap_top = unsafe { heap.offset((heap_size as isize)/2) };
    let mut mem = memory {
        heap_next : heap,
        global_next : global,
        heap_base : heap,
        heap_top : heap_top,
        heap_base_alt : heap_top,
        heap_top_alt : unsafe { heap.offset(heap_size as isize) },
        global_base : global,
        stack_base : stack_base,
        scratch_base : scratch,
        edi : 0
    };

    print_ptr(unsafe {
        scheme_entry(&mut ctxt, stack_base, &mut mem)
    });

    deallocate_protected_space(stack_top, stack_size);
    deallocate_protected_space(heap, stack_size);
}
<commit_msg>minor cleanup<commit_after>#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]

#![allow(unused_variables)]

include!("bindings.rs");

use std::os::raw::c_char;

#[derive(PartialEq)]
enum PrintState {
    OUT,
    IN,
    DISPLAY
}

fn print_ptr_rec(p: ptr, state: PrintState) {
    let x = p as u32;
    if  (x & fx_mask) == fx_tag {
        print!("{}", (x as i32) >> fx_shift);
    } else if x == bool_f {
        print!("#f");
    } else if x == bool_t {
        print!("#t");
    } else if x == list_nil {
        print!("()");
    } else if x == eof_obj {
        print!("#!eof");
    } else if (x & char_mask) == char_tag {
        let c = std::char::from_u32(x >> char_shift).
            expect("a char");
        if state == PrintState::DISPLAY {
            print!("{}", c);
        } else {
            if      c == '\t' { print!("#\\tab"); }
            else if c == '\n' { print!("#\\newline"); }
            else if c == '\r' { print!("#\\return"); }
            else if c == ' '  { print!("#\\space"); }
            else              { print!("#\\{}", c); }
        }
    } else if (x & obj_mask) == pair_tag {
        if state == PrintState::OUT { print!("("); }
        let cell = unsafe { *((x-pair_tag) as *const cell) };
        let car = cell.car;
        print_ptr_rec(car, PrintState::OUT);
        let cdr = cell.cdr;
        if cdr != list_nil {
            if (cdr & obj_mask) != pair_tag {
                print!(" . ");
                print_ptr_rec(cdr, PrintState::OUT);
            } else {
                print!(" ");
                print_ptr_rec(cdr, PrintState::IN);
            }
        }
        if state == PrintState::OUT { print!(")"); }
    } else if (x & obj_mask) == vector_tag {
        print!("#(");
        unsafe {
        let p = &*((x-vector_tag) as *const vector);
        let n = (p.length as i32) >> fx_shift;
        for i in 0..n {
            if i > 0 { print!(" "); }
            print_ptr_rec(
                *((p.buf.as_ptr()).offset(i as isize)),
                PrintState::OUT)
        }}
        print!(")");
    } else if (x & obj_mask) == string_tag {
        if state == PrintState::OUT { print!("\""); }
        unsafe {
        let p = &*((x-string_tag) as *const string);
        let n = (p.length as i32) >> fx_shift;
        for i in 0..n {
            let c = std::char::from_u32(*((p.buf.as_ptr()).offset(i as isize)) as u32)
                .expect("char");
            if c == '"'       { print!("\\\""); }
            else if c == '\\' { print!("\\\\"); }
            else              { print!("{}", c); }
        }}
        if state == PrintState::OUT { print!("\""); }
    } else if (x & obj_mask) == symbol_tag {
        print_ptr_rec((x - symbol_tag) | string_tag, PrintState::IN);
    } else if (x & obj_mask) == closure_tag {
        print!("#<procedure>");
    } else {
        print!("#<unknown 0x{:x}>", x);
    }
}
fn print_ptr(x: ptr) {
    print_ptr_rec(x, PrintState::OUT);
    println!("");
}

#[no_mangle]
pub extern "C" fn ik_log(msg: ptr) {
}
#[no_mangle]
pub extern "C" fn ik_error(x: ptr) {
    std::process::exit(0);
}
#[no_mangle]
pub extern "C" fn s_write(fd: ptr, str: ptr, len: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_open_write(fname: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_fflush(fd: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn scheme_write(fd: ptr, x: ptr, opt: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_open_read(fname: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_read_char(fd: ptr) -> ptr {
    0
}
#[no_mangle]
pub extern "C" fn s_close(fd: ptr) -> ptr {
    0
}

#[no_mangle]
pub extern "C" fn heap_alloc(mem: *mut memory, stack: *mut c_char, size: usize) -> *mut c_char {
    let heap_next = unsafe { (*mem).heap_next };
    let heap_new = unsafe { heap_next.offset(size as isize) };
    if heap_new >= unsafe { (*mem).heap_top } {
        eprintln!("Exception: overflow");
        std::process::exit(0);
    }
    unsafe {(*mem).heap_next = heap_new};
    return heap_next;
}

fn allocate_protected_space(size: usize) -> *mut c_char {
    let mut v = Vec::with_capacity(size);
    let ptr = v.as_mut_ptr();
    std::mem::forget(v);
    ptr
}

fn deallocate_protected_space(p: *mut c_char, size: usize) {
    unsafe { std::mem::drop(Vec::from_raw_parts(p, 0, size)) };
}

fn main() {
    let stack_size = 16 * 4096;
    let heap_size = 4 * 16 * 4096;
    let global_size = 16 * 4096;
    let scratch_size = 16 * 4096;

    let stack_top = allocate_protected_space(stack_size);
    let stack_base = unsafe { stack_top.offset(stack_size as isize) };

    let heap = allocate_protected_space(heap_size);
    let global = allocate_protected_space(global_size);
    let scratch = allocate_protected_space(scratch_size);

    let uninit = 0 as (*mut std::os::raw::c_void);

    let mut ctxt = context {
        eax : uninit,
        ebx : uninit,
        ecx : uninit,
        edx : uninit,
        esi : uninit,
        edi : uninit,
        ebp : uninit,
        esp : uninit,
    };

    let heap_top = unsafe { heap.offset((heap_size as isize)/2) };
    let mut mem = memory {
        heap_next : heap,
        global_next : global,
        heap_base : heap,
        heap_top : heap_top,
        heap_base_alt : heap_top,
        heap_top_alt : unsafe { heap.offset(heap_size as isize) },
        global_base : global,
        stack_base : stack_base,
        scratch_base : scratch,
        edi : 0
    };

    print_ptr(unsafe {
        scheme_entry(&mut ctxt, stack_base, &mut mem)
    });

    deallocate_protected_space(stack_top, stack_size);
    deallocate_protected_space(heap, stack_size);
}
<|endoftext|>


<commit_before>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    ///
    /// ```rust
    /// // should be
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
                ItemKind::Trait(..))
            {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let whitelisted_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds.iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                if let ty::PredicateKind::Trait(poly_trait_ref, _) = obligation.predicate.kind() {
                    if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars()
                    {
                        return None;
                    }
                    Some(poly_trait_ref)
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.tables()).consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds
                    .iter()
                    .filter(|t| t.skip_binder().self_ty() == ty)
                    .collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym!(vec_type));
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym!(Vec))
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
            .iter()
            .any(|&allow| attr.check_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>) {}
}
<commit_msg>Shrink ParamEnv to 16 bytes<commit_after>use crate::utils::ptr::get_spans;
use crate::utils::{
    get_trait_def_id, implements_trait, is_copy, is_self, is_type_diagnostic_item, multispan_sugg, paths, snippet,
    snippet_opt, span_lint_and_then,
};
use if_chain::if_chain;
use rustc_ast::ast::Attribute;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir::intravisit::FnKind;
use rustc_hir::{BindingAnnotation, Body, FnDecl, GenericArg, HirId, ItemKind, Node, PatKind, QPath, TyKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_lint::{LateContext, LateLintPass};
use rustc_middle::ty::{self, TypeFoldable};
use rustc_session::{declare_lint_pass, declare_tool_lint};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
use rustc_trait_selection::traits;
use rustc_trait_selection::traits::misc::can_type_implement_copy;
use rustc_typeck::expr_use_visitor as euv;
use std::borrow::Cow;

declare_clippy_lint! {
    /// **What it does:** Checks for functions taking arguments by value, but not
    /// consuming them in its
    /// body.
    ///
    /// **Why is this bad?** Taking arguments by reference is more flexible and can
    /// sometimes avoid
    /// unnecessary allocations.
    ///
    /// **Known problems:**
    /// * This lint suggests taking an argument by reference,
    /// however sometimes it is better to let users decide the argument type
    /// (by using `Borrow` trait, for example), depending on how the function is used.
    ///
    /// **Example:**
    /// ```rust
    /// fn foo(v: Vec<i32>) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    ///
    /// ```rust
    /// // should be
    /// fn foo(v: &[i32]) {
    ///     assert_eq!(v.len(), 42);
    /// }
    /// ```
    pub NEEDLESS_PASS_BY_VALUE,
    pedantic,
    "functions taking arguments by value, but not consuming them in its body"
}

declare_lint_pass!(NeedlessPassByValue => [NEEDLESS_PASS_BY_VALUE]);

macro_rules! need {
    ($e: expr) => {
        if let Some(x) = $e {
            x
        } else {
            return;
        }
    };
}

impl<'a, 'tcx> LateLintPass<'a, 'tcx> for NeedlessPassByValue {
    #[allow(clippy::too_many_lines)]
    fn check_fn(
        &mut self,
        cx: &LateContext<'a, 'tcx>,
        kind: FnKind<'tcx>,
        decl: &'tcx FnDecl<'_>,
        body: &'tcx Body<'_>,
        span: Span,
        hir_id: HirId,
    ) {
        if span.from_expansion() {
            return;
        }

        match kind {
            FnKind::ItemFn(.., header, _, attrs) => {
                if header.abi != Abi::Rust || requires_exact_signature(attrs) {
                    return;
                }
            },
            FnKind::Method(..) => (),
            FnKind::Closure(..) => return,
        }

        // Exclude non-inherent impls
        if let Some(Node::Item(item)) = cx.tcx.hir().find(cx.tcx.hir().get_parent_node(hir_id)) {
            if matches!(item.kind, ItemKind::Impl{ of_trait: Some(_), .. } |
                ItemKind::Trait(..))
            {
                return;
            }
        }

        // Allow `Borrow` or functions to be taken by value
        let borrow_trait = need!(get_trait_def_id(cx, &paths::BORROW_TRAIT));
        let whitelisted_traits = [
            need!(cx.tcx.lang_items().fn_trait()),
            need!(cx.tcx.lang_items().fn_once_trait()),
            need!(cx.tcx.lang_items().fn_mut_trait()),
            need!(get_trait_def_id(cx, &paths::RANGE_ARGUMENT_TRAIT)),
        ];

        let sized_trait = need!(cx.tcx.lang_items().sized_trait());

        let fn_def_id = cx.tcx.hir().local_def_id(hir_id);

        let preds = traits::elaborate_predicates(cx.tcx, cx.param_env.caller_bounds().iter())
            .filter(|p| !p.is_global())
            .filter_map(|obligation| {
                if let ty::PredicateKind::Trait(poly_trait_ref, _) = obligation.predicate.kind() {
                    if poly_trait_ref.def_id() == sized_trait || poly_trait_ref.skip_binder().has_escaping_bound_vars()
                    {
                        return None;
                    }
                    Some(poly_trait_ref)
                } else {
                    None
                }
            })
            .collect::<Vec<_>>();

        // Collect moved variables and spans which will need dereferencings from the
        // function body.
        let MovedVariablesCtxt {
            moved_vars,
            spans_need_deref,
            ..
        } = {
            let mut ctx = MovedVariablesCtxt::default();
            cx.tcx.infer_ctxt().enter(|infcx| {
                euv::ExprUseVisitor::new(&mut ctx, &infcx, fn_def_id, cx.param_env, cx.tables()).consume_body(body);
            });
            ctx
        };

        let fn_sig = cx.tcx.fn_sig(fn_def_id);
        let fn_sig = cx.tcx.erase_late_bound_regions(&fn_sig);

        for (idx, ((input, &ty), arg)) in decl.inputs.iter().zip(fn_sig.inputs()).zip(body.params).enumerate() {
            // All spans generated from a proc-macro invocation are the same...
            if span == input.span {
                return;
            }

            // Ignore `self`s.
            if idx == 0 {
                if let PatKind::Binding(.., ident, _) = arg.pat.kind {
                    if ident.as_str() == "self" {
                        continue;
                    }
                }
            }

            //
            // * Exclude a type that is specifically bounded by `Borrow`.
            // * Exclude a type whose reference also fulfills its bound. (e.g., `std::convert::AsRef`,
            //   `serde::Serialize`)
            let (implements_borrow_trait, all_borrowable_trait) = {
                let preds = preds
                    .iter()
                    .filter(|t| t.skip_binder().self_ty() == ty)
                    .collect::<Vec<_>>();

                (
                    preds.iter().any(|t| t.def_id() == borrow_trait),
                    !preds.is_empty() && {
                        let ty_empty_region = cx.tcx.mk_imm_ref(cx.tcx.lifetimes.re_root_empty, ty);
                        preds.iter().all(|t| {
                            let ty_params = &t.skip_binder().trait_ref.substs.iter().skip(1).collect::<Vec<_>>();
                            implements_trait(cx, ty_empty_region, t.def_id(), ty_params)
                        })
                    },
                )
            };

            if_chain! {
                if !is_self(arg);
                if !ty.is_mutable_ptr();
                if !is_copy(cx, ty);
                if !whitelisted_traits.iter().any(|&t| implements_trait(cx, ty, t, &[]));
                if !implements_borrow_trait;
                if !all_borrowable_trait;

                if let PatKind::Binding(mode, canonical_id, ..) = arg.pat.kind;
                if !moved_vars.contains(&canonical_id);
                then {
                    if mode == BindingAnnotation::Mutable || mode == BindingAnnotation::RefMut {
                        continue;
                    }

                    // Dereference suggestion
                    let sugg = |diag: &mut DiagnosticBuilder<'_>| {
                        if let ty::Adt(def, ..) = ty.kind {
                            if let Some(span) = cx.tcx.hir().span_if_local(def.did) {
                                if can_type_implement_copy(cx.tcx, cx.param_env, ty).is_ok() {
                                    diag.span_help(span, "consider marking this type as `Copy`");
                                }
                            }
                        }

                        let deref_span = spans_need_deref.get(&canonical_id);
                        if_chain! {
                            if is_type_diagnostic_item(cx, ty, sym!(vec_type));
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_owned()")]);
                            if let TyKind::Path(QPath::Resolved(_, ref path)) = input.kind;
                            if let Some(elem_ty) = path.segments.iter()
                                .find(|seg| seg.ident.name == sym!(Vec))
                                .and_then(|ps| ps.args.as_ref())
                                .map(|params| params.args.iter().find_map(|arg| match arg {
                                    GenericArg::Type(ty) => Some(ty),
                                    _ => None,
                                }).unwrap());
                            then {
                                let slice_ty = format!("&[{}]", snippet(cx, elem_ty.span, "_"));
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    slice_ty,
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x)),
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                // cannot be destructured, no need for `*` suggestion
                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        if is_type_diagnostic_item(cx, ty, sym!(string_type)) {
                            if let Some(clone_spans) =
                                get_spans(cx, Some(body.id()), idx, &[("clone", ".to_string()"), ("as_str", "")]) {
                                diag.span_suggestion(
                                    input.span,
                                    "consider changing the type to",
                                    "&str".to_string(),
                                    Applicability::Unspecified,
                                );

                                for (span, suggestion) in clone_spans {
                                    diag.span_suggestion(
                                        span,
                                        &snippet_opt(cx, span)
                                            .map_or(
                                                "change the call to".into(),
                                                |x| Cow::from(format!("change `{}` to", x))
                                            ),
                                        suggestion.into(),
                                        Applicability::Unspecified,
                                    );
                                }

                                assert!(deref_span.is_none());
                                return;
                            }
                        }

                        let mut spans = vec![(input.span, format!("&{}", snippet(cx, input.span, "_")))];

                        // Suggests adding `*` to dereference the added reference.
                        if let Some(deref_span) = deref_span {
                            spans.extend(
                                deref_span
                                    .iter()
                                    .cloned()
                                    .map(|span| (span, format!("*{}", snippet(cx, span, "<expr>")))),
                            );
                            spans.sort_by_key(|&(span, _)| span);
                        }
                        multispan_sugg(diag, "consider taking a reference instead", spans);
                    };

                    span_lint_and_then(
                        cx,
                        NEEDLESS_PASS_BY_VALUE,
                        input.span,
                        "this argument is passed by value, but not consumed in the function body",
                        sugg,
                    );
                }
            }
        }
    }
}

/// Functions marked with these attributes must have the exact signature.
fn requires_exact_signature(attrs: &[Attribute]) -> bool {
    attrs.iter().any(|attr| {
        [sym!(proc_macro), sym!(proc_macro_attribute), sym!(proc_macro_derive)]
            .iter()
            .any(|&allow| attr.check_name(allow))
    })
}

#[derive(Default)]
struct MovedVariablesCtxt {
    moved_vars: FxHashSet<HirId>,
    /// Spans which need to be prefixed with `*` for dereferencing the
    /// suggested additional reference.
    spans_need_deref: FxHashMap<HirId, FxHashSet<Span>>,
}

impl MovedVariablesCtxt {
    fn move_common(&mut self, cmt: &euv::PlaceWithHirId<'_>) {
        if let euv::PlaceBase::Local(vid) = cmt.place.base {
            self.moved_vars.insert(vid);
        }
    }
}

impl<'tcx> euv::Delegate<'tcx> for MovedVariablesCtxt {
    fn consume(&mut self, cmt: &euv::PlaceWithHirId<'tcx>, mode: euv::ConsumeMode) {
        if let euv::ConsumeMode::Move = mode {
            self.move_common(cmt);
        }
    }

    fn borrow(&mut self, _: &euv::PlaceWithHirId<'tcx>, _: ty::BorrowKind) {}

    fn mutate(&mut self, _: &euv::PlaceWithHirId<'tcx>) {}
}
<|endoftext|>


<commit_before>use js::run::exec::Executor;
use js::run::jit::JITCompiler;
use syntax::Lexer;
use syntax::Parser;
use getopts::Matches;
use std::io::stdio::stdin;
use std::io::{BufReader, BufferedReader};
/// An interactive command-line mode
pub struct Interactive {
	m: Matches
}
impl Interactive {
	/// Create a new interactive mode info
	pub fn new(m: Matches) -> Interactive {
		Interactive {
			m: m
		}
	}
	/// Run the interactive mode
	pub fn run(&self) {
		print!("> ");
		let mut engine : JITCompiler = Executor::new();
		let verbose = self.m.opt_present("v");
		let mut input = stdin();
		loop {
			let line = input.read_line().unwrap();
			let line_bytes = line.as_bytes();
			if verbose {
				println!("Lexing");
			}
			let mut lexer = Lexer::new(BufferedReader::new(BufReader::new(line_bytes)));
			lexer.lex().unwrap();
			let tokens = lexer.tokens;
			if verbose {
				println!("Tokens: {}", tokens);
				println!("Parsing");
			}
			let expr = Parser::new(tokens).parse_all().unwrap();
			if verbose {
				println!("Expression: {}", expr);
			}
			let compiled = engine.compile(&expr);
			match engine.run(compiled) {
				Ok(v) =>
					println!("{}", v),
				Err(v) =>
					println!("Failed with {}", v)
			}
			print!("> ");
		}
	}
}<commit_msg>Add more verbose messages to interactive mode<commit_after>use js::run::exec::Executor;
use js::run::jit::JITCompiler;
use syntax::Lexer;
use syntax::Parser;
use getopts::Matches;
use std::io::stdio::stdin;
use std::io::{BufReader, BufferedReader};
/// An interactive command-line mode
pub struct Interactive {
	m: Matches
}
impl Interactive {
	/// Create a new interactive mode info
	pub fn new(m: Matches) -> Interactive {
		Interactive {
			m: m
		}
	}
	/// Run the interactive mode
	pub fn run(&self) {
		print!("> ");
		let mut engine : JITCompiler = Executor::new();
		let verbose = self.m.opt_present("v");
		let mut input = stdin();
		loop {
			let line = input.read_line().unwrap();
			let line_bytes = line.as_bytes();
			if verbose {
				println!("Lexing");
			}
			let mut lexer = Lexer::new(BufferedReader::new(BufReader::new(line_bytes)));
			lexer.lex().unwrap();
			let tokens = lexer.tokens;
			if verbose {
				println!("Tokens: {}", tokens);
				println!("Parsing");
			}
			let expr = Parser::new(tokens).parse_all().unwrap();
			if verbose {
				println!("Expression: {}", expr);
				println!("Executing with LibJIT backend");
			}
			let compiled = engine.compile(&expr);
			match engine.run(compiled) {
				Ok(v) =>
					println!("{}", v),
				Err(v) =>
					println!("Failed with {}", v)
			}
			print!("> ");
		}
	}
}<|endoftext|>


<commit_before>//! Ignoring globs in `.gitignore` files.
//!
//! This uses a cache because the file with the globs in might not be the same
//! directory that we’re listing!

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::RwLock;

use fs::filter::IgnorePatterns;

/// An **ignore cache** holds sets of glob patterns paired with the
/// directories that they should be ignored underneath. Believe it or not,
/// that’s a valid English sentence.
#[derive(Default, Debug)]
pub struct IgnoreCache {
    entries: RwLock<Vec<(PathBuf, IgnorePatterns)>>
}

impl IgnoreCache {
    pub fn new() -> IgnoreCache {
        IgnoreCache::default()
    }

    #[allow(unused_results)]  // don’t do this
    pub fn discover_underneath(&self, path: &Path) {
        let mut path = Some(path);
        let mut entries = self.entries.write().unwrap();

        while let Some(p) = path {
            if p.components().next().is_none() { break }

            let ignore_file = p.join(".gitignore");
            if ignore_file.is_file() {
                debug!("Found a .gitignore file: {:?}", ignore_file);
                if let Ok(mut file) = File::open(ignore_file) {
                    let mut contents = String::new();
                    file.read_to_string(&mut contents).expect("Reading gitignore failed");

                    let (patterns, mut _errors) = IgnorePatterns::parse_from_iter(contents.lines());
                    entries.push((p.into(), patterns));
                }
            }
            else {
                debug!("Found no .gitignore file at {:?}", ignore_file);
            }

            path = p.parent();
        }
    }

    pub fn is_ignored(&self, suspect: &Path) -> bool {
        let entries = self.entries.read().unwrap();
        entries.iter().any(|&(ref base_path, ref patterns)| {
            if let Ok(suffix) = suspect.strip_prefix(&base_path) {
                patterns.is_ignored_path(suffix)
            }
            else {
                false
            }
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn empty() {
        let ignores = IgnoreCache::default();
        assert_eq!(false, ignores.is_ignored(Path::new("/usr/bin/drinking")));
        assert_eq!(false, ignores.is_ignored(Path::new("target/debug/exa")));
    }
}
<commit_msg>Don’t ignore .gitignore errors<commit_after>//! Ignoring globs in `.gitignore` files.
//!
//! This uses a cache because the file with the globs in might not be the same
//! directory that we’re listing!

use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};
use std::sync::RwLock;

use fs::filter::IgnorePatterns;

/// An **ignore cache** holds sets of glob patterns paired with the
/// directories that they should be ignored underneath. Believe it or not,
/// that’s a valid English sentence.
#[derive(Default, Debug)]
pub struct IgnoreCache {
    entries: RwLock<Vec<(PathBuf, IgnorePatterns)>>
}

impl IgnoreCache {
    pub fn new() -> IgnoreCache {
        IgnoreCache::default()
    }

    pub fn discover_underneath(&self, path: &Path) {
        let mut path = Some(path);
        let mut entries = self.entries.write().unwrap();

        while let Some(p) = path {
            if p.components().next().is_none() { break }

            let ignore_file = p.join(".gitignore");
            if ignore_file.is_file() {
                debug!("Found a .gitignore file: {:?}", ignore_file);
                if let Ok(mut file) = File::open(ignore_file) {
                    let mut contents = String::new();

                    match file.read_to_string(&mut contents) {
                        Ok(_) => {
                            let (patterns, mut _errors) = IgnorePatterns::parse_from_iter(contents.lines());
                            entries.push((p.into(), patterns));
                        }
                        Err(e) => debug!("Failed to read a .gitignore: {:?}", e)
                    }
                }
            }
            else {
                debug!("Found no .gitignore file at {:?}", ignore_file);
            }

            path = p.parent();
        }
    }

    pub fn is_ignored(&self, suspect: &Path) -> bool {
        let entries = self.entries.read().unwrap();
        entries.iter().any(|&(ref base_path, ref patterns)| {
            if let Ok(suffix) = suspect.strip_prefix(&base_path) {
                patterns.is_ignored_path(suffix)
            }
            else {
                false
            }
        })
    }
}

#[cfg(test)]
mod test {
    use super::*;

    #[test]
    fn empty() {
        let ignores = IgnoreCache::default();
        assert_eq!(false, ignores.is_ignored(Path::new("/usr/bin/drinking")));
        assert_eq!(false, ignores.is_ignored(Path::new("target/debug/exa")));
    }
}
<|endoftext|>


<commit_before>// # Custom collector example
//
// This example shows how you can implement your own
// collector. As an example, we will compute a collector
// that computes the standard deviation of a given fast field.
//
// Of course, you can have a look at the tantivy's built-in collectors
// such as the `CountCollector` for more examples.

// ---
// Importing tantivy...
use crate::collector::{Collector, SegmentCollector};
use crate::fastfield::FastFieldReader;
use crate::schema::Field;
use crate::{Score, SegmentReader, TantivyError};

/// The `FilterCollector` collector filters docs using a u64 fast field value and a predicate.
/// Only the documents for which the predicate returned "true" will be passed on to the next collector.
///
/// ```rust
/// use tantivy::collector::{TopDocs, FilterCollector};
/// use tantivy::query::QueryParser;
/// use tantivy::schema::{Schema, TEXT, INDEXED, FAST};
/// use tantivy::{doc, DocAddress, Index};
///
/// let mut schema_builder = Schema::builder();
/// let title = schema_builder.add_text_field("title", TEXT);
/// let price = schema_builder.add_u64_field("price", INDEXED | FAST);
/// let schema = schema_builder.build();
/// let index = Index::create_in_ram(schema);
///
/// let mut index_writer = index.writer_with_num_threads(1, 10_000_000).unwrap();
/// index_writer.add_document(doc!(title => "The Name of the Wind", price => 30_200u64));
/// index_writer.add_document(doc!(title => "The Diary of Muadib", price => 29_240u64));
/// index_writer.add_document(doc!(title => "A Dairy Cow", price => 21_240u64));
/// index_writer.add_document(doc!(title => "The Diary of a Young Girl", price => 20_120u64));
/// assert!(index_writer.commit().is_ok());
///
/// let reader = index.reader().unwrap();
/// let searcher = reader.searcher();
///
/// let query_parser = QueryParser::for_index(&index, vec![title]);
/// let query = query_parser.parse_query("diary").unwrap();
/// let no_filter_collector = FilterCollector::new(price, &|value| value > 20_120u64, TopDocs::with_limit(2));
/// let top_docs = searcher.search(&query, &no_filter_collector).unwrap();
///
/// assert_eq!(top_docs.len(), 1);
/// assert_eq!(top_docs[0].1, DocAddress(0, 1));
///
/// let filter_all_collector = FilterCollector::new(price, &|value| value < 5u64, TopDocs::with_limit(2));
/// let filtered_top_docs = searcher.search(&query, &filter_all_collector).unwrap();
///
/// assert_eq!(filtered_top_docs.len(), 0);
/// ```
pub struct FilterCollector<TCollector, TPredicate>
where
    TPredicate: 'static,
{
    field: Field,
    collector: TCollector,
    predicate: &'static TPredicate,
}

impl<TCollector, TPredicate> FilterCollector<TCollector, TPredicate>
where
    TCollector: Collector + Send + Sync,
    TPredicate: Fn(u64) -> bool + Send + Sync,
{
    /// Create a new FilterCollector.
    pub fn new(
        field: Field,
        predicate: &'static TPredicate,
        collector: TCollector,
    ) -> FilterCollector<TCollector, TPredicate> {
        FilterCollector {
            field,
            predicate,
            collector,
        }
    }
}

impl<TCollector, TPredicate> Collector for FilterCollector<TCollector, TPredicate>
where
    TCollector: Collector + Send + Sync,
    TPredicate: 'static + Fn(u64) -> bool + Send + Sync,
{
    // That's the type of our result.
    // Our standard deviation will be a float.
    type Fruit = TCollector::Fruit;

    type Child = FilterSegmentCollector<TCollector::Child, TPredicate>;

    fn for_segment(
        &self,
        segment_local_id: u32,
        segment_reader: &SegmentReader,
    ) -> crate::Result<FilterSegmentCollector<TCollector::Child, TPredicate>> {
        let fast_field_reader = segment_reader
            .fast_fields()
            .u64(self.field)
            .ok_or_else(|| {
                let field_name = segment_reader.schema().get_field_name(self.field);
                TantivyError::SchemaError(format!(
                    "Field {:?} is not a u64 fast field.",
                    field_name
                ))
            })?;
        let segment_collector = self
            .collector
            .for_segment(segment_local_id, segment_reader)?;
        Ok(FilterSegmentCollector {
            fast_field_reader,
            segment_collector: segment_collector,
            predicate: self.predicate,
        })
    }

    fn requires_scoring(&self) -> bool {
        self.collector.requires_scoring()
    }

    fn merge_fruits(
        &self,
        segment_fruits: Vec<<TCollector::Child as SegmentCollector>::Fruit>,
    ) -> crate::Result<TCollector::Fruit> {
        self.collector.merge_fruits(segment_fruits)
    }
}

pub struct FilterSegmentCollector<TSegmentCollector, TPredicate>
where
    TPredicate: 'static,
{
    fast_field_reader: FastFieldReader<u64>,
    segment_collector: TSegmentCollector,
    predicate: &'static TPredicate,
}

impl<TSegmentCollector, TPredicate> SegmentCollector
    for FilterSegmentCollector<TSegmentCollector, TPredicate>
where
    TSegmentCollector: SegmentCollector,
    TPredicate: 'static + Fn(u64) -> bool + Send + Sync,
{
    type Fruit = TSegmentCollector::Fruit;

    fn collect(&mut self, doc: u32, score: Score) {
        let value = self.fast_field_reader.get(doc);
        if (self.predicate)(value) {
            self.segment_collector.collect(doc, score)
        }
    }

    fn harvest(self) -> <TSegmentCollector as SegmentCollector>::Fruit {
        self.segment_collector.harvest()
    }
}
<commit_msg>Fix clippy warning<commit_after>// # Custom collector example
//
// This example shows how you can implement your own
// collector. As an example, we will compute a collector
// that computes the standard deviation of a given fast field.
//
// Of course, you can have a look at the tantivy's built-in collectors
// such as the `CountCollector` for more examples.

// ---
// Importing tantivy...
use crate::collector::{Collector, SegmentCollector};
use crate::fastfield::FastFieldReader;
use crate::schema::Field;
use crate::{Score, SegmentReader, TantivyError};

/// The `FilterCollector` collector filters docs using a u64 fast field value and a predicate.
/// Only the documents for which the predicate returned "true" will be passed on to the next collector.
///
/// ```rust
/// use tantivy::collector::{TopDocs, FilterCollector};
/// use tantivy::query::QueryParser;
/// use tantivy::schema::{Schema, TEXT, INDEXED, FAST};
/// use tantivy::{doc, DocAddress, Index};
///
/// let mut schema_builder = Schema::builder();
/// let title = schema_builder.add_text_field("title", TEXT);
/// let price = schema_builder.add_u64_field("price", INDEXED | FAST);
/// let schema = schema_builder.build();
/// let index = Index::create_in_ram(schema);
///
/// let mut index_writer = index.writer_with_num_threads(1, 10_000_000).unwrap();
/// index_writer.add_document(doc!(title => "The Name of the Wind", price => 30_200u64));
/// index_writer.add_document(doc!(title => "The Diary of Muadib", price => 29_240u64));
/// index_writer.add_document(doc!(title => "A Dairy Cow", price => 21_240u64));
/// index_writer.add_document(doc!(title => "The Diary of a Young Girl", price => 20_120u64));
/// assert!(index_writer.commit().is_ok());
///
/// let reader = index.reader().unwrap();
/// let searcher = reader.searcher();
///
/// let query_parser = QueryParser::for_index(&index, vec![title]);
/// let query = query_parser.parse_query("diary").unwrap();
/// let no_filter_collector = FilterCollector::new(price, &|value| value > 20_120u64, TopDocs::with_limit(2));
/// let top_docs = searcher.search(&query, &no_filter_collector).unwrap();
///
/// assert_eq!(top_docs.len(), 1);
/// assert_eq!(top_docs[0].1, DocAddress(0, 1));
///
/// let filter_all_collector = FilterCollector::new(price, &|value| value < 5u64, TopDocs::with_limit(2));
/// let filtered_top_docs = searcher.search(&query, &filter_all_collector).unwrap();
///
/// assert_eq!(filtered_top_docs.len(), 0);
/// ```
pub struct FilterCollector<TCollector, TPredicate>
where
    TPredicate: 'static,
{
    field: Field,
    collector: TCollector,
    predicate: &'static TPredicate,
}

impl<TCollector, TPredicate> FilterCollector<TCollector, TPredicate>
where
    TCollector: Collector + Send + Sync,
    TPredicate: Fn(u64) -> bool + Send + Sync,
{
    /// Create a new FilterCollector.
    pub fn new(
        field: Field,
        predicate: &'static TPredicate,
        collector: TCollector,
    ) -> FilterCollector<TCollector, TPredicate> {
        FilterCollector {
            field,
            predicate,
            collector,
        }
    }
}

impl<TCollector, TPredicate> Collector for FilterCollector<TCollector, TPredicate>
where
    TCollector: Collector + Send + Sync,
    TPredicate: 'static + Fn(u64) -> bool + Send + Sync,
{
    // That's the type of our result.
    // Our standard deviation will be a float.
    type Fruit = TCollector::Fruit;

    type Child = FilterSegmentCollector<TCollector::Child, TPredicate>;

    fn for_segment(
        &self,
        segment_local_id: u32,
        segment_reader: &SegmentReader,
    ) -> crate::Result<FilterSegmentCollector<TCollector::Child, TPredicate>> {
        let fast_field_reader = segment_reader
            .fast_fields()
            .u64(self.field)
            .ok_or_else(|| {
                let field_name = segment_reader.schema().get_field_name(self.field);
                TantivyError::SchemaError(format!(
                    "Field {:?} is not a u64 fast field.",
                    field_name
                ))
            })?;
        let segment_collector = self
            .collector
            .for_segment(segment_local_id, segment_reader)?;
        Ok(FilterSegmentCollector {
            fast_field_reader,
            segment_collector,
            predicate: self.predicate,
        })
    }

    fn requires_scoring(&self) -> bool {
        self.collector.requires_scoring()
    }

    fn merge_fruits(
        &self,
        segment_fruits: Vec<<TCollector::Child as SegmentCollector>::Fruit>,
    ) -> crate::Result<TCollector::Fruit> {
        self.collector.merge_fruits(segment_fruits)
    }
}

pub struct FilterSegmentCollector<TSegmentCollector, TPredicate>
where
    TPredicate: 'static,
{
    fast_field_reader: FastFieldReader<u64>,
    segment_collector: TSegmentCollector,
    predicate: &'static TPredicate,
}

impl<TSegmentCollector, TPredicate> SegmentCollector
    for FilterSegmentCollector<TSegmentCollector, TPredicate>
where
    TSegmentCollector: SegmentCollector,
    TPredicate: 'static + Fn(u64) -> bool + Send + Sync,
{
    type Fruit = TSegmentCollector::Fruit;

    fn collect(&mut self, doc: u32, score: Score) {
        let value = self.fast_field_reader.get(doc);
        if (self.predicate)(value) {
            self.segment_collector.collect(doc, score)
        }
    }

    fn harvest(self) -> <TSegmentCollector as SegmentCollector>::Fruit {
        self.segment_collector.harvest()
    }
}
<|endoftext|>