<commit_before>use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoError, CargoResult, internal};
use util::Config;

pub struct PathSource<'cfg> {
    source_id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
    recursive: bool,
}

impl<'cfg> PathSource<'cfg> {
    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    ///
    /// This source will only return the package at precisely the `path`
    /// specified, and it will be an error if there's not a package at `path`.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        PathSource {
            source_id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
            recursive: false,
        }
    }

    /// Creates a new source which is walked recursively to discover packages.
    ///
    /// This is similar to the `new` method except that instead of requiring a
    /// valid package to be present at `root` the folder is walked entirely to
    /// crawl for packages.
    ///
    /// Note that this should be used with care and likely shouldn't be chosen
    /// by default!
    pub fn new_recursive(root: &Path, id: &SourceId, config: &'cfg Config)
                         -> PathSource<'cfg> {
        PathSource {
            recursive: true,
            .. PathSource::new(root, id, config)
        }
    }

    pub fn root_package(&mut self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        self.update()?;

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    pub fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.recursive {
            ops::read_packages(&self.path, &self.source_id, self.config)
        } else {
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = ops::read_package(&path, &self.source_id, self.config)?;
            Ok(vec![pkg])
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                CargoError::from(format!("could not parse pattern `{}`: {}", p, e))
            })
        };

        let exclude = pkg.manifest()
                         .exclude()
                         .iter()
                         .map(|p| parse(p))
                         .collect::<Result<Vec<_>, _>>()?;

        let include = pkg.manifest()
                         .include()
                         .iter()
                         .map(|p| parse(p))
                         .collect::<Result<Vec<_>, _>>()?;

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, root).unwrap();
            include.iter().any(|p| p.matches_path(relative_path)) || {
                include.is_empty() &&
                 !exclude.iter().any(|p| p.matches_path(relative_path))
            }
        };

        if let Some(result) = self.discover_git_and_list_files(pkg, root, &mut filter) {
            return result;
        }

        self.list_files_walk(pkg, &mut filter)
    }

    // Returns Some(_) if found sibling Cargo.toml and .git folder;
    // otherwise caller should fall back on full file list.
    fn discover_git_and_list_files(&self,
                                   pkg: &Package,
                                   root: &Path,
                                   filter: &mut FnMut(&Path) -> bool)
                                   -> Option<CargoResult<Vec<PathBuf>>> {
        // If this package is in a git repository, then we really do want to
        // query the git repository as it takes into account items such as
        // .gitignore. We're not quite sure where the git repository is,
        // however, so we do a bit of a probe.
        //
        // We walk this package's path upwards and look for a sibling
        // Cargo.toml and .git folder. If we find one then we assume that we're
        // part of that repository.
        let mut cur = root;
        loop {
            if cur.join("Cargo.toml").is_file() {
                // If we find a git repository next to this Cargo.toml, we still
                // check to see if we are indeed part of the index. If not, then
                // this is likely an unrelated git repo, so keep going.
                if let Ok(repo) = git2::Repository::open(cur) {
                    let index = match repo.index() {
                        Ok(index) => index,
                        Err(err) => return Some(Err(err.into())),
                    };
                    let path = util::without_prefix(root, cur)
                                    .unwrap().join("Cargo.toml");
                    if index.get_path(&path, 0).is_some() {
                        return Some(self.list_files_git(pkg, repo, filter));
                    }
                }
            }
            // don't cross submodule boundaries
            if cur.join(".git").is_dir() {
                break
            }
            match cur.parent() {
                Some(parent) => cur = parent,
                None => break,
            }
        }
        return None;
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = repo.index()?;
        let root = repo.workdir().ok_or_else(|| {
            internal("Can't list files on a bare repository.")
        })?;
        let pkg_path = pkg.root();

        let mut ret = Vec::<PathBuf>::new();

        // We use information from the git repository to guide us in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked and untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        if let Some(suffix) = util::without_prefix(pkg_path, root) {
            opts.pathspec(suffix);
        }
        let statuses = repo.statuses(Some(&mut opts))?;
        let untracked = statuses.iter().filter_map(|entry| {
            match entry.status() {
                git2::STATUS_WT_NEW => Some((join(root, entry.path_bytes()), None)),
                _ => None
            }
        });

        let mut subpackages_found = Vec::new();

        for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = file_path?;

            // Filter out files blatantly outside this package. This is helped a
            // bit obove via the `pathspec` function call, but we need to filter
            // the entries in the index as well.
            if !file_path.starts_with(pkg_path) {
                continue
            }

            match file_path.file_name().and_then(|s| s.to_str()) {
                // Filter out Cargo.lock and target always, we don't want to
                // package a lock file no one will ever read and we also avoid
                // build artifacts
                Some("Cargo.lock") |
                Some("target") => continue,

                // Keep track of all sub-packages found and also strip out all
                // matches we've found so far. Note, though, that if we find
                // our own `Cargo.toml` we keep going.
                Some("Cargo.toml") => {
                    let path = file_path.parent().unwrap();
                    if path != pkg_path {
                        warn!("subpackage found: {}", path.display());
                        ret.retain(|p| !p.starts_with(path));
                        subpackages_found.push(path.to_path_buf());
                        continue
                    }
                }

                _ => {}
            }

            // If this file is part of any other sub-package we've found so far,
            // skip it.
            if subpackages_found.iter().any(|p| file_path.starts_with(p)) {
                continue
            }

            if is_dir.unwrap_or_else(|| file_path.is_dir()) {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, root).unwrap();
                let rel = rel.to_str().ok_or_else(|| {
                    CargoError::from(format!("invalid utf-8 filename: {}", rel.display()))
                })?;
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = self.list_files_git(pkg, repo, filter)?;
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        PathSource::walk(&file_path, &mut ret, false,
                                              filter)?;
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        PathSource::walk(pkg.root(), &mut ret, true, filter)?;
        Ok(ret)
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in fs::read_dir(path)? {
            let dir = dir?.path();
            let name = dir.file_name().and_then(|s| s.to_str());
            // Skip dotfile directories
            if name.map(|s| s.starts_with('.')) == Some(true) {
                continue
            } else if is_root {
                // Skip cargo artifacts
                match name {
                    Some("target") | Some("Cargo.lock") => continue,
                    _ => {}
                }
            }
            PathSource::walk(&dir, ret, false, filter)?;
        }
        Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self,
             dep: &Dependency,
             f: &mut FnMut(Summary)) -> CargoResult<()> {
        for s in self.packages.iter().map(|p| p.summary()) {
            if dep.matches(s) {
                f(s.clone())
            }
        }
        Ok(())
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = self.read_packages()?;
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!("getting packages; id={}", id);

        let pkg = self.packages.iter().find(|pkg| pkg.package_id() == id);
        pkg.cloned().ok_or_else(|| {
            internal(format!("failed to find {} in path source", id))
        })
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal("BUG: source was not updated"));
        }

        let mut max = FileTime::zero();
        let mut max_path = PathBuf::from("");
        for file in self.list_files(pkg)? {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(&file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            if mtime > max {
                max = mtime;
                max_path = file;
            }
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(format!("{} ({})", max, max_path.display()))
    }
}
<commit_msg>Fix #4135: if theres `include = [...]`, then dont prepopulate filelist via git<commit_after>use std::fmt::{self, Debug, Formatter};
use std::fs;
use std::path::{Path, PathBuf};

use filetime::FileTime;
use git2;
use glob::Pattern;

use core::{Package, PackageId, Summary, SourceId, Source, Dependency, Registry};
use ops;
use util::{self, CargoError, CargoResult, internal};
use util::Config;

pub struct PathSource<'cfg> {
    source_id: SourceId,
    path: PathBuf,
    updated: bool,
    packages: Vec<Package>,
    config: &'cfg Config,
    recursive: bool,
}

impl<'cfg> PathSource<'cfg> {
    /// Invoked with an absolute path to a directory that contains a Cargo.toml.
    ///
    /// This source will only return the package at precisely the `path`
    /// specified, and it will be an error if there's not a package at `path`.
    pub fn new(path: &Path, id: &SourceId, config: &'cfg Config)
               -> PathSource<'cfg> {
        PathSource {
            source_id: id.clone(),
            path: path.to_path_buf(),
            updated: false,
            packages: Vec::new(),
            config: config,
            recursive: false,
        }
    }

    /// Creates a new source which is walked recursively to discover packages.
    ///
    /// This is similar to the `new` method except that instead of requiring a
    /// valid package to be present at `root` the folder is walked entirely to
    /// crawl for packages.
    ///
    /// Note that this should be used with care and likely shouldn't be chosen
    /// by default!
    pub fn new_recursive(root: &Path, id: &SourceId, config: &'cfg Config)
                         -> PathSource<'cfg> {
        PathSource {
            recursive: true,
            .. PathSource::new(root, id, config)
        }
    }

    pub fn root_package(&mut self) -> CargoResult<Package> {
        trace!("root_package; source={:?}", self);

        self.update()?;

        match self.packages.iter().find(|p| p.root() == &*self.path) {
            Some(pkg) => Ok(pkg.clone()),
            None => Err(internal("no package found in source"))
        }
    }

    pub fn read_packages(&self) -> CargoResult<Vec<Package>> {
        if self.updated {
            Ok(self.packages.clone())
        } else if self.recursive {
            ops::read_packages(&self.path, &self.source_id, self.config)
        } else {
            let path = self.path.join("Cargo.toml");
            let (pkg, _) = ops::read_package(&path, &self.source_id, self.config)?;
            Ok(vec![pkg])
        }
    }

    /// List all files relevant to building this package inside this source.
    ///
    /// This function will use the appropriate methods to determine the
    /// set of files underneath this source's directory which are relevant for
    /// building `pkg`.
    ///
    /// The basic assumption of this method is that all files in the directory
    /// are relevant for building this package, but it also contains logic to
    /// use other methods like .gitignore to filter the list of files.
    pub fn list_files(&self, pkg: &Package) -> CargoResult<Vec<PathBuf>> {
        let root = pkg.root();

        let parse = |p: &String| {
            Pattern::new(p).map_err(|e| {
                CargoError::from(format!("could not parse pattern `{}`: {}", p, e))
            })
        };

        let exclude = pkg.manifest()
                         .exclude()
                         .iter()
                         .map(|p| parse(p))
                         .collect::<Result<Vec<_>, _>>()?;

        let include = pkg.manifest()
                         .include()
                         .iter()
                         .map(|p| parse(p))
                         .collect::<Result<Vec<_>, _>>()?;

        let mut filter = |p: &Path| {
            let relative_path = util::without_prefix(p, root).unwrap();
            include.iter().any(|p| p.matches_path(relative_path)) || {
                include.is_empty() &&
                 !exclude.iter().any(|p| p.matches_path(relative_path))
            }
        };

        // attempt git-prepopulate only if no `include` (rust-lang/cargo#4135)
        if include.is_empty() {
            if let Some(result) = self.discover_git_and_list_files(pkg, root, &mut filter) {
                return result;
            }
        }

        self.list_files_walk(pkg, &mut filter)
    }

    // Returns Some(_) if found sibling Cargo.toml and .git folder;
    // otherwise caller should fall back on full file list.
    fn discover_git_and_list_files(&self,
                                   pkg: &Package,
                                   root: &Path,
                                   filter: &mut FnMut(&Path) -> bool)
                                   -> Option<CargoResult<Vec<PathBuf>>> {
        // If this package is in a git repository, then we really do want to
        // query the git repository as it takes into account items such as
        // .gitignore. We're not quite sure where the git repository is,
        // however, so we do a bit of a probe.
        //
        // We walk this package's path upwards and look for a sibling
        // Cargo.toml and .git folder. If we find one then we assume that we're
        // part of that repository.
        let mut cur = root;
        loop {
            if cur.join("Cargo.toml").is_file() {
                // If we find a git repository next to this Cargo.toml, we still
                // check to see if we are indeed part of the index. If not, then
                // this is likely an unrelated git repo, so keep going.
                if let Ok(repo) = git2::Repository::open(cur) {
                    let index = match repo.index() {
                        Ok(index) => index,
                        Err(err) => return Some(Err(err.into())),
                    };
                    let path = util::without_prefix(root, cur)
                                    .unwrap().join("Cargo.toml");
                    if index.get_path(&path, 0).is_some() {
                        return Some(self.list_files_git(pkg, repo, filter));
                    }
                }
            }
            // don't cross submodule boundaries
            if cur.join(".git").is_dir() {
                break
            }
            match cur.parent() {
                Some(parent) => cur = parent,
                None => break,
            }
        }
        return None;
    }

    fn list_files_git(&self, pkg: &Package, repo: git2::Repository,
                      filter: &mut FnMut(&Path) -> bool)
                      -> CargoResult<Vec<PathBuf>> {
        warn!("list_files_git {}", pkg.package_id());
        let index = repo.index()?;
        let root = repo.workdir().ok_or_else(|| {
            internal("Can't list files on a bare repository.")
        })?;
        let pkg_path = pkg.root();

        let mut ret = Vec::<PathBuf>::new();

        // We use information from the git repository to guide us in traversing
        // its tree. The primary purpose of this is to take advantage of the
        // .gitignore and auto-ignore files that don't matter.
        //
        // Here we're also careful to look at both tracked and untracked files as
        // the untracked files are often part of a build and may become relevant
        // as part of a future commit.
        let index_files = index.iter().map(|entry| {
            use libgit2_sys::GIT_FILEMODE_COMMIT;
            let is_dir = entry.mode == GIT_FILEMODE_COMMIT as u32;
            (join(root, &entry.path), Some(is_dir))
        });
        let mut opts = git2::StatusOptions::new();
        opts.include_untracked(true);
        if let Some(suffix) = util::without_prefix(pkg_path, root) {
            opts.pathspec(suffix);
        }
        let statuses = repo.statuses(Some(&mut opts))?;
        let untracked = statuses.iter().filter_map(|entry| {
            match entry.status() {
                git2::STATUS_WT_NEW => Some((join(root, entry.path_bytes()), None)),
                _ => None
            }
        });

        let mut subpackages_found = Vec::new();

        for (file_path, is_dir) in index_files.chain(untracked) {
            let file_path = file_path?;

            // Filter out files blatantly outside this package. This is helped a
            // bit obove via the `pathspec` function call, but we need to filter
            // the entries in the index as well.
            if !file_path.starts_with(pkg_path) {
                continue
            }

            match file_path.file_name().and_then(|s| s.to_str()) {
                // Filter out Cargo.lock and target always, we don't want to
                // package a lock file no one will ever read and we also avoid
                // build artifacts
                Some("Cargo.lock") |
                Some("target") => continue,

                // Keep track of all sub-packages found and also strip out all
                // matches we've found so far. Note, though, that if we find
                // our own `Cargo.toml` we keep going.
                Some("Cargo.toml") => {
                    let path = file_path.parent().unwrap();
                    if path != pkg_path {
                        warn!("subpackage found: {}", path.display());
                        ret.retain(|p| !p.starts_with(path));
                        subpackages_found.push(path.to_path_buf());
                        continue
                    }
                }

                _ => {}
            }

            // If this file is part of any other sub-package we've found so far,
            // skip it.
            if subpackages_found.iter().any(|p| file_path.starts_with(p)) {
                continue
            }

            if is_dir.unwrap_or_else(|| file_path.is_dir()) {
                warn!("  found submodule {}", file_path.display());
                let rel = util::without_prefix(&file_path, root).unwrap();
                let rel = rel.to_str().ok_or_else(|| {
                    CargoError::from(format!("invalid utf-8 filename: {}", rel.display()))
                })?;
                // Git submodules are currently only named through `/` path
                // separators, explicitly not `\` which windows uses. Who knew?
                let rel = rel.replace(r"\", "/");
                match repo.find_submodule(&rel).and_then(|s| s.open()) {
                    Ok(repo) => {
                        let files = self.list_files_git(pkg, repo, filter)?;
                        ret.extend(files.into_iter());
                    }
                    Err(..) => {
                        PathSource::walk(&file_path, &mut ret, false,
                                              filter)?;
                    }
                }
            } else if (*filter)(&file_path) {
                // We found a file!
                warn!("  found {}", file_path.display());
                ret.push(file_path);
            }
        }
        return Ok(ret);

        #[cfg(unix)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::os::unix::prelude::*;
            use std::ffi::OsStr;
            Ok(path.join(<OsStr as OsStrExt>::from_bytes(data)))
        }
        #[cfg(windows)]
        fn join(path: &Path, data: &[u8]) -> CargoResult<PathBuf> {
            use std::str;
            match str::from_utf8(data) {
                Ok(s) => Ok(path.join(s)),
                Err(..) => Err(internal("cannot process path in git with a non \
                                         unicode filename")),
            }
        }
    }

    fn list_files_walk(&self, pkg: &Package, filter: &mut FnMut(&Path) -> bool)
                       -> CargoResult<Vec<PathBuf>> {
        let mut ret = Vec::new();
        PathSource::walk(pkg.root(), &mut ret, true, filter)?;
        Ok(ret)
    }

    fn walk(path: &Path, ret: &mut Vec<PathBuf>,
            is_root: bool, filter: &mut FnMut(&Path) -> bool) -> CargoResult<()>
    {
        if !fs::metadata(&path).map(|m| m.is_dir()).unwrap_or(false) {
            if (*filter)(path) {
                ret.push(path.to_path_buf());
            }
            return Ok(())
        }
        // Don't recurse into any sub-packages that we have
        if !is_root && fs::metadata(&path.join("Cargo.toml")).is_ok() {
            return Ok(())
        }
        for dir in fs::read_dir(path)? {
            let dir = dir?.path();
            let name = dir.file_name().and_then(|s| s.to_str());
            // Skip dotfile directories
            if name.map(|s| s.starts_with('.')) == Some(true) {
                continue
            } else if is_root {
                // Skip cargo artifacts
                match name {
                    Some("target") | Some("Cargo.lock") => continue,
                    _ => {}
                }
            }
            PathSource::walk(&dir, ret, false, filter)?;
        }
        Ok(())
    }
}

impl<'cfg> Debug for PathSource<'cfg> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "the paths source")
    }
}

impl<'cfg> Registry for PathSource<'cfg> {
    fn query(&mut self,
             dep: &Dependency,
             f: &mut FnMut(Summary)) -> CargoResult<()> {
        for s in self.packages.iter().map(|p| p.summary()) {
            if dep.matches(s) {
                f(s.clone())
            }
        }
        Ok(())
    }
}

impl<'cfg> Source for PathSource<'cfg> {
    fn source_id(&self) -> &SourceId {
        &self.source_id
    }

    fn update(&mut self) -> CargoResult<()> {
        if !self.updated {
            let packages = self.read_packages()?;
            self.packages.extend(packages.into_iter());
            self.updated = true;
        }

        Ok(())
    }

    fn download(&mut self, id: &PackageId) -> CargoResult<Package> {
        trace!("getting packages; id={}", id);

        let pkg = self.packages.iter().find(|pkg| pkg.package_id() == id);
        pkg.cloned().ok_or_else(|| {
            internal(format!("failed to find {} in path source", id))
        })
    }

    fn fingerprint(&self, pkg: &Package) -> CargoResult<String> {
        if !self.updated {
            return Err(internal("BUG: source was not updated"));
        }

        let mut max = FileTime::zero();
        let mut max_path = PathBuf::from("");
        for file in self.list_files(pkg)? {
            // An fs::stat error here is either because path is a
            // broken symlink, a permissions error, or a race
            // condition where this path was rm'ed - either way,
            // we can ignore the error and treat the path's mtime
            // as 0.
            let mtime = fs::metadata(&file).map(|meta| {
                FileTime::from_last_modification_time(&meta)
            }).unwrap_or(FileTime::zero());
            warn!("{} {}", mtime, file.display());
            if mtime > max {
                max = mtime;
                max_path = file;
            }
        }
        trace!("fingerprint {}: {}", self.path.display(), max);
        Ok(format!("{} ({})", max, max_path.display()))
    }
}
<|endoftext|>


<commit_before>fn main() {
    let some_vec = vec![1, 2, 3];
    let some_vec_iter = some_vec.iter();

    for val in some_vec_iter {
        println!("Value: {}", val);
    }
}
<commit_msg>Example of using map<commit_after>fn main() {
    let some_vec = vec![1, 2, 3];
    let some_vec_iter = some_vec.iter();

    for val in some_vec_iter {
        println!("Value: {}", val);
    }

    let some_other_vec: Vec<_> = some_vec.iter().map(|x| x + 1).collect();
    println!("{:?}", some_other_vec);
}
<|endoftext|>


<commit_before>/// TextStyle holds the displayed attributes flags.
#[derive(Debug, Default, Hash, Clone, Copy, Eq, PartialEq)]
pub struct TextStyle {
    /// The character should blink.
    pub is_blinking: bool,
    /// The character is a part of a selection.
    pub is_selected: bool,
    /// The fg/bg attributes are reversed (used to simulate cursors/marks).
    pub is_inverse: bool,
    /// The character should be displayed as bold.
    pub is_bold: bool,
    /// The character should be displayed in italic.
    pub is_italic: bool,
    /// rbg tuple for foreground color
    pub color: (u8, u8, u8), // RGB
    /// rbg tuple for background color
    pub bg_color: (u8, u8, u8), // RGB
}

impl TextStyle {
    pub fn new() -> Self {
        TextStyle {
            is_blinking: false,
            is_selected: false,
            is_inverse: false,
            is_bold: false,
            is_italic: false,
            color: Self::default_color(),
            bg_color: Self::default_bg_color(),
        }
    }

    pub fn default_color() -> (u8, u8, u8) {
        // (192, 192, 192) // White
        // (128, 128, 128)    // Gray
        //(177, 177, 177)

        // (160, 160, 160)

        (180, 180, 180)
    }

    pub fn default_bg_color() -> (u8, u8, u8) {
        // (30, 34, 39)
        //(45, 55, 67)
        //(40, 44, 49)

        (39, 40, 54)
    }

    pub fn title_color() -> (u8, u8, u8) {
        Self::default_bg_color()
    }

    pub fn title_bg_color() -> (u8, u8, u8) {
        // (131, 165, 152)
        Self::default_color()
    }

    pub fn default_selected_bg_color() -> (u8, u8, u8) {
        let sbg = Self::default_bg_color();
        let add = 25;
        (sbg.0 + add, sbg.1 + add, sbg.2 + add)
    }

    pub fn default_mark_line_bg_color() -> (u8, u8, u8) {
        let sbg = Self::default_bg_color();
        let add = 5;
        (sbg.0 + add, sbg.1 + add, sbg.2 + add)
    }
}

/// A CodepointInfo contains displayed character attributes.

/// The displayed screen is composed of LineCell(s), that contain CodepointInfo(s).
#[derive(Hash, Default, Debug, Clone, Copy, Eq, PartialEq)]
pub struct CodepointInfo {
    /// Ignore this CodepointInfo if set to false.
    pub used: bool, // rename into ignore ?
    /// The CodepointInfo is some part metadata.

    /// When set to true offset must be set to None
    pub metadata: bool, // TODO(ceg): prefer enum use enum { Normal, Eof, ... }
    /// The real codepoint found on storage
    pub cp: char,
    /// The codepoint to display
    pub displayed_cp: char,
    /// Storage offset
    pub offset: Option<u64>,
    /// Storage size in bytes
    pub size: usize,
    /// Hints for render
    pub skip_render: bool,
    /// Style to apply when rendering
    pub style: TextStyle,
}

impl CodepointInfo {
    pub fn new() -> Self {
        CodepointInfo {
            used: false,
            metadata: true,
            cp: ' ',
            displayed_cp: ' ',
            offset: None,
            size: 0,
            skip_render: false,
            //
            style: TextStyle::new(),
        }
    }
}

/*
Logical Color	Terminal Color	RGB Value Used by SGD
Black	0 0 0
Light_red	255 0 0
Light_green	0 255 0
Yellow	255 255 0
Light_blue	0 0 255
Light_magenta	255 0 255
Light_cyan	    0 255 255
High_white	    255 255 255
Gray	    128 128 128
Red	    128 0 0
Green	0 128 0
Brown	128 128 0
Blue	0 0 128
Magenta	128 0 128
Cyan	0 128 128
White	192 192 192
*/
<commit_msg>update default_mark_line_bg_color() : bg += {5 -> 10}<commit_after>/// TextStyle holds the displayed attributes flags.
#[derive(Debug, Default, Hash, Clone, Copy, Eq, PartialEq)]
pub struct TextStyle {
    /// The character should blink.
    pub is_blinking: bool,
    /// The character is a part of a selection.
    pub is_selected: bool,
    /// The fg/bg attributes are reversed (used to simulate cursors/marks).
    pub is_inverse: bool,
    /// The character should be displayed as bold.
    pub is_bold: bool,
    /// The character should be displayed in italic.
    pub is_italic: bool,
    /// rbg tuple for foreground color
    pub color: (u8, u8, u8), // RGB
    /// rbg tuple for background color
    pub bg_color: (u8, u8, u8), // RGB
}

impl TextStyle {
    pub fn new() -> Self {
        TextStyle {
            is_blinking: false,
            is_selected: false,
            is_inverse: false,
            is_bold: false,
            is_italic: false,
            color: Self::default_color(),
            bg_color: Self::default_bg_color(),
        }
    }

    pub fn default_color() -> (u8, u8, u8) {
        // (192, 192, 192) // White
        // (128, 128, 128)    // Gray
        //(177, 177, 177)

        // (160, 160, 160)

        (180, 180, 180)
    }

    pub fn default_bg_color() -> (u8, u8, u8) {
        // (30, 34, 39)
        //(45, 55, 67)
        //(40, 44, 49)

        (39, 40, 54)
    }

    pub fn title_color() -> (u8, u8, u8) {
        Self::default_bg_color()
    }

    pub fn title_bg_color() -> (u8, u8, u8) {
        // (131, 165, 152)
        Self::default_color()
    }

    pub fn default_selected_bg_color() -> (u8, u8, u8) {
        let sbg = Self::default_bg_color();
        let add = 25;
        (sbg.0 + add, sbg.1 + add, sbg.2 + add)
    }

    pub fn default_mark_line_bg_color() -> (u8, u8, u8) {
        let sbg = Self::default_bg_color();

        let add = 10;
        (
            sbg.0.saturating_add(add),
            sbg.1.saturating_add(add),
            sbg.2.saturating_add(add),
        )

        // let sub = 7;
        // (sbg.0.saturating_sub(sub), sbg.1.saturating_sub(sub), sbg.2.saturating_sub(sub))
    }
}

/// A CodepointInfo contains displayed character attributes.

/// The displayed screen is composed of LineCell(s), that contain CodepointInfo(s).
#[derive(Hash, Default, Debug, Clone, Copy, Eq, PartialEq)]
pub struct CodepointInfo {
    /// Ignore this CodepointInfo if set to false.
    pub used: bool, // rename into ignore ?
    /// The CodepointInfo is some part metadata.

    /// When set to true offset must be set to None
    pub metadata: bool, // TODO(ceg): prefer enum use enum { Normal, Eof, ... }
    /// The real codepoint found on storage
    pub cp: char,
    /// The codepoint to display
    pub displayed_cp: char,
    /// Storage offset
    pub offset: Option<u64>,
    /// Storage size in bytes
    pub size: usize,
    /// Hints for render
    pub skip_render: bool,
    /// Style to apply when rendering
    pub style: TextStyle,
}

impl CodepointInfo {
    pub fn new() -> Self {
        CodepointInfo {
            used: false,
            metadata: true,
            cp: ' ',
            displayed_cp: ' ',
            offset: None,
            size: 0,
            skip_render: false,
            //
            style: TextStyle::new(),
        }
    }
}

/*
Logical Color	Terminal Color	RGB Value Used by SGD
Black	0 0 0
Light_red	255 0 0
Light_green	0 255 0
Yellow	255 255 0
Light_blue	0 0 255
Light_magenta	255 0 255
Light_cyan	    0 255 255
High_white	    255 255 255
Gray	    128 128 128
Red	    128 0 0
Green	0 128 0
Brown	128 128 0
Blue	0 0 128
Magenta	128 0 128
Cyan	0 128 128
White	192 192 192
*/
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::error::Error as StdError;
use std::fmt;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use base::ast;
use vm::{VM, filename_to_module, load_script};
use base::macros::{Macro, Error as MacroError};
use base::types::TcIdent;

#[derive(Debug)]
pub enum Error {
    CyclicDependency(String),
    String(&'static str),
    Other(Box<StdError>),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::CyclicDependency(ref s) => {
                write!(f, "Module '{}' occurs in a cyclic dependency", s)
            }
            Error::String(s) => s.fmt(f),
            Error::Other(ref e) => e.fmt(f),
        }
    }
}

fn error<E: StdError + 'static>(e: E) -> Error {
    Error::Other(Box::new(e))
}

impl StdError for Error {
    fn description(&self) -> &str {
        "import error"
    }
}

pub struct Import {
    visited: RefCell<Vec<String>>,
    paths: RefCell<Vec<PathBuf>>,
}

impl Import {
    pub fn new() -> Import {
        Import {
            visited: RefCell::new(Vec::new()),
            paths: RefCell::new(vec![PathBuf::from(".")]),
        }
    }

    pub fn add_path<P: Into<PathBuf>>(&self, path: P) {
        self.paths.borrow_mut().push(path.into());
    }
}

impl<'a> Macro<VM<'a>> for Import {
    fn expand(&self,
              vm: &VM<'a>,
              arguments: &mut [ast::LExpr<TcIdent>])
              -> Result<ast::LExpr<TcIdent>, MacroError> {
        if arguments.len() != 1 {
            return Err(Error::String("Expected import to get 1 argument").into());
        }
        match *arguments[0] {
            ast::Expr::Literal(ast::LiteralEnum::String(ref filename)) => {
                let modulename = filename_to_module(filename);
                let path = Path::new(&filename[..]);
                // Only load the script if it is not already loaded
                let name = vm.symbol(&*modulename);
                debug!("Import '{}' {:?}", modulename, self.visited);
                if !vm.global_exists(&modulename) {
                    if self.visited.borrow().iter().any(|m| **m == **filename) {
                        return Err(Error::CyclicDependency(filename.clone()).into());
                    }
                    self.visited.borrow_mut().push(filename.clone());
                    let file = self.paths
                                   .borrow()
                                   .iter()
                                   .filter_map(|p| {
                                       let mut base = p.clone();
                                       base.push(path);
                                       match File::open(&base) {
                                           Ok(file) => Some(file),
                                           Err(_) => None,
                                       }
                                   })
                                   .next();
                    let mut file = try!(file.ok_or_else(|| Error::String("Could not find file")));
                    let mut buffer = String::new();
                    try!(file.read_to_string(&mut buffer).map_err(error));
                    try!(load_script(vm, &modulename, &buffer));
                    self.visited.borrow_mut().pop();
                }
                // FIXME Does not handle shadowing
                Ok(ast::located(arguments[0].location,
                                ast::Expr::Identifier(TcIdent::new(name))))
            }
            _ => return Err(Error::String("Expected a string literal to import").into()),
        }
    }
}
<commit_msg>docs(vm::import): Document the import macro<commit_after>use std::cell::RefCell;
use std::error::Error as StdError;
use std::fs::File;
use std::io;
use std::io::Read;
use std::path::{Path, PathBuf};

use base::ast;
use vm::{VM, filename_to_module, load_script};
use base::macros::{Macro, Error as MacroError};
use base::types::TcIdent;

quick_error! {
    /// Error type for the import macro
    #[derive(Debug)]
    pub enum Error {
        /// The importer found a cyclic dependency when loading files
        CyclicDependency(module: String) {
            description("Cyclic dependency")
            display("Module '{}' occurs in a cyclic dependency", module)
        }
        /// Generic message error
        String(message: &'static str) {
            description(message)
            display("{}", message)
        }
        /// The importer could not load the imported file
        IO(err: io::Error) {
            description(err.description())
            display("{}", err)
            from()
        }
    }
}

/// Macro which rewrites occurances of `import "filename"` to a load of that file if it is not
/// already loaded and then a global access to the loaded module
pub struct Import {
    visited: RefCell<Vec<String>>,
    paths: RefCell<Vec<PathBuf>>,
}

impl Import {
    /// Creates a new import macro
    pub fn new() -> Import {
        Import {
            visited: RefCell::new(Vec::new()),
            paths: RefCell::new(vec![PathBuf::from(".")]),
        }
    }

    /// Adds a path to the list of paths which the importer uses to find files
    pub fn add_path<P: Into<PathBuf>>(&self, path: P) {
        self.paths.borrow_mut().push(path.into());
    }
}

impl<'a> Macro<VM<'a>> for Import {
    fn expand(&self,
              vm: &VM<'a>,
              arguments: &mut [ast::LExpr<TcIdent>])
              -> Result<ast::LExpr<TcIdent>, MacroError> {
        if arguments.len() != 1 {
            return Err(Error::String("Expected import to get 1 argument").into());
        }
        match *arguments[0] {
            ast::Expr::Literal(ast::LiteralEnum::String(ref filename)) => {
                let modulename = filename_to_module(filename);
                let path = Path::new(&filename[..]);
                // Only load the script if it is not already loaded
                let name = vm.symbol(&*modulename);
                debug!("Import '{}' {:?}", modulename, self.visited);
                if !vm.global_exists(&modulename) {
                    if self.visited.borrow().iter().any(|m| **m == **filename) {
                        return Err(Error::CyclicDependency(filename.clone()).into());
                    }
                    self.visited.borrow_mut().push(filename.clone());
                    let file = self.paths
                                   .borrow()
                                   .iter()
                                   .filter_map(|p| {
                                       let mut base = p.clone();
                                       base.push(path);
                                       match File::open(&base) {
                                           Ok(file) => Some(file),
                                           Err(_) => None,
                                       }
                                   })
                                   .next();
                    let mut file = try!(file.ok_or_else(|| Error::String("Could not find file")));
                    let mut buffer = String::new();
                    try!(file.read_to_string(&mut buffer));
                    try!(load_script(vm, &modulename, &buffer));
                    self.visited.borrow_mut().pop();
                }
                // FIXME Does not handle shadowing
                Ok(ast::located(arguments[0].location,
                                ast::Expr::Identifier(TcIdent::new(name))))
            }
            _ => return Err(Error::String("Expected a string literal to import").into()),
        }
    }
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::PointerAddress;
use graphql_ir::{Program, Selection, Transformed, Transformer};
use std::collections::HashMap;

type Seen = HashMap<PointerAddress, Transformed<Selection>>;

///
/// Sorts selections in the fragments and queries (and their selections)
///
pub fn sort_selections<'s>(program: &'s Program<'s>) -> Program<'s> {
    let mut transform = SortSelectionsTransform::new();
    transform
        .transform_program(program)
        .unwrap_or_else(|| program.clone())
}

#[derive(Default)]
struct SortSelectionsTransform {
    seen: Seen,
}

impl SortSelectionsTransform {
    pub fn new() -> Self {
        Default::default()
    }
}

impl Transformer for SortSelectionsTransform {
    const NAME: &'static str = "SortSelectionsTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        let mut next_selections = self
            .transform_list(selections, Self::transform_selection)
            .unwrap_or_else(|| selections.to_vec());
        next_selections.sort_unstable();
        Some(next_selections)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::InlineFragment(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_inline_fragment(selection)
                    .map(Selection::InlineFragment);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::LinkedField(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_linked_field(selection)
                    .map(Selection::LinkedField);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            _ => Transformed::Keep,
        }
    }
}
<commit_msg>Ensure sort_selection sorts all nodes<commit_after>/*
 * Copyright (c) Facebook, Inc. and its affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

use crate::util::PointerAddress;
use graphql_ir::{Program, Selection, Transformed, Transformer};
use std::collections::HashMap;

type Seen = HashMap<PointerAddress, Transformed<Selection>>;

///
/// Sorts selections in the fragments and queries (and their selections)
///
pub fn sort_selections<'s>(program: &'s Program<'s>) -> Program<'s> {
    let mut transform = SortSelectionsTransform::new();
    transform
        .transform_program(program)
        .unwrap_or_else(|| program.clone())
}

#[derive(Default)]
struct SortSelectionsTransform {
    seen: Seen,
}

impl SortSelectionsTransform {
    pub fn new() -> Self {
        Default::default()
    }
}

impl Transformer for SortSelectionsTransform {
    const NAME: &'static str = "SortSelectionsTransform";
    const VISIT_ARGUMENTS: bool = false;
    const VISIT_DIRECTIVES: bool = false;

    fn transform_selections(&mut self, selections: &[Selection]) -> Option<Vec<Selection>> {
        let mut next_selections = self
            .transform_list(selections, Self::transform_selection)
            .unwrap_or_else(|| selections.to_vec());
        next_selections.sort_unstable();
        Some(next_selections)
    }

    fn transform_selection(&mut self, selection: &Selection) -> Transformed<Selection> {
        match selection {
            Selection::InlineFragment(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_inline_fragment(selection)
                    .map(Selection::InlineFragment);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::LinkedField(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_linked_field(selection)
                    .map(Selection::LinkedField);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            Selection::Condition(selection) => {
                let key = PointerAddress::new(selection);
                if let Some(prev) = self.seen.get(&key) {
                    return prev.clone();
                }
                let transformed = self
                    .transform_condition(selection)
                    .map(Selection::Condition);
                self.seen.insert(key, transformed.clone());
                transformed
            }
            _ => Transformed::Keep,
        }
    }
}
<|endoftext|>


<commit_before>use std::iter::FromIterator;

mod optimize_state;
use self::optimize_state::OptimizeState;

use token::Token;
use token::Token::*;
use IsbfcIR;

fn _optimize(tokens: &Vec<Token>) -> OptimizeState {
    let mut do_output = false;
    let mut state = OptimizeState::default();

    for token in tokens {
        match *token {
            Set(..) | Add(..) | Move(_) | LoadOut(..) | LoadOutSet(_) | Output | MulCopy(..) => {}
            _ => {
                if do_output {
                    state.tokens.push(Output);
                    do_output = false;
                }

                state.apply_adds_sets();
            }
        }

        match *token {
            Loop(_) | Input | Scan(_) => {
                state.apply_shift();
            }
            _ => {}
        }

        match *token {
            Set(mut offset, value) => {
                offset += state.shift;
                state.set(offset, value);
            }
            Add(mut offset, value) => {
                offset += state.shift;
                state.add(offset, value);
            }
            MulCopy(mut src, mut dest, mul) => {
                src += state.shift;
                dest += state.shift;
                if let Some(value) = state.sets.get(&src).cloned() {
                    state.add(dest, value * mul);
                } else {
                    if state.sets.contains_key(&dest) || state.adds.contains_key(&src) ||
                       state.adds.contains_key(&dest) {
                        state.apply_adds_sets();
                    }
                    state.tokens.push(MulCopy(src, dest, mul));
                }
            }
            If(offset, ref contents) => {
                let mut newcontents = Vec::new();
                for i in contents {
                    newcontents.push(match *i {
                        Set(offset, value) => Set(offset + state.shift, value),
                        MulCopy(src, dest, mul) => {
                            MulCopy(src + state.shift, dest + state.shift, mul)
                        }
                        _ => unreachable!(),
                    });
                }
                state.tokens.push(If(offset + state.shift, newcontents));
            }
            Move(offset) => state.shift += offset,
            Output => do_output = true,
            LoadOut(mut offset, add) => {
                offset += state.shift;
                state.tokens.push(if state.sets.contains_key(&offset) {
                    LoadOutSet(state.sets.get(&offset).unwrap() + add)
                } else {
                    LoadOut(offset, state.adds.get(&offset).unwrap_or(&0) + add)
                });
            }
            Loop(ref contents) => _optimize_loop(contents, &mut state),
            LoadOutSet(value) => state.tokens.push(LoadOutSet(value)),
            Input => state.tokens.push(Input),
            Scan(offset) => state.tokens.push(Scan(offset + state.shift)),
        }
    }

    if do_output {
        state.tokens.push(Output);
    }

    state
}

fn _optimize_loop(tokens: &Vec<Token>, outer: &mut OptimizeState) {
    let mut inner = _optimize(tokens);

    if inner.shift != 0 && inner.sets.is_empty() && inner.adds.is_empty() &&
       inner.tokens.is_empty() {
        outer.tokens.push(Scan(inner.shift));
    } else if inner.shift == 0 && inner.tokens.is_empty() && inner.adds.contains_key(&0) &&
              inner.adds.len() == 1 {
        if !inner.sets.is_empty() {
            let mut iftokens = Vec::new();
            for (offset, value) in &inner.sets {
                iftokens.push(Set(*offset, *value));
            }
            iftokens.push(Set(0, 0));
            outer.tokens.push(If(0, iftokens));
        } else {
            outer.set(0, 0);
        }
    } else if inner.shift == 0 && inner.tokens.is_empty() && inner.adds.get(&0) == Some(&-1) {
        let contents = inner.adds.iter().filter_map(|(offset, value)| {
            if *offset != 0 {
                let src = 0;
                let dest = *offset;
                let mul = *value;
                Some(MulCopy(src, dest, mul))
            } else {
                None
            }
        });

        if !inner.sets.is_empty() {
            let iftokens = Vec::from_iter(inner.sets
                .iter()
                .map(|(offset, value)| Set(*offset, *value))
                .chain(contents));
            outer.tokens.push(If(0, iftokens));
        } else {
            outer.tokens.extend(contents);
        }

        outer.set(0, 0);
    } else {
        inner.apply_adds_sets();
        inner.apply_shift();

        outer.tokens.push(Loop(inner.tokens));
    }
}

impl IsbfcIR {
    /// Returns an optimized version of the intermediate representation
    pub fn optimize(&self) -> IsbfcIR {
        // Ignore sets/adds/shifts at end of file
        let mut oldtokens =_optimize(&self.tokens).tokens;
        let mut newtokens =_optimize(&oldtokens).tokens;
        while newtokens != oldtokens {
            oldtokens = newtokens;
            newtokens = _optimize(&oldtokens).tokens;
        }
        IsbfcIR{tokens: newtokens}
    }
}
<commit_msg>Style changes<commit_after>use std::iter::FromIterator;

mod optimize_state;
use self::optimize_state::OptimizeState;

use token::Token;
use token::Token::*;
use IsbfcIR;

fn _optimize(tokens: &Vec<Token>) -> OptimizeState {
    let mut do_output = false;
    let mut state = OptimizeState::default();

    for token in tokens {
        match *token {
            Set(..) | Add(..) | Move(_) | LoadOut(..) | LoadOutSet(_) | Output | MulCopy(..) => {}
            _ => {
                if do_output {
                    state.tokens.push(Output);
                    do_output = false;
                }

                state.apply_adds_sets();
            }
        }

        match *token {
            Loop(_) | Input | Scan(_) => {
                state.apply_shift();
            }
            _ => {}
        }

        match *token {
            Set(mut offset, value) => {
                offset += state.shift;
                state.set(offset, value);
            }
            Add(mut offset, value) => {
                offset += state.shift;
                state.add(offset, value);
            }
            MulCopy(mut src, mut dest, mul) => {
                src += state.shift;
                dest += state.shift;
                if let Some(value) = state.sets.get(&src).cloned() {
                    state.add(dest, value * mul);
                } else {
                    if state.sets.contains_key(&dest) || state.adds.contains_key(&src) ||
                       state.adds.contains_key(&dest) {
                        state.apply_adds_sets();
                    }
                    state.tokens.push(MulCopy(src, dest, mul));
                }
            }
            If(offset, ref contents) => {
                let mut newcontents = Vec::new();
                for i in contents {
                    newcontents.push(match *i {
                        Set(offset, value) => Set(offset + state.shift, value),
                        MulCopy(src, dest, mul) => {
                            MulCopy(src + state.shift, dest + state.shift, mul)
                        }
                        _ => unreachable!(),
                    });
                }
                state.tokens.push(If(offset + state.shift, newcontents));
            }
            Move(offset) => state.shift += offset,
            Output => do_output = true,
            LoadOut(mut offset, add) => {
                offset += state.shift;
                state.tokens.push(if let Some(set) = state.sets.get_mut(&offset) {
                    LoadOutSet(*set + add)
                } else {
                    LoadOut(offset, state.adds.get(&offset).unwrap_or(&0) + add)
                });
            }
            Loop(ref contents) => _optimize_loop(contents, &mut state),
            LoadOutSet(value) => state.tokens.push(LoadOutSet(value)),
            Input => state.tokens.push(Input),
            Scan(offset) => state.tokens.push(Scan(offset + state.shift)),
        }
    }

    if do_output {
        state.tokens.push(Output);
    }

    state
}

fn _optimize_loop(tokens: &Vec<Token>, outer: &mut OptimizeState) {
    let mut inner = _optimize(tokens);

    if inner.shift != 0 && inner.sets.is_empty() && inner.adds.is_empty() &&
       inner.tokens.is_empty() {
        outer.tokens.push(Scan(inner.shift));
    } else if inner.shift == 0 && inner.tokens.is_empty() && inner.adds.contains_key(&0) &&
              inner.adds.len() == 1 {
        if !inner.sets.is_empty() {
            let mut iftokens = Vec::new();
            for (offset, value) in &inner.sets {
                iftokens.push(Set(*offset, *value));
            }
            iftokens.push(Set(0, 0));
            outer.tokens.push(If(0, iftokens));
        } else {
            outer.set(0, 0);
        }
    } else if inner.shift == 0 && inner.tokens.is_empty() && inner.adds.get(&0) == Some(&-1) {
        let contents = inner.adds.iter().filter_map(|(offset, value)| {
            if *offset != 0 {
                Some(MulCopy(0, *offset, *value))
            } else {
                None
            }
        });

        if !inner.sets.is_empty() {
            let iftokens = Vec::from_iter(inner.sets
                .iter()
                .map(|(offset, value)| Set(*offset, *value))
                .chain(contents));
            outer.tokens.push(If(0, iftokens));
        } else {
            outer.tokens.extend(contents);
        }

        outer.set(0, 0);
    } else {
        inner.apply_adds_sets();
        inner.apply_shift();

        outer.tokens.push(Loop(inner.tokens));
    }
}

impl IsbfcIR {
    /// Returns an optimized version of the intermediate representation
    pub fn optimize(&self) -> IsbfcIR {
        // Ignore sets/adds/shifts at end of file
        let mut oldtokens =_optimize(&self.tokens).tokens;
        let mut newtokens =_optimize(&oldtokens).tokens;
        while newtokens != oldtokens {
            oldtokens = newtokens;
            newtokens = _optimize(&oldtokens).tokens;
        }
        IsbfcIR{tokens: newtokens}
    }
}
<|endoftext|>


<commit_before>bitflags! {
    pub struct Flags : u8 {
        const BACKSL = 1;
        const SQUOTE = 2;
        const DQUOTE = 4;
        const TRIM   = 8;
    }
}

pub struct QuoteTerminator {
    buffer: String,
    eof:    Option<String>,
    eof_buffer: String,
    read:   usize,
    flags:  Flags,
}

impl QuoteTerminator {
    pub fn new(input: String) -> QuoteTerminator {
        QuoteTerminator { buffer: input, eof: None, eof_buffer: String::new(), read: 0, flags: Flags::empty() }
    }

    pub fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut eof_found = false;
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            _ if self.flags.contains(BACKSL) => self.flags ^= BACKSL,
                            b'\\'                            => self.flags ^= BACKSL,
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"'  if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        use std::str;
                                        let eof_phrase = unsafe { str::from_utf8_unchecked(&as_bytes[self.read..]) };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        eof_found = true;
                                        break
                                    }
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if eof_found {
                    self.buffer.push('\n');
                    return false
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE) {
                self.read += 1;
                self.buffer.push('\n');
                false
            } else {
                match self.buffer.bytes().last() {
                    Some(b'\\') => {
                        let _ = self.buffer.pop();
                        self.read -= 1;
                        self.flags |= TRIM;
                        false
                    },
                    Some(b'|') | Some(b'&') => {
                        false
                    }
                    _ => true
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub fn consume(self) -> String { self.buffer }
}
<commit_msg>Fix QuoteTerminator Parsing of Background Operator<commit_after>bitflags! {
    pub struct Flags : u8 {
        const BACKSL = 1;
        const SQUOTE = 2;
        const DQUOTE = 4;
        const TRIM   = 8;
    }
}

pub struct QuoteTerminator {
    buffer: String,
    eof:    Option<String>,
    eof_buffer: String,
    read:   usize,
    flags:  Flags,
}

impl QuoteTerminator {
    pub fn new(input: String) -> QuoteTerminator {
        QuoteTerminator { buffer: input, eof: None, eof_buffer: String::new(), read: 0, flags: Flags::empty() }
    }

    pub fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut eof_found = false;
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            _ if self.flags.contains(BACKSL) => self.flags ^= BACKSL,
                            b'\\'                            => self.flags ^= BACKSL,
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"'  if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        use std::str;
                                        let eof_phrase = unsafe { str::from_utf8_unchecked(&as_bytes[self.read..]) };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        eof_found = true;
                                        break
                                    }
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if eof_found {
                    self.buffer.push('\n');
                    return false
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE) {
                self.read += 1;
                self.buffer.push('\n');
                false
            } else {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                    false
                } else {
                    // If the last two bytes are either '&&' or '||', we aren't terminated yet.
                    let bytes = self.buffer.as_bytes();
                    if bytes.len() >= 2 {
                        let bytes = &bytes[bytes.len()-2..];
                        bytes != &[b'&', b'&'] && bytes != &[b'|', b'|']
                    } else {
                        false
                    }
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub fn consume(self) -> String { self.buffer }
}
<|endoftext|>


<commit_before><commit_msg>bin: add type dumps<commit_after><|endoftext|>