<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 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 = repo.index()?;
                    let path = util::without_prefix(root, cur)
                                    .unwrap().join("Cargo.toml");
                    if index.get_path(&path, 0).is_some() {
                        return self.list_files_git(pkg, repo, &mut filter);
                    }
                }
            }
            // don't cross submodule boundaries
            if cur.join(".git").is_dir() {
                break
            }
            match cur.parent() {
                Some(parent) => cur = parent,
                None => break,
            }
        }
        self.list_files_walk(pkg, &mut filter)
    }

    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>Factor search for sibling Cargo.toml and .git folder into separate subroutine.<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))
            }
        };

        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>#[macro_use]
extern crate helix;
extern crate rand;

use rand::random;

const SECONDS_PER_MINUTE: i64 = 60;
const SECONDS_PER_HOUR:   i64 = 3600;
const SECONDS_PER_DAY:    i64 = 86400;
const SECONDS_PER_WEEK:   i64 = 604800;
const SECONDS_PER_MONTH:  i64 = 2629746; // 1/12 of a gregorian year
const SECONDS_PER_YEAR:   i64 = 31556952; // length of a gregorian year (365.2425 days)

declare_types! {
    class Duration {
        struct {
            seconds: Option<i32>,
            minutes: Option<i32>,
            hours:   Option<i32>,
            days:    Option<i32>,
            weeks:   Option<i32>,
            months:  Option<i32>,
            years:   Option<i32>,
            value:   i64,
        }

        def initialize(helix: helix::Metadata) {
            Duration {
                helix:   helix,
                seconds: Some(0),
                minutes: None,
                hours:   None,
                days:    None,
                weeks:   None,
                months:  None,
                years:   None,
                value:   0,
            }
        }

        def value(&self) -> /* i64 */ String {
            format!("{}", self.value)
        }

        def plus(&mut self, other: &Duration) {
            self.seconds = sum_part(self.seconds, other.seconds);
            self.minutes = sum_part(self.minutes, other.minutes);
            self.hours = sum_part(self.hours, other.hours);
            self.days = sum_part(self.days, other.days);
            self.weeks = sum_part(self.weeks, other.weeks);
            self.months = sum_part(self.months, other.months);
            self.years = sum_part(self.years, other.years);

            self.value += other.value;
        }

        def negate(&mut self) {

            self.seconds = negate_part(self.seconds);
            self.minutes = negate_part(self.minutes);
            self.hours = negate_part(self.hours);
            self.days = negate_part(self.days);
            self.weeks = negate_part(self.weeks);
            self.months = negate_part(self.months);
            self.years = negate_part(self.years);

            self.value *= -1;
        }

        def randomize(&mut self) {
            self.seconds = randomize_part();
            self.minutes = randomize_part();
            self.hours = randomize_part();
            self.days = randomize_part();
            self.weeks = randomize_part();
            self.months = randomize_part();
            self.years = randomize_part();

            self.value = compute_value(&self);
        }

        def inspect(&self) -> String {
            let mut parts = Vec::new();

            format_part(&mut parts, self.years, "year", "years");
            format_part(&mut parts, self.months, "month", "months");
            format_part(&mut parts, self.weeks, "week", "weeks");
            format_part(&mut parts, self.days, "day", "days");
            format_part(&mut parts, self.hours, "hour", "hours");
            format_part(&mut parts, self.minutes, "minute", "minutes");
            format_part(&mut parts, self.seconds, "second", "seconds");

            to_sentence(parts)
        }
    }
}

fn format_part(parts: &mut Vec<String>, value: Option<i32>, singular: &str, plural: &str) {
    if let Some(v) = value {
        parts.push(format!("{} {}", v, if v == 1 { singular } else { plural }));
    }
}

// TODO: over/under flow bug
fn sum_part(lhs: Option<i32>, rhs: Option<i32>) -> Option<i32> {
    match lhs {
        Some(lval) => {
            match rhs {
                Some(rval) => Some(lval + rval),
                None => Some(lval)
            }
        },
        None => rhs
    }
}

fn negate_part(part: Option<i32>) -> Option<i32> {
    match part {
        Some(value) => Some(-value),
        None => None
    }
}

fn randomize_part() -> Option<i32> {
    if random() {
        Some(random::<i8>() as i32)
    } else {
        None
    }
}

fn compute_part_value(part: Option<i32>, unit: i64) -> i64 {
    match part {
        Some(value) => value as i64 * unit,
        None => 0
    }
}

fn compute_value(duration: &Duration) -> i64 {
    let mut value = 0;

    value += compute_part_value(duration.seconds, 1);
    value += compute_part_value(duration.minutes, SECONDS_PER_MINUTE);
    value += compute_part_value(duration.hours, SECONDS_PER_HOUR);
    value += compute_part_value(duration.days, SECONDS_PER_DAY);
    value += compute_part_value(duration.weeks, SECONDS_PER_WEEK);
    value += compute_part_value(duration.months, SECONDS_PER_MONTH);
    value += compute_part_value(duration.years, SECONDS_PER_YEAR);

    value
}

fn to_sentence(mut parts: Vec<String>) -> String {
    match parts.len() {
        0 => "".to_string(),
        1 => parts.pop().unwrap(),
        _ => {
            let last = parts.pop().unwrap();
            format!("{} and {}", parts.join(", "), last)
        }
    }
}
<commit_msg>Add Duration#iso8601<commit_after>#[macro_use]
extern crate helix;
extern crate rand;

use std::fmt::Write;
use rand::random;

const SECONDS_PER_MINUTE: i64 = 60;
const SECONDS_PER_HOUR:   i64 = 3600;
const SECONDS_PER_DAY:    i64 = 86400;
const SECONDS_PER_WEEK:   i64 = 604800;
const SECONDS_PER_MONTH:  i64 = 2629746; // 1/12 of a gregorian year
const SECONDS_PER_YEAR:   i64 = 31556952; // length of a gregorian year (365.2425 days)

declare_types! {
    class Duration {
        struct {
            seconds: Option<i32>,
            minutes: Option<i32>,
            hours:   Option<i32>,
            days:    Option<i32>,
            weeks:   Option<i32>,
            months:  Option<i32>,
            years:   Option<i32>,
            value:   i64,
        }

        def initialize(helix: helix::Metadata) {
            Duration {
                helix:   helix,
                seconds: Some(0),
                minutes: None,
                hours:   None,
                days:    None,
                weeks:   None,
                months:  None,
                years:   None,
                value:   0,
            }
        }

        def value(&self) -> /* i64 */ String {
            format!("{}", self.value)
        }

        def plus(&mut self, other: &Duration) {
            self.seconds = sum_part(self.seconds, other.seconds);
            self.minutes = sum_part(self.minutes, other.minutes);
            self.hours = sum_part(self.hours, other.hours);
            self.days = sum_part(self.days, other.days);
            self.weeks = sum_part(self.weeks, other.weeks);
            self.months = sum_part(self.months, other.months);
            self.years = sum_part(self.years, other.years);

            self.value += other.value;
        }

        def negate(&mut self) {

            self.seconds = negate_part(self.seconds);
            self.minutes = negate_part(self.minutes);
            self.hours = negate_part(self.hours);
            self.days = negate_part(self.days);
            self.weeks = negate_part(self.weeks);
            self.months = negate_part(self.months);
            self.years = negate_part(self.years);

            self.value *= -1;
        }

        def randomize(&mut self) {
            self.seconds = randomize_part();
            self.minutes = randomize_part();
            self.hours = randomize_part();
            self.days = randomize_part();
            self.weeks = randomize_part();
            self.months = randomize_part();
            self.years = randomize_part();

            self.value = compute_value(&self);
        }

        def iso8601(&self) -> String {
            if self.value == 0 {
                return "PT0S".to_string();
            }

            let mut output = String::new();

            let sign = if
                self.value < 0 &&
                self.seconds.unwrap_or(0) < 0 &&
                self.minutes.unwrap_or(0) < 0 &&
                self.hours.unwrap_or(0) < 0 &&
                self.days.unwrap_or(0) < 0 &&
                self.weeks.unwrap_or(0) < 0 &&
                self.months.unwrap_or(0) < 0 &&
                self.years.unwrap_or(0) < 0 {
                -1
            } else {
                1
            };

            if sign == -1 {
                output.push('-');
            }

            output.push('P');

            format_iso8601_part(&mut output, self.years, "Y");
            format_iso8601_part(&mut output, self.months, "M");
            format_iso8601_part(&mut output, self.weeks, "W");
            format_iso8601_part(&mut output, self.days, "D");

            if self.hours.unwrap_or(0) + self.minutes.unwrap_or(0) + self.seconds.unwrap_or(0) != 0 {
                output.push('T');

                format_iso8601_part(&mut output, self.hours, "H");
                format_iso8601_part(&mut output, self.minutes, "M");
                format_iso8601_part(&mut output, self.seconds, "S");
            }

            output
        }

        def inspect(&self) -> String {
            let mut parts = Vec::new();

            format_inspect_part(&mut parts, self.years, "year", "years");
            format_inspect_part(&mut parts, self.months, "month", "months");
            format_inspect_part(&mut parts, self.weeks, "week", "weeks");
            format_inspect_part(&mut parts, self.days, "day", "days");
            format_inspect_part(&mut parts, self.hours, "hour", "hours");
            format_inspect_part(&mut parts, self.minutes, "minute", "minutes");
            format_inspect_part(&mut parts, self.seconds, "second", "seconds");

            to_sentence(parts)
        }
    }
}

fn format_iso8601_part(string: &mut String, value: Option<i32>, unit: &str) {
    if let Some(v) = value {
        if v != 0 {
            write!(string, "{}{}", v, unit).unwrap();
        }
    }
}

fn format_inspect_part(parts: &mut Vec<String>, value: Option<i32>, singular: &str, plural: &str) {
    if let Some(v) = value {
        parts.push(format!("{} {}", v, if v == 1 { singular } else { plural }));
    }
}

// TODO: over/under flow bug
fn sum_part(lhs: Option<i32>, rhs: Option<i32>) -> Option<i32> {
    match lhs {
        Some(lval) => {
            match rhs {
                Some(rval) => Some(lval + rval),
                None => Some(lval)
            }
        },
        None => rhs
    }
}

fn negate_part(part: Option<i32>) -> Option<i32> {
    match part {
        Some(value) => Some(-value),
        None => None
    }
}

fn randomize_part() -> Option<i32> {
    if random() {
        Some(random::<i8>() as i32)
    } else {
        None
    }
}

fn compute_part_value(part: Option<i32>, unit: i64) -> i64 {
    match part {
        Some(value) => value as i64 * unit,
        None => 0
    }
}

fn compute_value(duration: &Duration) -> i64 {
    let mut value = 0;

    value += compute_part_value(duration.seconds, 1);
    value += compute_part_value(duration.minutes, SECONDS_PER_MINUTE);
    value += compute_part_value(duration.hours, SECONDS_PER_HOUR);
    value += compute_part_value(duration.days, SECONDS_PER_DAY);
    value += compute_part_value(duration.weeks, SECONDS_PER_WEEK);
    value += compute_part_value(duration.months, SECONDS_PER_MONTH);
    value += compute_part_value(duration.years, SECONDS_PER_YEAR);

    value
}

fn to_sentence(mut parts: Vec<String>) -> String {
    match parts.len() {
        0 => "".to_string(),
        1 => parts.pop().unwrap(),
        _ => {
            let last = parts.pop().unwrap();
            format!("{} and {}", parts.join(", "), last)
        }
    }
}
<|endoftext|>


<commit_before>extern crate fitsio;
extern crate tempdir;

/* This example docuents the following things:
 *
 * TODO: make sure this list is up to date with the example below
 *
 * creating a new file
 * writing header keys
 * writing an image into the primary hdu
 * creating a table
 * closing the file
 * re-opening it and reading the data out
 */

use std::error::Error;
use tempdir::TempDir;
use fitsio::FitsFile;
// TODO: move Image related structs/types into a more sensible place
use fitsio::fitsfile::ImageDescription;
// TODO: remove the import of imagetype from main package
use fitsio::types::ImageType;
// TODO: tidy up column description import
use fitsio::columndescription::{ColumnDataType, ColumnDescription};

fn run() -> Result<(), Box<Error>> {
    /* Create a temporary directory to work from */
    let tmp_dir = TempDir::new("fitsio")?;
    let file_path = tmp_dir.path().join("example.fits");

    // creating a new file with 512 rows and 1024 columns
    let primary_hdu_description = ImageDescription {
        // TODO: use a rust enum not C enum
        data_type: ImageType::DOUBLE_IMG,
        dimensions: &[512, 1024],
    };
    // TODO: handle this error properly and/or handle this file path properly

    {
        let mut fitsfile = FitsFile::create(file_path.to_str().unwrap())
            .with_custom_primary(&primary_hdu_description)
            .open()?;

        /* We will now add some dummy header keys. We add:
         * - the name of the project (String)
         * - the exposure time (f32)
         * - the image id (i64)
         */

        /* First we get the primary HDU */
        // TODO: add primary hdu method => let hdu = fitsfile.primary_hdu()?;
        let hdu = fitsfile.hdu(0)?;
        /* Now we add the header keys */
        // TODO: implement WritesKey for &str and String (AsRef<str>?)
        hdu.write_key(
            &mut fitsfile,
            "PROJECT",
            "My First Astronomy Project".to_string(),
        )?;

        /* Now the exposure time */
        hdu.write_key(&mut fitsfile, "EXPTIME", 15.2f32)?;

        /* And finally the image id */
        // TODO: implement write_key for u64?
        hdu.write_key(&mut fitsfile, "IMAGE_ID", 20180101010005i64)?;

        /* Now we write some dummy data to the primary HDU. We write the full image in this case.
         * */

        let dummy_data: Vec<f32> = (0..(1024 * 512))
            .map(|val| (val as f32) * 12.5 + 102.5)
            .collect();

        // TODO: remove documentation from trait methods altogether
        // TODO: document `write_image` in main lib documentation
        hdu.write_image(&mut fitsfile, &dummy_data)?;

        // TODO: Adding a new image HDU?

        /* Now we add a new table HDU called "DATA"
         *
         * We add three columns:
         *
         * 1. Object id (i32)
         * 2. Object name (String, up to 10 characters)
         * 3. Object magnitude (f32)
         * */
        let col1 = ColumnDescription::new("OBJ_ID")
            .with_type(ColumnDataType::Int)
            .create()?;
        let col2 = ColumnDescription::new("NAME")
            .with_type(ColumnDataType::String)
            .that_repeats(10)
            .create()?;
        let col3 = ColumnDescription::new("MAG")
            .with_type(ColumnDataType::Float)
            .create()?;
        let columns = &[col1, col2, col3];
        // TODO: create table should take &str or String (AsRef<str>?)
        let table_hdu = fitsfile.create_table("DATA".to_string(), columns)?;

        /* Add some data to the columns */
        // TODO: add row methods
        let n_rows = 10;
        let obj_id_data: Vec<i32> = (0..n_rows).collect();
        let name_data: Vec<String> = (0..n_rows).map(|idx| format!("N{}", idx)).collect();
        let mag_data: Vec<f32> = (0..n_rows).map(|idx| -0.2 * idx as f32).collect();

        table_hdu.write_col(&mut fitsfile, "OBJ_ID", &obj_id_data)?;
        table_hdu.write_col(&mut fitsfile, "NAME", &name_data)?;
        table_hdu.write_col(&mut fitsfile, "MAG", &mag_data)?;

        // TODO: pretty print table structure?

        /* `fitsfile` is dropped here at the end of the scope, which closes fhe file on disk */
    }

    /* Here we re-open the file. We want to adjust things, so we use the `edit` method */
    let mut fitsfile = FitsFile::edit(file_path.to_str().unwrap())?;

    /* Get the primary HDU and read a section of the image data */

    let phdu = fitsfile.hdu(0)?;
    // TODO: let phdu = fitsfile.primary_hdu()?;

    // TODO: if we drop `fitsfile`, can we still use a FitsHdu object? What is the error that
    // occurs?!

    /* Let's say we have a region around a star that we want to extract. The star is at (25, 25,
     * 1-indexed) and we want to extract a 5x5 box around it. This means we want to read rows 19 to
     * 19, and columns 19 to 29 (0-indexed). The range arguments are exclusive of the upper bound,
     * so we must use 19..29 for each axis.
     *
     * Note: this is not performant, and if this is the desired use of `fitsio`, I would not
     * suggest this approach. */
    let image_data: Vec<f32> = phdu.read_region(&mut fitsfile, &[&(19..29), &(19..29)])?;
    assert_eq!(image_data.len(), 100);

    /* We can now get the column data. Let's assume we want all of the magnitudes for objects near
     * this one (by index). This object is at index 4 (0-indexed) and we want to read one value
     * before and after it. */
    let table_hdu = fitsfile.hdu("DATA")?;
    let magnitudes: Vec<f32> = table_hdu.read_col_range(&mut fitsfile, "MAG", &(3..6))?;
    assert_eq!(magnitudes.len(), 3);

    /* The file is closed when it is dropped here */

    Ok(())
}

fn main() {
    run().unwrap();
}
<commit_msg>Convert all todo items to github issues<commit_after>extern crate fitsio;
extern crate tempdir;

/* This example docuents the following things:
 *
 * creating a new file
 * writing header keys
 * writing an image into the primary hdu
 * creating a table
 * adding data to a table
 * closing the file
 * re-opening it
 * reading an image region
 * reading some rows from a table column
 */

use std::error::Error;
use tempdir::TempDir;
use fitsio::FitsFile;
use fitsio::fitsfile::ImageDescription;
use fitsio::types::ImageType;
use fitsio::columndescription::{ColumnDataType, ColumnDescription};

fn run() -> Result<(), Box<Error>> {
    /* Create a temporary directory to work from */
    let tmp_dir = TempDir::new("fitsio")?;
    let file_path = tmp_dir.path().join("example.fits");

    // creating a new file with 512 rows and 1024 columns
    let primary_hdu_description = ImageDescription {
        data_type: ImageType::DOUBLE_IMG,
        dimensions: &[512, 1024],
    };

    {
        let mut fitsfile = FitsFile::create(file_path.to_str().unwrap())
            .with_custom_primary(&primary_hdu_description)
            .open()?;

        /* We will now add some dummy header keys. We add:
         * - the name of the project (String)
         * - the exposure time (f32)
         * - the image id (i64)
         */

        /* First we get the primary HDU */
        let hdu = fitsfile.hdu(0)?;

        /* Now we add the header keys */
        hdu.write_key(
            &mut fitsfile,
            "PROJECT",
            "My First Astronomy Project".to_string(),
        )?;

        /* Now the exposure time */
        hdu.write_key(&mut fitsfile, "EXPTIME", 15.2f32)?;

        /* And finally the image id */
        hdu.write_key(&mut fitsfile, "IMAGE_ID", 20180101010005i64)?;

        /* Now we write some dummy data to the primary HDU. We write the full image in this case.
         * */

        let dummy_data: Vec<f32> = (0..(1024 * 512))
            .map(|val| (val as f32) * 12.5 + 102.5)
            .collect();

        hdu.write_image(&mut fitsfile, &dummy_data)?;

        /* We can create a new image with the following */
        let image_description = ImageDescription {
            data_type: ImageType::LONG_IMG,
            dimensions: &[256, 256],
        };
        fitsfile.create_image("IMG".to_string(), &image_description)?;

        /* Now we add a new table HDU called "DATA"
         *
         * We add three columns:
         *
         * 1. Object id (i32)
         * 2. Object name (String, up to 10 characters)
         * 3. Object magnitude (f32)
         * */
        let col1 = ColumnDescription::new("OBJ_ID")
            .with_type(ColumnDataType::Int)
            .create()?;
        let col2 = ColumnDescription::new("NAME")
            .with_type(ColumnDataType::String)
            .that_repeats(10)
            .create()?;
        let col3 = ColumnDescription::new("MAG")
            .with_type(ColumnDataType::Float)
            .create()?;
        let columns = &[col1, col2, col3];
        let table_hdu = fitsfile.create_table("DATA".to_string(), columns)?;

        /* Add some data to the columns */
        let n_rows = 10;
        let obj_id_data: Vec<i32> = (0..n_rows).collect();
        let name_data: Vec<String> = (0..n_rows).map(|idx| format!("N{}", idx)).collect();
        let mag_data: Vec<f32> = (0..n_rows).map(|idx| -0.2 * idx as f32).collect();

        table_hdu.write_col(&mut fitsfile, "OBJ_ID", &obj_id_data)?;
        table_hdu.write_col(&mut fitsfile, "NAME", &name_data)?;
        table_hdu.write_col(&mut fitsfile, "MAG", &mag_data)?;

        /* `fitsfile` is dropped here at the end of the scope, which closes fhe file on disk */
    }

    /* Here we re-open the file. We want to adjust things, so we use the `edit` method */
    let mut fitsfile = FitsFile::edit(file_path.to_str().unwrap())?;

    /* Get the primary HDU and read a section of the image data */

    let phdu = fitsfile.hdu(0)?;

    /* Let's say we have a region around a star that we want to extract. The star is at (25, 25,
     * 1-indexed) and we want to extract a 5x5 box around it. This means we want to read rows 19 to
     * 19, and columns 19 to 29 (0-indexed). The range arguments are exclusive of the upper bound,
     * so we must use 19..29 for each axis.
     *
     * Note: this is not performant, and if this is the desired use of `fitsio`, I would not
     * suggest this approach. */
    let image_data: Vec<f32> = phdu.read_region(&mut fitsfile, &[&(19..29), &(19..29)])?;
    assert_eq!(image_data.len(), 100);

    /* We can now get the column data. Let's assume we want all of the magnitudes for objects near
     * this one (by index). This object is at index 4 (0-indexed) and we want to read one value
     * before and after it. */
    let table_hdu = fitsfile.hdu("DATA")?;
    let magnitudes: Vec<f32> = table_hdu.read_col_range(&mut fitsfile, "MAG", &(3..6))?;
    assert_eq!(magnitudes.len(), 3);

    /* The file is closed when it is dropped here */

    Ok(())
}

fn main() {
    run().unwrap();
}
<|endoftext|>


<commit_before>//! Implements a command for uninstalling `sentry-cli`
use std::env;
use std::fs;

use clap::{App, AppSettings, ArgMatches};
use console::style;
use failure::Error;

use crate::utils::fs::is_writable;
use crate::utils::system::{is_homebrew_install, is_npm_install, QuietExit};
use crate::utils::ui::prompt_to_continue;

fn is_hidden() -> bool {
    cfg!(windows) || is_homebrew_install() || is_npm_install()
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Uninstall the sentry-cli executable.")
        .settings(&if is_hidden() {
            vec![AppSettings::Hidden]
        } else {
            vec![]
        })
}

pub fn execute<'a>(_matches: &ArgMatches<'a>) -> Result<(), Error> {
    let exe = env::current_exe()?;

    if is_homebrew_install() {
        println!("This installation of sentry-cli is managed through homebrew");
        println!("Please use homebrew to uninstall sentry-cli");
        println!();
        println!("{} brew uninstall sentry-cli", style("$").dim());
        return Err(QuietExit(1).into());
    }
    if is_npm_install() {
        println!("This installation of sentry-cli is managed through npm/yarn");
        println!("Please use npm/yarn to uninstall sentry-cli");
        return Err(QuietExit(1).into());
    }
    if cfg!(windows) {
        println!("Cannot uninstall on Windows :(");
        println!();
        println!("Delete this file yourself: {}", exe.display());
        return Err(QuietExit(1).into());
    }

    if !prompt_to_continue("Do you really want to uninstall sentry-cli?")? {
        println!("Aborted!");
        return Ok(());
    }

    if !is_writable(&exe) {
        println!("Need to sudo to uninstall {}", exe.display());
        runas::Command::new("rm").arg("-f").arg(&exe).status()?;
    } else {
        fs::remove_file(&exe)?;
    }
    println!("Uninstalled!");

    Ok(())
}
<commit_msg>feat: add skip confirmation arg to uninstall cmd (#812)<commit_after>//! Implements a command for uninstalling `sentry-cli`
use std::env;
use std::fs;

use clap::{App, AppSettings, Arg, ArgMatches};
use console::style;
use failure::Error;

use crate::utils::fs::is_writable;
use crate::utils::system::{is_homebrew_install, is_npm_install, QuietExit};
use crate::utils::ui::prompt_to_continue;

fn is_hidden() -> bool {
    cfg!(windows) || is_homebrew_install() || is_npm_install()
}

pub fn make_app<'a, 'b: 'a>(app: App<'a, 'b>) -> App<'a, 'b> {
    app.about("Uninstall the sentry-cli executable.")
        .arg(
            Arg::with_name("confirm")
                .long("confirm")
                .help("Skip uninstall confirmation prompt."),
        )
        .settings(&if is_hidden() {
            vec![AppSettings::Hidden]
        } else {
            vec![]
        })
}

pub fn execute<'a>(matches: &ArgMatches<'a>) -> Result<(), Error> {
    let exe = env::current_exe()?;

    if is_homebrew_install() {
        println!("This installation of sentry-cli is managed through homebrew");
        println!("Please use homebrew to uninstall sentry-cli");
        println!();
        println!("{} brew uninstall sentry-cli", style("$").dim());
        return Err(QuietExit(1).into());
    }
    if is_npm_install() {
        println!("This installation of sentry-cli is managed through npm/yarn");
        println!("Please use npm/yarn to uninstall sentry-cli");
        return Err(QuietExit(1).into());
    }
    if cfg!(windows) {
        println!("Cannot uninstall on Windows :(");
        println!();
        println!("Delete this file yourself: {}", exe.display());
        return Err(QuietExit(1).into());
    }

    if !matches.is_present("confirm") {
        if !prompt_to_continue("Do you really want to uninstall sentry-cli?")? {
            println!("Aborted!");
            return Ok(());
        }
    }

    if !is_writable(&exe) {
        println!("Need to sudo to uninstall {}", exe.display());
        runas::Command::new("rm").arg("-f").arg(&exe).status()?;
    } else {
        fs::remove_file(&exe)?;
    }
    println!("Uninstalled!");

    Ok(())
}
<|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)

        (199, 199, 199)
    }

    pub fn default_bg_color() -> (u8, u8, u8) {
        (11, 16, 39)
    }

    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 = 20;
        (
            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
*/
<commit_msg>update colors<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)

        (199, 199, 199)
    }

    pub fn default_bg_color() -> (u8, u8, u8) {
        (11, 16, 39)
    }

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

    pub fn title_bg_color() -> (u8, u8, u8) {
        Self::default_color()
    }

    pub fn default_selected_bg_color() -> (u8, u8, u8) {
        Self::default_mark_line_bg_color()
    }

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

        let add = 20;
        (
            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>//! Defines the render target for tray, where our image will be written too
//! during rendering
//! TODO: Reconstruction filters

use std::vec::Vec;
use std::{iter, cmp, f32};
use std::sync::Mutex;

use film::Colorf;
use film::filter::Filter;
use sampler::Region;

const FILTER_TABLE_SIZE: usize = 16;

/// A struct containing results of an image sample where a ray was fired through
/// continuous pixel coordinates [x, y] and color `color` was computed
pub struct ImageSample {
    pub x: f32,
    pub y: f32,
    pub color: Colorf,
}

impl ImageSample {
    pub fn new(x: f32, y: f32, color: Colorf) -> ImageSample {
        ImageSample { x: x, y: y, color: color }
    }
}

/// RenderTarget is a RGBF render target to write our image too while rendering
pub struct RenderTarget {
    width: usize,
    height: usize,
    pixels_locked: Vec<Mutex<Vec<Colorf>>>,
    lock_size: (i32, i32),
    filter: Box<Filter + Send + Sync>,
    filter_table: Vec<f32>,
    filter_pixel_width: (i32, i32),
}

impl RenderTarget {
    /// Create a render target with `width * height` pixels
    pub fn new(image_dim: (usize, usize), lock_size: (usize, usize),
               filter: Box<Filter + Send + Sync>) -> RenderTarget {
        if image_dim.0 % lock_size.0 != 0 || image_dim.1 % lock_size.1 != 0 {
            panic!("Image with dimension {:?} not evenly divided by blocks of {:?}", image_dim, lock_size);
        }
        let width = image_dim.0;
        let height = image_dim.1;
        let filter_pixel_width = (f32::floor(filter.width() / 0.5) as i32,
                                  f32::floor(filter.height() / 0.5) as i32);
        let mut filter_table: Vec<f32> = iter::repeat(0.0).take(FILTER_TABLE_SIZE * FILTER_TABLE_SIZE)
            .collect();
        for y in 0..FILTER_TABLE_SIZE {
            let fy = (y as f32 + 0.5) * filter.height() / FILTER_TABLE_SIZE as f32;
            for x in 0..FILTER_TABLE_SIZE {
                let fx = (x as f32 + 0.5) * filter.width() / FILTER_TABLE_SIZE as f32;
                filter_table[y * FILTER_TABLE_SIZE + x] = filter.weight(fx, fy);
            }
        }

        let x_blocks = width / lock_size.0;
        let y_blocks = height / lock_size.1;
        let mut pixels_locked = Vec::with_capacity(x_blocks * y_blocks);
        for _ in 0..x_blocks * y_blocks {
            pixels_locked.push(Mutex::new(iter::repeat(Colorf::broadcast(0.0))
                                          .take(lock_size.0 * lock_size.1).collect()));
        }

        RenderTarget { width: width, height: height,
            pixels_locked: pixels_locked,
            lock_size: (lock_size.0 as i32, lock_size.1 as i32),
            filter: filter,
            filter_table: filter_table,
            filter_pixel_width: filter_pixel_width,
        }
    }
    /// Write all the image samples to the render target
    pub fn write(&self, samples: &Vec<ImageSample>, region: &Region) {
        // Determine which blocks we touch with our set of samples
        let x_range = (cmp::max(region.start.0 as i32 - self.filter_pixel_width.0, 0),
                       cmp::min(region.end.0 as i32 + self.filter_pixel_width.0, self.width as i32 - 1));
        let y_range = (cmp::max(region.start.1 as i32 - self.filter_pixel_width.1, 0),
                       cmp::min(region.end.1 as i32 + self.filter_pixel_width.1, self.height as i32 - 1));

        if x_range.1 - x_range.0 < 0 || y_range.1 - y_range.0 < 0 {
                return;
        }
        println!("Writing too x range {:?}, y range {:?}", x_range, y_range);
        let block_x_range = (x_range.0 / self.lock_size.0, x_range.1 / self.lock_size.0);
        let block_y_range = (y_range.0 / self.lock_size.1, y_range.1 / self.lock_size.1);
        println!("Block ranges: x = {:?}, y = {:?}", block_x_range, block_y_range);
        // Temporary storage for filtered samples so we can compute the filtered results for
        // the block we're writing too without having to get the lock
        let mut filtered_samples: Vec<_> = iter::repeat(Colorf::broadcast(0.0))
            .take((self.lock_size.0 * self.lock_size.1) as usize).collect();
        println!("filter width = {:?}", self.filter_pixel_width);
        println!("Filter table = {:?}", self.filter_table);

        let blocks_per_row = self.width as i32 / self.lock_size.0;
        for y in block_y_range.0..block_y_range.1 + 1 {
            for x in block_x_range.0..block_x_range.1 + 1 {
                let block_x_start = x * self.lock_size.0;
                let block_y_start = y * self.lock_size.1;
                println!("Writing to block ({}, {})", x, y);

                let x_write_range = (cmp::max(x_range.0, block_x_start),
                                     cmp::min(x_range.1 + 1, block_x_start + self.lock_size.0));
                let y_write_range = (cmp::max(y_range.0, block_y_start),
                                     cmp::min(y_range.1 + 1, block_y_start + self.lock_size.1));
                println!("Write ranges: x = {:?}, y = {:?}", x_write_range, y_write_range);

                let block_samples = samples.iter().filter(|s| {
                        s.x >= (x_write_range.0 - self.filter_pixel_width.0) as f32
                        && s.x < (x_write_range.1 + self.filter_pixel_width.0) as f32
                        && s.y >= (y_write_range.0 - self.filter_pixel_width.1) as f32
                        && s.y < (y_write_range.1 + self.filter_pixel_width.1) as f32
                });

                for c in &mut filtered_samples {
                    *c = Colorf::broadcast(0.0);
                }

                // Compute the filtered samples for the block
                let mut bsamples_count = 0;
                for c in block_samples {
                    bsamples_count += 1;
                    let img_x = c.x - 0.5;
                    let img_y = c.y - 0.5;
                    println!("Sample at ({}, {})", c.x, c.y);
                    for iy in y_write_range.0..y_write_range.1 {
                        let fy = f32::abs(iy as f32 - img_y) * self.filter.inv_height()
                            * FILTER_TABLE_SIZE as f32;
                        let fy_idx = cmp::min(fy as usize, FILTER_TABLE_SIZE - 1);
                        if fy > self.filter.height() {
                            println!("y-filtered out");
                            continue;
                        }
                        for ix in x_write_range.0..x_write_range.1 {
                            println!("Dist from sample: ({}, {})", ix as f32 - img_x, iy as f32 - img_y);
                            let fx = f32::abs(ix as f32 - img_x) * self.filter.inv_width()
                                * FILTER_TABLE_SIZE as f32;
                            if fx > self.filter.width() {
                                println!("x-filtered out");
                                continue;
                            }
                            let fx_idx = cmp::min(fx as usize, FILTER_TABLE_SIZE - 1);
                            println!("float indices = ({}, {}), filter indices = ({}, {})",
                                     fx, fy, fx_idx, fy_idx);
                            let weight = self.filter_table[fy_idx * FILTER_TABLE_SIZE + fx_idx];
                            let px = ((iy - block_y_start) * self.lock_size.0 + ix - block_x_start) as usize;
                            println!("Weight at pixel ({}, {}) = {}", ix, iy, weight);
                            // TODO: Can't currently overload the += operator
                            // Coming soon though, see RFC #953 https://github.com/rust-lang/rfcs/pull/953
                            filtered_samples[px].r += weight * c.color.r;
                            filtered_samples[px].g += weight * c.color.g;
                            filtered_samples[px].b += weight * c.color.b;
                            filtered_samples[px].a += weight;
                        }
                    }
                }
                println!("# samples for this block = {}", bsamples_count);

                // Acquire lock for the block and write the filtered samples
                let block_idx = (y * blocks_per_row + x) as usize;
                let mut pixels = self.pixels_locked[block_idx].lock().unwrap();
                for iy in y_write_range.0..y_write_range.1 {
                    for ix in x_write_range.0..x_write_range.1 {
                        let px = ((iy - block_y_start) * self.lock_size.0 + ix - block_x_start) as usize;
                        let c = &filtered_samples[px];
                        pixels[px].r += c.r;
                        pixels[px].g += c.g;
                        pixels[px].b += c.b;
                        pixels[px].a += c.a;
                    }
                }
                println!("---------------------------------");
            }
        }
    }
    /// Clear the render target to black
    pub fn clear(&mut self) {
        let x_blocks = self.width / self.lock_size.0 as usize;
        let y_blocks = self.height / self.lock_size.1 as usize;
        for by in 0..y_blocks {
            for bx in 0..x_blocks {
                let block_idx = (by * x_blocks + bx) as usize;
                let mut pixels = self.pixels_locked[block_idx].lock().unwrap();
                for p in pixels.iter_mut() {
                    *p = Colorf::broadcast(0.0);
                }
            }
        }
    }
    /// Get the dimensions of the render target
    pub fn dimensions(&self) -> (usize, usize) {
        (self.width, self.height)
    }
    /// Convert the floating point color buffer to 24bpp sRGB for output to an image
    pub fn get_render(&self) -> Vec<u8> {
        let mut render: Vec<u8> = iter::repeat(0u8).take(self.width * self.height * 3).collect();
        let x_blocks = self.width / self.lock_size.0 as usize;
        let y_blocks = self.height / self.lock_size.1 as usize;
        for by in 0..y_blocks {
            for bx in 0..x_blocks {
                let block_x_start = bx * self.lock_size.0 as usize;
                let block_y_start = by * self.lock_size.1 as usize;
                let block_idx = (by * x_blocks + bx) as usize;
                let pixels = self.pixels_locked[block_idx].lock().unwrap();
                for y in 0..self.lock_size.1 as usize {
                    for x in 0..self.lock_size.0 as usize {
                        let c = &pixels[y * self.lock_size.0 as usize + x];
                        if c.a > 0.0 {
                            let cn = (*c / c.a).clamp().to_srgb();
                            let px = (y + block_y_start) * self.width * 3 + (x + block_x_start) * 3;
                            for i in 0..3 {
                                render[px + i] = (cn[i] * 255.0) as u8;
                            }
                        }
                    }
                }
            }
        }
        render
    }
}

<commit_msg>Fix filtering bug: Check that the sample actually effects this pixel<commit_after>//! Defines the render target for tray, where our image will be written too
//! during rendering
//! TODO: Reconstruction filters

use std::vec::Vec;
use std::{iter, cmp, f32};
use std::sync::Mutex;

use film::Colorf;
use film::filter::Filter;
use sampler::Region;

const FILTER_TABLE_SIZE: usize = 16;

/// A struct containing results of an image sample where a ray was fired through
/// continuous pixel coordinates [x, y] and color `color` was computed
pub struct ImageSample {
    pub x: f32,
    pub y: f32,
    pub color: Colorf,
}

impl ImageSample {
    pub fn new(x: f32, y: f32, color: Colorf) -> ImageSample {
        ImageSample { x: x, y: y, color: color }
    }
}

/// RenderTarget is a RGBF render target to write our image too while rendering
pub struct RenderTarget {
    width: usize,
    height: usize,
    pixels_locked: Vec<Mutex<Vec<Colorf>>>,
    lock_size: (i32, i32),
    filter: Box<Filter + Send + Sync>,
    filter_table: Vec<f32>,
    filter_pixel_width: (i32, i32),
}

impl RenderTarget {
    /// Create a render target with `width * height` pixels
    pub fn new(image_dim: (usize, usize), lock_size: (usize, usize),
               filter: Box<Filter + Send + Sync>) -> RenderTarget {
        if image_dim.0 % lock_size.0 != 0 || image_dim.1 % lock_size.1 != 0 {
            panic!("Image with dimension {:?} not evenly divided by blocks of {:?}", image_dim, lock_size);
        }
        let width = image_dim.0;
        let height = image_dim.1;
        let filter_pixel_width = (f32::floor(filter.width() / 0.5) as i32,
                                  f32::floor(filter.height() / 0.5) as i32);
        let mut filter_table: Vec<f32> = iter::repeat(0.0).take(FILTER_TABLE_SIZE * FILTER_TABLE_SIZE)
            .collect();
        for y in 0..FILTER_TABLE_SIZE {
            let fy = (y as f32 + 0.5) * filter.height() / FILTER_TABLE_SIZE as f32;
            for x in 0..FILTER_TABLE_SIZE {
                let fx = (x as f32 + 0.5) * filter.width() / FILTER_TABLE_SIZE as f32;
                filter_table[y * FILTER_TABLE_SIZE + x] = filter.weight(fx, fy);
            }
        }

        let x_blocks = width / lock_size.0;
        let y_blocks = height / lock_size.1;
        let mut pixels_locked = Vec::with_capacity(x_blocks * y_blocks);
        for _ in 0..x_blocks * y_blocks {
            pixels_locked.push(Mutex::new(iter::repeat(Colorf::broadcast(0.0))
                                          .take(lock_size.0 * lock_size.1).collect()));
        }

        RenderTarget { width: width, height: height,
            pixels_locked: pixels_locked,
            lock_size: (lock_size.0 as i32, lock_size.1 as i32),
            filter: filter,
            filter_table: filter_table,
            filter_pixel_width: filter_pixel_width,
        }
    }
    /// Write all the image samples to the render target
    pub fn write(&self, samples: &Vec<ImageSample>, region: &Region) {
        // Determine which blocks we touch with our set of samples
        let x_range = (cmp::max(region.start.0 as i32 - self.filter_pixel_width.0, 0),
                       cmp::min(region.end.0 as i32 + self.filter_pixel_width.0, self.width as i32 - 1));
        let y_range = (cmp::max(region.start.1 as i32 - self.filter_pixel_width.1, 0),
                       cmp::min(region.end.1 as i32 + self.filter_pixel_width.1, self.height as i32 - 1));

        if x_range.1 - x_range.0 < 0 || y_range.1 - y_range.0 < 0 {
                return;
        }
        let block_x_range = (x_range.0 / self.lock_size.0, x_range.1 / self.lock_size.0);
        let block_y_range = (y_range.0 / self.lock_size.1, y_range.1 / self.lock_size.1);
        // Temporary storage for filtered samples so we can compute the filtered results for
        // the block we're writing too without having to get the lock
        let mut filtered_samples: Vec<_> = iter::repeat(Colorf::broadcast(0.0))
            .take((self.lock_size.0 * self.lock_size.1) as usize).collect();

        let blocks_per_row = self.width as i32 / self.lock_size.0;
        for y in block_y_range.0..block_y_range.1 + 1 {
            for x in block_x_range.0..block_x_range.1 + 1 {
                let block_x_start = x * self.lock_size.0;
                let block_y_start = y * self.lock_size.1;

                let x_write_range = (cmp::max(x_range.0, block_x_start),
                                     cmp::min(x_range.1 + 1, block_x_start + self.lock_size.0));
                let y_write_range = (cmp::max(y_range.0, block_y_start),
                                     cmp::min(y_range.1 + 1, block_y_start + self.lock_size.1));

                let block_samples = samples.iter().filter(|s| {
                        s.x >= (x_write_range.0 - self.filter_pixel_width.0) as f32
                        && s.x < (x_write_range.1 + self.filter_pixel_width.0) as f32
                        && s.y >= (y_write_range.0 - self.filter_pixel_width.1) as f32
                        && s.y < (y_write_range.1 + self.filter_pixel_width.1) as f32
                });

                for c in &mut filtered_samples {
                    *c = Colorf::broadcast(0.0);
                }

                // Compute the filtered samples for the block
                for c in block_samples {
                    let img_x = c.x - 0.5;
                    let img_y = c.y - 0.5;
                    for iy in y_write_range.0..y_write_range.1 {
                        let fy = f32::abs(iy as f32 - img_y) * self.filter.inv_height();
                        // While we know this sample effects some pixels in this block it may not
                        // necessarily effect this specific pixel, so double check that it's in
                        // the filter's dimensions.
                        if fy > self.filter.height() {
                            continue;
                        }
                        let fy_idx = cmp::min((fy * FILTER_TABLE_SIZE as f32) as usize, FILTER_TABLE_SIZE - 1);

                        for ix in x_write_range.0..x_write_range.1 {
                            let fx = f32::abs(ix as f32 - img_x) * self.filter.inv_width();
                            // Check that we're also in the width of the filter
                            if fx > self.filter.width() {
                                continue;
                            }
                            let fx_idx = cmp::min((fx * FILTER_TABLE_SIZE as f32) as usize, FILTER_TABLE_SIZE - 1);

                            let weight = self.filter_table[fy_idx * FILTER_TABLE_SIZE + fx_idx];
                            let px = ((iy - block_y_start) * self.lock_size.0 + ix - block_x_start) as usize;
                            // TODO: Can't currently overload the += operator
                            // Coming soon though, see RFC #953 https://github.com/rust-lang/rfcs/pull/953
                            filtered_samples[px].r += weight * c.color.r;
                            filtered_samples[px].g += weight * c.color.g;
                            filtered_samples[px].b += weight * c.color.b;
                            filtered_samples[px].a += weight;
                        }
                    }
                }

                // Acquire lock for the block and write the filtered samples
                let block_idx = (y * blocks_per_row + x) as usize;
                let mut pixels = self.pixels_locked[block_idx].lock().unwrap();
                for iy in y_write_range.0..y_write_range.1 {
                    for ix in x_write_range.0..x_write_range.1 {
                        let px = ((iy - block_y_start) * self.lock_size.0 + ix - block_x_start) as usize;
                        let c = &filtered_samples[px];
                        pixels[px].r += c.r;
                        pixels[px].g += c.g;
                        pixels[px].b += c.b;
                        pixels[px].a += c.a;
                    }
                }
            }
        }
    }
    /// Clear the render target to black
    pub fn clear(&mut self) {
        let x_blocks = self.width / self.lock_size.0 as usize;
        let y_blocks = self.height / self.lock_size.1 as usize;
        for by in 0..y_blocks {
            for bx in 0..x_blocks {
                let block_idx = (by * x_blocks + bx) as usize;
                let mut pixels = self.pixels_locked[block_idx].lock().unwrap();
                for p in pixels.iter_mut() {
                    *p = Colorf::broadcast(0.0);
                }
            }
        }
    }
    /// Get the dimensions of the render target
    pub fn dimensions(&self) -> (usize, usize) {
        (self.width, self.height)
    }
    /// Convert the floating point color buffer to 24bpp sRGB for output to an image
    pub fn get_render(&self) -> Vec<u8> {
        let mut render: Vec<u8> = iter::repeat(0u8).take(self.width * self.height * 3).collect();
        let x_blocks = self.width / self.lock_size.0 as usize;
        let y_blocks = self.height / self.lock_size.1 as usize;
        for by in 0..y_blocks {
            for bx in 0..x_blocks {
                let block_x_start = bx * self.lock_size.0 as usize;
                let block_y_start = by * self.lock_size.1 as usize;
                let block_idx = (by * x_blocks + bx) as usize;
                let pixels = self.pixels_locked[block_idx].lock().unwrap();
                for y in 0..self.lock_size.1 as usize {
                    for x in 0..self.lock_size.0 as usize {
                        let c = &pixels[y * self.lock_size.0 as usize + x];
                        if c.a > 0.0 {
                            let cn = (*c / c.a).clamp().to_srgb();
                            let px = (y + block_y_start) * self.width * 3 + (x + block_x_start) * 3;
                            for i in 0..3 {
                                render[px + i] = (cn[i] * 255.0) as u8;
                            }
                        }
                    }
                }
            }
        }
        render
    }
}

<|endoftext|>


<commit_before>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&mut self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Op(ref mut operator) => operator.run(memory),
            Node::Function(ref mut function) => function.run(memory, Vec::new()),
            _ => Ok(self.clone()),
        }
    }
}

impl Operator {
    fn run(&mut self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref mut expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }
            Operator::Add(ref mut left, ref mut right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 + val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            _ => unimplemented!(),
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

impl Function {
    fn run(&mut self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &mut self.body {
            match expr.node {
                Node::Return(_) => return revert_scope_after(expr.run(memory), scope, memory),
                _ => expr.run(memory)?,
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let mut expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt");

        let result = expr.run(&mut memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let mut expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt");

        let result = expr.run(&mut memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let mut expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = HashMap::new();
        let result = expr.run(&mut memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }
}
<commit_msg>remove line that should never be reached<commit_after>#![allow(unused)]

use interpreter::ast::*;
use interpreter::tokenizer::*;

use std::collections::HashMap;

pub type Memory = HashMap<Ident, Expr>;

impl Expr {
    fn run(&mut self, memory: &mut Memory) -> Result<Expr, String> {
        match self.node {
            Node::Ident(ref id) => {
                memory
                    .get(id)
                    .ok_or_else(|| {
                        self.debug.to_string() + ", but " + &id.0 + " has not been defined yet."
                    })
                    .map(|val| val.clone())
            }
            Node::Op(ref mut operator) => operator.run(memory),
            Node::Function(ref mut function) => function.run(memory, Vec::new()),
            _ => Ok(self.clone()),
        }
    }
}

impl Operator {
    fn run(&mut self, memory: &mut Memory) -> Result<Expr, String> {
        match *self {
            Operator::Assign(ref id, ref mut expr) => {
                let val = expr.run(memory)?;
                memory.insert(id.clone(), val);
                Ok(Expr::new(Node::Noop, expr.debug.clone()))
            }
            Operator::Add(ref mut left, ref mut right) => {
                let left_res = left.run(memory)?;
                let right_res = right.run(memory)?;
                match (left_res.node, right_res.node) {
                    (Node::Int(val1), Node::Int(val2)) => {
                        Ok(Expr::new(Node::Int(val1 + val2), DebugInfo::none()))
                    }
                    _ => unimplemented!(),
                }
            }
            _ => unimplemented!(),
        }
    }
}

/// get the list of visible identifiers at this scope level
fn get_scope(memory: &mut Memory) -> Vec<Ident> {
    memory.keys().cloned().collect()
}

/// remove any identifiers that were created during the scoped region
fn revert_scope(scope: Vec<Ident>, memory: &mut Memory) {
    memory.retain(|ident, _| scope.contains(ident));
}

fn revert_scope_after<T>(val: T, scope: Vec<Ident>, memory: &mut Memory) -> T {
    revert_scope(scope, memory);
    val
}

impl Function {
    fn run(&mut self, memory: &mut Memory, args: Vec<Expr>) -> Result<Expr, String> {
        if self.args.len() != args.len() {
            return Err(format!(
                "Function {} called with incorrect number of arguments. Expected {} but found {}.",
                self.name,
                self.args.len(),
                args.len()
            ))?;
        }
        let scope = get_scope(memory);
        for (id, val) in self.args.iter().zip(args) {
            memory.insert(id.clone(), val);
        }
        for mut expr in &mut self.body {
            match expr.node {
                Node::Return(_) => return revert_scope_after(expr.run(memory), scope, memory),
                _ => expr.run(memory)?,
            };
        }

        revert_scope(scope, memory);

        Ok(Expr::new(Node::Noop, DebugInfo::none()))
    }
}

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

    fn create_global(name: &str, memory: &mut Memory) {
        memory.insert(
            Ident(name.to_string()),
            Expr::new(Node::Noop, DebugInfo::none()),
        );
    }

    #[test]
    fn walk_assign() {
        let mut expr = Expr::parse("$someInt = 35").unwrap();
        let mut memory = HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt");

        let result = expr.run(&mut memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    fn walk_assign2() {
        let mut expr = Expr::parse("$otherInt = 35; $someInt = $otherInt + 2;").unwrap();
        let mut memory = HashMap::new();

        // create $someInt outside the scope of the anonymous outer function that
        // Expr::parse returns, that way the effect of the assignment can be seen here.
        create_global("$someInt");

        let result = expr.run(&mut memory).unwrap();
        println!("{:#?}\n memory: {:#?}", result, memory);
    }

    #[test]
    #[should_panic]
    fn walk_assign_undefined() {
        let mut expr = Expr::parse("$someInt = 35; $otherInt = $someInt + $whatIsThis;").unwrap();
        let mut memory = HashMap::new();
        let result = expr.run(&mut memory).unwrap();
    }
}
<|endoftext|>