Import 'which' package version 3.1.1

* Add OWNERS
Bug: 152884384
Test: make

Change-Id: Ic643b3c23001db1124e626548e13e43b27b89c7b
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
new file mode 100644
index 0000000..98aa420
--- /dev/null
+++ b/.cargo_vcs_info.json
@@ -0,0 +1,5 @@
+{
+  "git": {
+    "sha1": "1ffb2479652982ab8aaa1706bba6de5281a7ba22"
+  }
+}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..a9d37c5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+target
+Cargo.lock
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..0de7621
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,18 @@
+language: rust
+
+os:
+  - linux
+  - osx
+
+rust:
+  - stable
+
+cache:
+  directories:
+    - $HOME/.cargo
+
+script:
+  - cargo build --all
+  - cargo test
+  - cargo test --no-default-features
+  - cargo doc --all --no-deps
\ No newline at end of file
diff --git a/Android.bp b/Android.bp
new file mode 100644
index 0000000..fc7db87
--- /dev/null
+++ b/Android.bp
@@ -0,0 +1,14 @@
+// This file is generated by cargo2android.py.
+
+rust_library_host_rlib {
+    name: "libwhich",
+    crate_name: "which",
+    srcs: ["src/lib.rs"],
+    edition: "2015",
+    rlibs: [
+        "liblibc",
+    ],
+}
+
+// dependent_library ["feature_list"]
+//   libc-0.2.68 "default,std"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..567792b
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,36 @@
+# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
+#
+# When uploading crates to the registry Cargo will automatically
+# "normalize" Cargo.toml files for maximal compatibility
+# with all versions of Cargo and also rewrite `path` dependencies
+# to registry (e.g., crates.io) dependencies
+#
+# If you believe there's an error in this file please file an
+# issue against the rust-lang/cargo repository. If you're
+# editing this file be aware that the upstream Cargo.toml
+# will likely look very different (and much more reasonable)
+
+[package]
+name = "which"
+version = "3.1.1"
+authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
+description = "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
+documentation = "https://docs.rs/which/"
+readme = "README.md"
+keywords = ["which", "which-rs", "unix", "command"]
+categories = ["os", "filesystem"]
+license = "MIT"
+repository = "https://github.com/harryfei/which-rs.git"
+[dependencies.failure]
+version = "0.1.7"
+features = ["std"]
+optional = true
+default-features = false
+
+[dependencies.libc]
+version = "0.2.65"
+[dev-dependencies.tempdir]
+version = "0.3.7"
+
+[features]
+default = ["failure"]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
new file mode 100644
index 0000000..2c353c8
--- /dev/null
+++ b/Cargo.toml.orig
@@ -0,0 +1,26 @@
+[package]
+name = "which"
+version = "3.1.1"
+authors = ["Harry Fei <tiziyuanfang@gmail.com>"]
+repository = "https://github.com/harryfei/which-rs.git"
+documentation = "https://docs.rs/which/"
+license = "MIT"
+description = "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
+readme = "README.md"
+categories = ["os", "filesystem"]
+keywords = ["which", "which-rs", "unix", "command"]
+
+[dependencies]
+libc = "0.2.65"
+
+[dependencies.failure]
+version = "0.1.7"
+default-features = false
+features = ["std"]
+optional = true
+
+[dev-dependencies]
+tempdir = "0.3.7"
+
+[features]
+default = ["failure"]
diff --git a/LICENSE b/LICENSE
new file mode 120000
index 0000000..85de3d4
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1 @@
+LICENSE.txt
\ No newline at end of file
diff --git a/LICENSE.txt b/LICENSE.txt
new file mode 100644
index 0000000..369139b
--- /dev/null
+++ b/LICENSE.txt
@@ -0,0 +1,19 @@
+Copyright (c) 2015 fangyuanziti
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/METADATA b/METADATA
new file mode 100644
index 0000000..9595f0d
--- /dev/null
+++ b/METADATA
@@ -0,0 +1,19 @@
+name: "which"
+description: "A Rust equivalent of Unix command \"which\". Locate installed executable in cross platforms."
+third_party {
+  url {
+    type: HOMEPAGE
+    value: "https://crates.io/crates/which"
+  }
+  url {
+    type: GIT
+    value: "https://github.com/harryfei/which-rs.git"
+  }
+  version: "3.1.1"
+  license_type: NOTICE
+  last_upgrade_date {
+    year: 2020
+    month: 3
+    day: 31
+  }
+}
diff --git a/MODULE_LICENSE_BSD_LIKE b/MODULE_LICENSE_BSD_LIKE
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/MODULE_LICENSE_BSD_LIKE
diff --git a/OWNERS b/OWNERS
new file mode 100644
index 0000000..46fc303
--- /dev/null
+++ b/OWNERS
@@ -0,0 +1 @@
+include platform/prebuilts/rust:/OWNERS
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..2c8d7e1
--- /dev/null
+++ b/README.md
@@ -0,0 +1,34 @@
+[![Travis Build Status](https://travis-ci.org/harryfei/which-rs.svg?branch=master)](https://travis-ci.org/harryfei/which-rs)
+[![Appveyor Build status](https://ci.appveyor.com/api/projects/status/1y40b135iaixs9x6?svg=true)](https://ci.appveyor.com/project/HarryFei/which-rs)
+
+# which
+
+A Rust equivalent of Unix command "which". Locate installed executable in cross platforms.
+
+## Support platforms
+
+* Linux
+* Windows
+* macOS
+
+## Example
+
+To find which rustc exectable binary is using.
+
+``` rust
+use which::which;
+
+let result = which::which("rustc").unwrap();
+assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
+```
+
+## Errors
+
+By default this crate exposes a [`failure`] based error. This is optional, disable the default
+features to get an error type implementing the standard library `Error` trait.
+
+[`failure`]: https://crates.io/crates/failure
+
+## Documentation
+
+The documentation is [available online](https://docs.rs/which/).
diff --git a/appveyor.yml b/appveyor.yml
new file mode 100644
index 0000000..e8beb07
--- /dev/null
+++ b/appveyor.yml
@@ -0,0 +1,29 @@
+os: Visual Studio 2015
+
+environment:
+  matrix:
+  - channel: stable
+    target: x86_64-pc-windows-msvc
+  - channel: stable
+    target: i686-pc-windows-msvc
+  - channel: stable
+    target: x86_64-pc-windows-gnu
+  - channel: stable
+    target: i686-pc-windows-gnu
+install:
+  # Set PATH for MinGW toolset
+  - if %target% == x86_64-pc-windows-gnu set PATH=%PATH%;C:\msys64\mingw64\bin
+  - if %target% == i686-pc-windows-gnu set PATH=%PATH%;C:\msys64\mingw32\bin
+
+  # Install Rust toolset
+  - appveyor DownloadFile https://win.rustup.rs/ -FileName rustup-init.exe
+  - rustup-init -yv --default-toolchain %channel% --default-host %target%
+  - set PATH=%PATH%;%USERPROFILE%\.cargo\bin
+  - rustc -vV
+  - cargo -vV
+
+build: false
+
+test_script:
+  - cargo test
+  - cargo test --no-default-features
diff --git a/src/checker.rs b/src/checker.rs
new file mode 100644
index 0000000..6021711
--- /dev/null
+++ b/src/checker.rs
@@ -0,0 +1,70 @@
+use finder::Checker;
+#[cfg(unix)]
+use libc;
+#[cfg(unix)]
+use std::ffi::CString;
+use std::fs;
+#[cfg(unix)]
+use std::os::unix::ffi::OsStrExt;
+use std::path::Path;
+
+pub struct ExecutableChecker;
+
+impl ExecutableChecker {
+    pub fn new() -> ExecutableChecker {
+        ExecutableChecker
+    }
+}
+
+impl Checker for ExecutableChecker {
+    #[cfg(unix)]
+    fn is_valid(&self, path: &Path) -> bool {
+        CString::new(path.as_os_str().as_bytes())
+            .and_then(|c| Ok(unsafe { libc::access(c.as_ptr(), libc::X_OK) == 0 }))
+            .unwrap_or(false)
+    }
+
+    #[cfg(windows)]
+    fn is_valid(&self, _path: &Path) -> bool {
+        true
+    }
+}
+
+pub struct ExistedChecker;
+
+impl ExistedChecker {
+    pub fn new() -> ExistedChecker {
+        ExistedChecker
+    }
+}
+
+impl Checker for ExistedChecker {
+    fn is_valid(&self, path: &Path) -> bool {
+        fs::metadata(path)
+            .map(|metadata| metadata.is_file())
+            .unwrap_or(false)
+    }
+}
+
+pub struct CompositeChecker {
+    checkers: Vec<Box<dyn Checker>>,
+}
+
+impl CompositeChecker {
+    pub fn new() -> CompositeChecker {
+        CompositeChecker {
+            checkers: Vec::new(),
+        }
+    }
+
+    pub fn add_checker(mut self, checker: Box<dyn Checker>) -> CompositeChecker {
+        self.checkers.push(checker);
+        self
+    }
+}
+
+impl Checker for CompositeChecker {
+    fn is_valid(&self, path: &Path) -> bool {
+        self.checkers.iter().all(|checker| checker.is_valid(path))
+    }
+}
diff --git a/src/error.rs b/src/error.rs
new file mode 100644
index 0000000..c75fe4c
--- /dev/null
+++ b/src/error.rs
@@ -0,0 +1,92 @@
+#[cfg(feature = "failure")]
+use failure::{Backtrace, Context, Fail};
+use std;
+use std::fmt::{self, Display};
+
+#[derive(Debug)]
+pub struct Error {
+    #[cfg(feature = "failure")]
+    inner: Context<ErrorKind>,
+    #[cfg(not(feature = "failure"))]
+    inner: ErrorKind,
+}
+
+// To suppress false positives from cargo-clippy
+#[cfg_attr(feature = "cargo-clippy", allow(empty_line_after_outer_attr))]
+#[derive(Copy, Clone, Eq, PartialEq, Debug)]
+pub enum ErrorKind {
+    BadAbsolutePath,
+    BadRelativePath,
+    CannotFindBinaryPath,
+    CannotGetCurrentDir,
+    CannotCanonicalize,
+}
+
+#[cfg(feature = "failure")]
+impl Fail for ErrorKind {}
+
+impl Display for ErrorKind {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        let display = match *self {
+            ErrorKind::BadAbsolutePath => "Bad absolute path",
+            ErrorKind::BadRelativePath => "Bad relative path",
+            ErrorKind::CannotFindBinaryPath => "Cannot find binary path",
+            ErrorKind::CannotGetCurrentDir => "Cannot get current directory",
+            ErrorKind::CannotCanonicalize => "Cannot canonicalize path",
+        };
+        f.write_str(display)
+    }
+}
+
+#[cfg(feature = "failure")]
+impl Fail for Error {
+    fn cause(&self) -> Option<&dyn Fail> {
+        self.inner.cause()
+    }
+
+    fn backtrace(&self) -> Option<&Backtrace> {
+        self.inner.backtrace()
+    }
+}
+
+#[cfg(not(feature = "failure"))]
+impl std::error::Error for Error {}
+
+impl Display for Error {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        Display::fmt(&self.inner, f)
+    }
+}
+
+impl Error {
+    pub fn kind(&self) -> ErrorKind {
+        #[cfg(feature = "failure")]
+        {
+            *self.inner.get_context()
+        }
+        #[cfg(not(feature = "failure"))]
+        {
+            self.inner
+        }
+    }
+}
+
+impl From<ErrorKind> for Error {
+    fn from(kind: ErrorKind) -> Error {
+        Error {
+            #[cfg(feature = "failure")]
+            inner: Context::new(kind),
+            #[cfg(not(feature = "failure"))]
+            inner: kind,
+        }
+    }
+}
+
+#[cfg(feature = "failure")]
+impl From<Context<ErrorKind>> for Error {
+    fn from(inner: Context<ErrorKind>) -> Error {
+        Error { inner }
+    }
+}
+
+pub type Result<T> = std::result::Result<T, Error>;
diff --git a/src/finder.rs b/src/finder.rs
new file mode 100644
index 0000000..2519aa8
--- /dev/null
+++ b/src/finder.rs
@@ -0,0 +1,155 @@
+use error::*;
+#[cfg(windows)]
+use helper::has_executable_extension;
+use std::env;
+use std::ffi::OsStr;
+#[cfg(windows)]
+use std::ffi::OsString;
+use std::iter;
+use std::path::{Path, PathBuf};
+
+pub trait Checker {
+    fn is_valid(&self, path: &Path) -> bool;
+}
+
+trait PathExt {
+    fn has_separator(&self) -> bool;
+
+    fn to_absolute<P>(self, cwd: P) -> PathBuf
+    where
+        P: AsRef<Path>;
+}
+
+impl PathExt for PathBuf {
+    fn has_separator(&self) -> bool {
+        self.components().count() > 1
+    }
+
+    fn to_absolute<P>(self, cwd: P) -> PathBuf
+    where
+        P: AsRef<Path>,
+    {
+        if self.is_absolute() {
+            self
+        } else {
+            let mut new_path = PathBuf::from(cwd.as_ref());
+            new_path.push(self);
+            new_path
+        }
+    }
+}
+
+pub struct Finder;
+
+impl Finder {
+    pub fn new() -> Finder {
+        Finder
+    }
+
+    pub fn find<T, U, V>(
+        &self,
+        binary_name: T,
+        paths: Option<U>,
+        cwd: V,
+        binary_checker: &dyn Checker,
+    ) -> Result<PathBuf>
+    where
+        T: AsRef<OsStr>,
+        U: AsRef<OsStr>,
+        V: AsRef<Path>,
+    {
+        let path = PathBuf::from(&binary_name);
+
+        let binary_path_candidates: Box<dyn Iterator<Item = _>> = if path.has_separator() {
+            // Search binary in cwd if the path have a path separator.
+            let candidates = Self::cwd_search_candidates(path, cwd).into_iter();
+            Box::new(candidates)
+        } else {
+            // Search binary in PATHs(defined in environment variable).
+            let p = paths.ok_or(ErrorKind::CannotFindBinaryPath)?;
+            let paths: Vec<_> = env::split_paths(&p).collect();
+
+            let candidates = Self::path_search_candidates(path, paths).into_iter();
+
+            Box::new(candidates)
+        };
+
+        for p in binary_path_candidates {
+            // find a valid binary
+            if binary_checker.is_valid(&p) {
+                return Ok(p);
+            }
+        }
+
+        // can't find any binary
+        return Err(ErrorKind::CannotFindBinaryPath.into());
+    }
+
+    fn cwd_search_candidates<C>(binary_name: PathBuf, cwd: C) -> impl IntoIterator<Item = PathBuf>
+    where
+        C: AsRef<Path>,
+    {
+        let path = binary_name.to_absolute(cwd);
+
+        Self::append_extension(iter::once(path))
+    }
+
+    fn path_search_candidates<P>(
+        binary_name: PathBuf,
+        paths: P,
+    ) -> impl IntoIterator<Item = PathBuf>
+    where
+        P: IntoIterator<Item = PathBuf>,
+    {
+        let new_paths = paths.into_iter().map(move |p| p.join(binary_name.clone()));
+
+        Self::append_extension(new_paths)
+    }
+
+    #[cfg(unix)]
+    fn append_extension<P>(paths: P) -> impl IntoIterator<Item = PathBuf>
+    where
+        P: IntoIterator<Item = PathBuf>,
+    {
+        paths
+    }
+
+    #[cfg(windows)]
+    fn append_extension<P>(paths: P) -> impl IntoIterator<Item = PathBuf>
+    where
+        P: IntoIterator<Item = PathBuf>,
+    {
+        // Read PATHEXT env variable and split it into vector of String
+        let path_exts =
+            env::var_os("PATHEXT").unwrap_or(OsString::from(env::consts::EXE_EXTENSION));
+
+        let exe_extension_vec = env::split_paths(&path_exts)
+            .filter_map(|e| e.to_str().map(|e| e.to_owned()))
+            .collect::<Vec<_>>();
+
+        paths
+            .into_iter()
+            .flat_map(move |p| -> Box<dyn Iterator<Item = _>> {
+                // Check if path already have executable extension
+                if has_executable_extension(&p, &exe_extension_vec) {
+                    Box::new(iter::once(p))
+                } else {
+                    // Appended paths with windows executable extensions.
+                    // e.g. path `c:/windows/bin` will expend to:
+                    // c:/windows/bin.COM
+                    // c:/windows/bin.EXE
+                    // c:/windows/bin.CMD
+                    // ...
+                    let ps = exe_extension_vec.clone().into_iter().map(move |e| {
+                        // Append the extension.
+                        let mut p = p.clone().to_path_buf().into_os_string();
+                        p.push(e);
+
+                        PathBuf::from(p)
+                    });
+
+                    Box::new(ps)
+                }
+            })
+    }
+}
diff --git a/src/helper.rs b/src/helper.rs
new file mode 100644
index 0000000..71658a0
--- /dev/null
+++ b/src/helper.rs
@@ -0,0 +1,40 @@
+use std::path::Path;
+
+/// Check if given path has extension which in the given vector.
+pub fn has_executable_extension<T: AsRef<Path>, S: AsRef<str>>(path: T, exts_vec: &Vec<S>) -> bool {
+    let ext = path.as_ref().extension().and_then(|e| e.to_str());
+    match ext {
+        Some(ext) => exts_vec
+            .iter()
+            .any(|e| ext.eq_ignore_ascii_case(&e.as_ref()[1..])),
+        _ => false,
+    }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::path::PathBuf;
+
+    #[test]
+    fn test_extension_in_extension_vector() {
+        // Case insensitive
+        assert!(has_executable_extension(
+            PathBuf::from("foo.exe"),
+            &vec![".COM", ".EXE", ".CMD"]
+        ));
+
+        assert!(has_executable_extension(
+            PathBuf::from("foo.CMD"),
+            &vec![".COM", ".EXE", ".CMD"]
+        ));
+    }
+
+    #[test]
+    fn test_extension_not_in_extension_vector() {
+        assert!(!has_executable_extension(
+            PathBuf::from("foo.bar"),
+            &vec![".COM", ".EXE", ".CMD"]
+        ));
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..42a6963
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,271 @@
+//! which
+//!
+//! A Rust equivalent of Unix command `which(1)`.
+//! # Example:
+//!
+//! To find which rustc executable binary is using:
+//!
+//! ``` norun
+//! use which::which;
+//!
+//! let result = which::which("rustc").unwrap();
+//! assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
+//!
+//! ```
+
+#[cfg(feature = "failure")]
+extern crate failure;
+extern crate libc;
+
+#[cfg(feature = "failure")]
+use failure::ResultExt;
+mod checker;
+mod error;
+mod finder;
+#[cfg(windows)]
+mod helper;
+
+use std::env;
+use std::fmt;
+use std::path;
+
+use std::ffi::OsStr;
+
+use checker::CompositeChecker;
+use checker::ExecutableChecker;
+use checker::ExistedChecker;
+pub use error::*;
+use finder::Finder;
+
+/// Find a exectable binary's path by name.
+///
+/// If given an absolute path, returns it if the file exists and is executable.
+///
+/// If given a relative path, returns an absolute path to the file if
+/// it exists and is executable.
+///
+/// If given a string without path separators, looks for a file named
+/// `binary_name` at each directory in `$PATH` and if it finds an executable
+/// file there, returns it.
+///
+/// # Example
+///
+/// ``` norun
+/// use which::which;
+/// use std::path::PathBuf;
+///
+/// let result = which::which("rustc").unwrap();
+/// assert_eq!(result, PathBuf::from("/usr/bin/rustc"));
+///
+/// ```
+pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
+    #[cfg(feature = "failure")]
+    let cwd = env::current_dir().context(ErrorKind::CannotGetCurrentDir)?;
+    #[cfg(not(feature = "failure"))]
+    let cwd = env::current_dir().map_err(|_| ErrorKind::CannotGetCurrentDir)?;
+
+    which_in(binary_name, env::var_os("PATH"), &cwd)
+}
+
+/// Find `binary_name` in the path list `paths`, using `cwd` to resolve relative paths.
+pub fn which_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<path::PathBuf>
+where
+    T: AsRef<OsStr>,
+    U: AsRef<OsStr>,
+    V: AsRef<path::Path>,
+{
+    let binary_checker = CompositeChecker::new()
+        .add_checker(Box::new(ExistedChecker::new()))
+        .add_checker(Box::new(ExecutableChecker::new()));
+
+    let finder = Finder::new();
+
+    finder.find(binary_name, paths, cwd, &binary_checker)
+}
+
+/// An owned, immutable wrapper around a `PathBuf` containing the path of an executable.
+///
+/// The constructed `PathBuf` is the output of `which` or `which_in`, but `which::Path` has the
+/// advantage of being a type distinct from `std::path::Path` and `std::path::PathBuf`.
+///
+/// It can be beneficial to use `which::Path` instead of `std::path::Path` when you want the type
+/// system to enforce the need for a path that exists and points to a binary that is executable.
+///
+/// Since `which::Path` implements `Deref` for `std::path::Path`, all methods on `&std::path::Path`
+/// are also available to `&which::Path` values.
+#[derive(Clone, PartialEq)]
+pub struct Path {
+    inner: path::PathBuf,
+}
+
+impl Path {
+    /// Returns the path of an executable binary by name.
+    ///
+    /// This calls `which` and maps the result into a `Path`.
+    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
+        which(binary_name).map(|inner| Path { inner })
+    }
+
+    /// Returns the path of an executable binary by name in the path list `paths` and using the
+    /// current working directory `cwd` to resolve relative paths.
+    ///
+    /// This calls `which_in` and maps the result into a `Path`.
+    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<Path>
+    where
+        T: AsRef<OsStr>,
+        U: AsRef<OsStr>,
+        V: AsRef<path::Path>,
+    {
+        which_in(binary_name, paths, cwd).map(|inner| Path { inner })
+    }
+
+    /// Returns a reference to a `std::path::Path`.
+    pub fn as_path(&self) -> &path::Path {
+        self.inner.as_path()
+    }
+
+    /// Consumes the `which::Path`, yielding its underlying `std::path::PathBuf`.
+    pub fn into_path_buf(self) -> path::PathBuf {
+        self.inner
+    }
+}
+
+impl fmt::Debug for Path {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&self.inner, f)
+    }
+}
+
+impl std::ops::Deref for Path {
+    type Target = path::Path;
+
+    fn deref(&self) -> &path::Path {
+        self.inner.deref()
+    }
+}
+
+impl AsRef<path::Path> for Path {
+    fn as_ref(&self) -> &path::Path {
+        self.as_path()
+    }
+}
+
+impl AsRef<OsStr> for Path {
+    fn as_ref(&self) -> &OsStr {
+        self.as_os_str()
+    }
+}
+
+impl Eq for Path {}
+
+impl PartialEq<path::PathBuf> for Path {
+    fn eq(&self, other: &path::PathBuf) -> bool {
+        self.inner == *other
+    }
+}
+
+impl PartialEq<Path> for path::PathBuf {
+    fn eq(&self, other: &Path) -> bool {
+        *self == other.inner
+    }
+}
+
+/// An owned, immutable wrapper around a `PathBuf` containing the _canonical_ path of an
+/// executable.
+///
+/// The constructed `PathBuf` is the result of `which` or `which_in` followed by
+/// `Path::canonicalize`, but `CanonicalPath` has the advantage of being a type distinct from
+/// `std::path::Path` and `std::path::PathBuf`.
+///
+/// It can be beneficial to use `CanonicalPath` instead of `std::path::Path` when you want the type
+/// system to enforce the need for a path that exists, points to a binary that is executable, is
+/// absolute, has all components normalized, and has all symbolic links resolved
+///
+/// Since `CanonicalPath` implements `Deref` for `std::path::Path`, all methods on
+/// `&std::path::Path` are also available to `&CanonicalPath` values.
+#[derive(Clone, PartialEq)]
+pub struct CanonicalPath {
+    inner: path::PathBuf,
+}
+
+impl CanonicalPath {
+    /// Returns the canonical path of an executable binary by name.
+    ///
+    /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
+    pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
+        which(binary_name)
+            .and_then(|p| {
+                p.canonicalize()
+                    .map_err(|_| ErrorKind::CannotCanonicalize.into())
+            })
+            .map(|inner| CanonicalPath { inner })
+    }
+
+    /// Returns the canonical path of an executable binary by name in the path list `paths` and
+    /// using the current working directory `cwd` to resolve relative paths.
+    ///
+    /// This calls `which` and `Path::canonicalize` and maps the result into a `CanonicalPath`.
+    pub fn new_in<T, U, V>(binary_name: T, paths: Option<U>, cwd: V) -> Result<CanonicalPath>
+    where
+        T: AsRef<OsStr>,
+        U: AsRef<OsStr>,
+        V: AsRef<path::Path>,
+    {
+        which_in(binary_name, paths, cwd)
+            .and_then(|p| {
+                p.canonicalize()
+                    .map_err(|_| ErrorKind::CannotCanonicalize.into())
+            })
+            .map(|inner| CanonicalPath { inner })
+    }
+
+    /// Returns a reference to a `std::path::Path`.
+    pub fn as_path(&self) -> &path::Path {
+        self.inner.as_path()
+    }
+
+    /// Consumes the `which::CanonicalPath`, yielding its underlying `std::path::PathBuf`.
+    pub fn into_path_buf(self) -> path::PathBuf {
+        self.inner
+    }
+}
+
+impl fmt::Debug for CanonicalPath {
+    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+        fmt::Debug::fmt(&self.inner, f)
+    }
+}
+
+impl std::ops::Deref for CanonicalPath {
+    type Target = path::Path;
+
+    fn deref(&self) -> &path::Path {
+        self.inner.deref()
+    }
+}
+
+impl AsRef<path::Path> for CanonicalPath {
+    fn as_ref(&self) -> &path::Path {
+        self.as_path()
+    }
+}
+
+impl AsRef<OsStr> for CanonicalPath {
+    fn as_ref(&self) -> &OsStr {
+        self.as_os_str()
+    }
+}
+
+impl Eq for CanonicalPath {}
+
+impl PartialEq<path::PathBuf> for CanonicalPath {
+    fn eq(&self, other: &path::PathBuf) -> bool {
+        self.inner == *other
+    }
+}
+
+impl PartialEq<CanonicalPath> for path::PathBuf {
+    fn eq(&self, other: &CanonicalPath) -> bool {
+        *self == other.inner
+    }
+}
diff --git a/tests/basic.rs b/tests/basic.rs
new file mode 100644
index 0000000..b6ba30d
--- /dev/null
+++ b/tests/basic.rs
@@ -0,0 +1,307 @@
+extern crate tempdir;
+extern crate which;
+
+use std::env;
+use std::ffi::{OsStr, OsString};
+use std::fs;
+use std::io;
+use std::path::{Path, PathBuf};
+use tempdir::TempDir;
+
+struct TestFixture {
+    /// Temp directory.
+    pub tempdir: TempDir,
+    /// $PATH
+    pub paths: OsString,
+    /// Binaries created in $PATH
+    pub bins: Vec<PathBuf>,
+}
+
+const SUBDIRS: &'static [&'static str] = &["a", "b", "c"];
+const BIN_NAME: &'static str = "bin";
+
+#[cfg(unix)]
+fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
+    use std::os::unix::fs::OpenOptionsExt;
+    let bin = dir.join(path).with_extension(extension);
+    fs::OpenOptions::new()
+        .write(true)
+        .create(true)
+        .mode(0o666 | (libc::S_IXUSR as u32))
+        .open(&bin)
+        .and_then(|_f| bin.canonicalize())
+}
+
+fn touch(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
+    let b = dir.join(path).with_extension(extension);
+    fs::File::create(&b).and_then(|_f| b.canonicalize())
+}
+
+#[cfg(windows)]
+fn mk_bin(dir: &Path, path: &str, extension: &str) -> io::Result<PathBuf> {
+    touch(dir, path, extension)
+}
+
+impl TestFixture {
+    // tmp/a/bin
+    // tmp/a/bin.exe
+    // tmp/a/bin.cmd
+    // tmp/b/bin
+    // tmp/b/bin.exe
+    // tmp/b/bin.cmd
+    // tmp/c/bin
+    // tmp/c/bin.exe
+    // tmp/c/bin.cmd
+    pub fn new() -> TestFixture {
+        let tempdir = TempDir::new("which_tests").unwrap();
+        let mut builder = fs::DirBuilder::new();
+        builder.recursive(true);
+        let mut paths = vec![];
+        let mut bins = vec![];
+        for d in SUBDIRS.iter() {
+            let p = tempdir.path().join(d);
+            builder.create(&p).unwrap();
+            bins.push(mk_bin(&p, &BIN_NAME, "").unwrap());
+            bins.push(mk_bin(&p, &BIN_NAME, "exe").unwrap());
+            bins.push(mk_bin(&p, &BIN_NAME, "cmd").unwrap());
+            paths.push(p);
+        }
+        TestFixture {
+            tempdir: tempdir,
+            paths: env::join_paths(paths).unwrap(),
+            bins: bins,
+        }
+    }
+
+    #[allow(dead_code)]
+    pub fn touch(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
+        touch(self.tempdir.path(), &path, &extension)
+    }
+
+    pub fn mk_bin(&self, path: &str, extension: &str) -> io::Result<PathBuf> {
+        mk_bin(self.tempdir.path(), &path, &extension)
+    }
+}
+
+fn _which<T: AsRef<OsStr>>(f: &TestFixture, path: T) -> which::Result<which::CanonicalPath> {
+    which::CanonicalPath::new_in(path, Some(f.paths.clone()), f.tempdir.path())
+}
+
+#[test]
+#[cfg(unix)]
+fn it_works() {
+    use std::process::Command;
+    let result = which::Path::new("rustc");
+    assert!(result.is_ok());
+
+    let which_result = Command::new("which").arg("rustc").output();
+
+    assert_eq!(
+        String::from(result.unwrap().to_str().unwrap()),
+        String::from_utf8(which_result.unwrap().stdout)
+            .unwrap()
+            .trim()
+    );
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which() {
+    let f = TestFixture::new();
+    assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[0])
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which() {
+    let f = TestFixture::new();
+    assert_eq!(_which(&f, &BIN_NAME).unwrap(), f.bins[1])
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_extension() {
+    let f = TestFixture::new();
+    let b = Path::new(&BIN_NAME).with_extension("");
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[0])
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_extension() {
+    let f = TestFixture::new();
+    let b = Path::new(&BIN_NAME).with_extension("cmd");
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[2])
+}
+
+#[test]
+fn test_which_not_found() {
+    let f = TestFixture::new();
+    assert!(_which(&f, "a").is_err());
+}
+
+#[test]
+fn test_which_second() {
+    let f = TestFixture::new();
+    let b = f.mk_bin("b/another", env::consts::EXE_EXTENSION).unwrap();
+    assert_eq!(_which(&f, "another").unwrap(), b);
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_absolute() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, &f.bins[3]).unwrap(),
+        f.bins[3].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_absolute() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, &f.bins[4]).unwrap(),
+        f.bins[4].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_absolute_path_case() {
+    // Test that an absolute path with an uppercase extension
+    // is accepted.
+    let f = TestFixture::new();
+    let p = &f.bins[4];
+    assert_eq!(_which(&f, &p).unwrap(), f.bins[4].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_absolute_extension() {
+    let f = TestFixture::new();
+    // Don't append EXE_EXTENSION here.
+    let b = f.bins[3].parent().unwrap().join(&BIN_NAME);
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_absolute_extension() {
+    let f = TestFixture::new();
+    // Don't append EXE_EXTENSION here.
+    let b = f.bins[4].parent().unwrap().join(&BIN_NAME);
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_relative() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, "b/bin").unwrap(),
+        f.bins[3].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_relative() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, "b/bin").unwrap(),
+        f.bins[4].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_relative_extension() {
+    // test_which_relative tests a relative path without an extension,
+    // so test a relative path with an extension here.
+    let f = TestFixture::new();
+    let b = Path::new("b/bin").with_extension(env::consts::EXE_EXTENSION);
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[3].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_relative_extension() {
+    // test_which_relative tests a relative path without an extension,
+    // so test a relative path with an extension here.
+    let f = TestFixture::new();
+    let b = Path::new("b/bin").with_extension("cmd");
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[5].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_relative_extension_case() {
+    // Test that a relative path with an uppercase extension
+    // is accepted.
+    let f = TestFixture::new();
+    let b = Path::new("b/bin").with_extension("EXE");
+    assert_eq!(_which(&f, &b).unwrap(), f.bins[4].canonicalize().unwrap());
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_relative_leading_dot() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, "./b/bin").unwrap(),
+        f.bins[3].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(windows)]
+fn test_which_relative_leading_dot() {
+    let f = TestFixture::new();
+    assert_eq!(
+        _which(&f, "./b/bin").unwrap(),
+        f.bins[4].canonicalize().unwrap()
+    );
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_non_executable() {
+    // Shouldn't return non-executable files.
+    let f = TestFixture::new();
+    f.touch("b/another", "").unwrap();
+    assert!(_which(&f, "another").is_err());
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_absolute_non_executable() {
+    // Shouldn't return non-executable files, even if given an absolute path.
+    let f = TestFixture::new();
+    let b = f.touch("b/another", "").unwrap();
+    assert!(_which(&f, &b).is_err());
+}
+
+#[test]
+#[cfg(unix)]
+fn test_which_relative_non_executable() {
+    // Shouldn't return non-executable files.
+    let f = TestFixture::new();
+    f.touch("b/another", "").unwrap();
+    assert!(_which(&f, "b/another").is_err());
+}
+
+#[test]
+#[cfg(feature = "failure")]
+fn test_failure() {
+    let f = TestFixture::new();
+
+    let run = || -> std::result::Result<PathBuf, failure::Error> {
+        // Test the conversion to failure
+        let p = _which(&f, "./b/bin")?;
+        Ok(p.into_path_buf())
+    };
+
+    let _ = run();
+}