mod checker;
mod error;
mod finder;
mod helper;
pub mod sys;
use std::fmt;
use std::path;
use std::ffi::{OsStr, OsString};
pub use crate::error::*;
use crate::finder::Finder;
use crate::sys::Sys;
#[cfg(feature = "real-sys")]
pub fn which<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
which_all(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}
#[cfg(feature = "real-sys")]
pub fn which_global<T: AsRef<OsStr>>(binary_name: T) -> Result<path::PathBuf> {
which_all_global(binary_name).and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}
#[cfg(feature = "real-sys")]
pub fn which_all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = path::PathBuf>> {
let cwd = sys::RealSys.current_dir().ok();
Finder::new(&sys::RealSys).find(binary_name, sys::RealSys.env_path(), cwd, Noop)
}
#[cfg(feature = "real-sys")]
pub fn which_all_global<T: AsRef<OsStr>>(
binary_name: T,
) -> Result<impl Iterator<Item = path::PathBuf>> {
Finder::new(&sys::RealSys).find(
binary_name,
sys::RealSys.env_path(),
Option::<&Path>::None,
Noop,
)
}
#[cfg(all(feature = "regex", feature = "real-sys"))]
pub fn which_re(
regex: impl std::borrow::Borrow<Regex>,
) -> Result<impl Iterator<Item = path::PathBuf>> {
which_re_in(regex, sys::RealSys.env_path())
}
#[cfg(feature = "real-sys")]
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>,
{
which_in_all(binary_name, paths, cwd)
.and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}
#[cfg(all(feature = "regex", feature = "real-sys"))]
pub fn which_re_in<T>(
regex: impl std::borrow::Borrow<Regex>,
paths: Option<T>,
) -> Result<impl Iterator<Item = path::PathBuf>>
where
T: AsRef<OsStr>,
{
Finder::new(&sys::RealSys).find_re(regex, paths, Noop)
}
#[cfg(feature = "real-sys")]
pub fn which_in_all<'a, T, U, V>(
binary_name: T,
paths: Option<U>,
cwd: V,
) -> Result<impl Iterator<Item = path::PathBuf> + 'a>
where
T: AsRef<OsStr>,
U: AsRef<OsStr>,
V: AsRef<path::Path> + 'a,
{
Finder::new(&sys::RealSys).find(binary_name, paths, Some(cwd), Noop)
}
#[cfg(feature = "real-sys")]
pub fn which_in_global<T, U>(
binary_name: T,
paths: Option<U>,
) -> Result<impl Iterator<Item = path::PathBuf>>
where
T: AsRef<OsStr>,
U: AsRef<OsStr>,
{
Finder::new(&sys::RealSys).find(binary_name, paths, Option::<&Path>::None, Noop)
}
pub struct WhichConfig<TSys: sys::Sys, F = Noop> {
cwd: CwdOption,
custom_path_list: Option<OsString>,
binary_name: Option<OsString>,
nonfatal_error_handler: F,
#[cfg(feature = "regex")]
regex: Option<Regex>,
sys: TSys,
}
enum CwdOption {
Unspecified,
UseSysCwd,
RefuseCwd,
UseCustomCwd(path::PathBuf),
}
#[derive(Default, Debug, Clone)]
pub struct Noop;
pub trait NonFatalErrorHandler {
fn handle(&mut self, e: NonFatalError);
}
impl NonFatalErrorHandler for Noop {
fn handle(&mut self, _: NonFatalError) {
}
}
impl<T> NonFatalErrorHandler for T
where
T: FnMut(NonFatalError),
{
fn handle(&mut self, e: NonFatalError) {
(self)(e);
}
}
#[cfg(feature = "real-sys")]
impl<F: Default> Default for WhichConfig<&sys::RealSys, F> {
fn default() -> Self {
Self {
cwd: CwdOption::Unspecified,
custom_path_list: None,
binary_name: None,
nonfatal_error_handler: F::default(),
#[cfg(feature = "regex")]
regex: None,
sys: &sys::RealSys,
}
}
}
#[cfg(feature = "regex")]
type Regex = regex::Regex;
#[cfg(not(feature = "regex"))]
type Regex = ();
#[cfg(feature = "real-sys")]
impl WhichConfig<&sys::RealSys, Noop> {
pub fn new() -> Self {
Self::new_with_sys(&sys::RealSys)
}
}
impl<TSys: Sys> WhichConfig<TSys, Noop> {
pub fn new_with_sys(sys: TSys) -> Self {
Self {
cwd: CwdOption::Unspecified,
custom_path_list: None,
binary_name: None,
nonfatal_error_handler: Noop,
#[cfg(feature = "regex")]
regex: None,
sys,
}
}
}
impl<'a, TSys: Sys + 'a, F: NonFatalErrorHandler + 'a> WhichConfig<TSys, F> {
pub fn system_cwd(mut self, use_cwd: bool) -> Self {
#[cfg(feature = "regex")]
if self.regex.is_some() && use_cwd {
panic!("which can't use regex and cwd at the same time!")
}
self.cwd = if use_cwd {
CwdOption::UseSysCwd
} else {
CwdOption::RefuseCwd
};
self
}
pub fn custom_cwd(mut self, cwd: path::PathBuf) -> Self {
#[cfg(feature = "regex")]
if self.regex.is_some() {
panic!("which can't use regex and cwd at the same time!")
}
self.cwd = CwdOption::UseCustomCwd(cwd);
self
}
#[allow(unused_variables)]
#[allow(unused_mut)]
pub fn regex(mut self, regex: Regex) -> Self {
#[cfg(not(feature = "regex"))]
{
panic!("which's regex feature was not enabled in your Cargo.toml!")
}
#[cfg(feature = "regex")]
{
if matches!(self.cwd, CwdOption::UseSysCwd)
|| matches!(self.cwd, CwdOption::UseCustomCwd(_))
{
panic!("which can't use regex and cwd at the same time!")
}
if self.binary_name.is_some() {
panic!("which can't use `binary_name` and `regex` at the same time!");
}
self.regex = Some(regex);
self
}
}
pub fn binary_name(mut self, name: OsString) -> Self {
#[cfg(feature = "regex")]
if self.regex.is_some() {
panic!("which can't use `binary_name` and `regex` at the same time!");
}
self.binary_name = Some(name);
self
}
pub fn custom_path_list(mut self, custom_path_list: OsString) -> Self {
self.custom_path_list = Some(custom_path_list);
self
}
pub fn system_path_list(mut self) -> Self {
self.custom_path_list = None;
self
}
pub fn nonfatal_error_handler<NewF>(self, handler: NewF) -> WhichConfig<TSys, NewF> {
WhichConfig {
custom_path_list: self.custom_path_list,
cwd: self.cwd,
binary_name: self.binary_name,
nonfatal_error_handler: handler,
#[cfg(feature = "regex")]
regex: self.regex,
sys: self.sys,
}
}
pub fn first_result(self) -> Result<path::PathBuf> {
self.all_results()
.and_then(|mut i| i.next().ok_or(Error::CannotFindBinaryPath))
}
pub fn all_results(self) -> Result<impl Iterator<Item = path::PathBuf> + 'a> {
let paths = self.custom_path_list.or_else(|| self.sys.env_path());
#[cfg(feature = "regex")]
if let Some(regex) = self.regex {
return Finder::new(self.sys)
.find_re(regex, paths, self.nonfatal_error_handler)
.map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf> + 'a>);
}
let cwd = match self.cwd {
CwdOption::RefuseCwd => None,
CwdOption::UseCustomCwd(custom) => Some(custom),
CwdOption::UseSysCwd | CwdOption::Unspecified => self.sys.current_dir().ok(),
};
Finder::new(self.sys)
.find(
self.binary_name.expect(
"binary_name not set! You must set binary_name or regex before searching!",
),
paths,
cwd,
self.nonfatal_error_handler,
)
.map(|i| Box::new(i) as Box<dyn Iterator<Item = path::PathBuf> + 'a>)
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct Path {
inner: path::PathBuf,
}
impl Path {
#[cfg(feature = "real-sys")]
pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<Path> {
which(binary_name).map(|inner| Path { inner })
}
#[cfg(feature = "real-sys")]
pub fn all<T: AsRef<OsStr>>(binary_name: T) -> Result<impl Iterator<Item = Path>> {
which_all(binary_name).map(|inner| inner.map(|inner| Path { inner }))
}
#[cfg(feature = "real-sys")]
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 })
}
#[cfg(feature = "real-sys")]
pub fn all_in<'a, T, U, V>(
binary_name: T,
paths: Option<U>,
cwd: V,
) -> Result<impl Iterator<Item = Path> + 'a>
where
T: AsRef<OsStr>,
U: AsRef<OsStr>,
V: AsRef<path::Path> + 'a,
{
which_in_all(binary_name, paths, cwd).map(|inner| inner.map(|inner| Path { inner }))
}
pub fn as_path(&self) -> &path::Path {
self.inner.as_path()
}
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 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
}
}
#[derive(Clone, PartialEq, Eq)]
pub struct CanonicalPath {
inner: path::PathBuf,
}
impl CanonicalPath {
#[cfg(feature = "real-sys")]
pub fn new<T: AsRef<OsStr>>(binary_name: T) -> Result<CanonicalPath> {
which(binary_name)
.and_then(|p| {
sys::RealSys
.canonicalize(&p)
.map_err(|_| Error::CannotCanonicalize)
})
.map(|inner| CanonicalPath { inner })
}
#[cfg(feature = "real-sys")]
pub fn all<T: AsRef<OsStr>>(
binary_name: T,
) -> Result<impl Iterator<Item = Result<CanonicalPath>>> {
which_all(binary_name).map(|inner| {
inner.map(|inner| {
sys::RealSys
.canonicalize(&inner)
.map_err(|_| Error::CannotCanonicalize)
.map(|inner| CanonicalPath { inner })
})
})
}
#[cfg(feature = "real-sys")]
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| {
sys::RealSys
.canonicalize(&p)
.map_err(|_| Error::CannotCanonicalize)
})
.map(|inner| CanonicalPath { inner })
}
#[cfg(feature = "real-sys")]
pub fn all_in<'a, T, U, V>(
binary_name: T,
paths: Option<U>,
cwd: V,
) -> Result<impl Iterator<Item = Result<CanonicalPath>> + 'a>
where
T: AsRef<OsStr>,
U: AsRef<OsStr>,
V: AsRef<path::Path> + 'a,
{
which_in_all(binary_name, paths, cwd).map(|inner| {
inner.map(|inner| {
sys::RealSys
.canonicalize(&inner)
.map_err(|_| Error::CannotCanonicalize)
.map(|inner| CanonicalPath { inner })
})
})
}
pub fn as_path(&self) -> &path::Path {
self.inner.as_path()
}
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 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
}
}