#![warn(missing_docs)]
#![warn(clippy::doc_markdown)]
#[cfg(target_os = "linux")]
mod linux;
#[cfg(target_os = "linux")]
use linux::*;
#[cfg(windows)]
mod windows;
#[cfg(windows)]
use windows::*;
use std::env;
use std::io::{Error, ErrorKind};
use std::path::{Path, PathBuf};
#[macro_export]
macro_rules! default_paths {
() => {
$crate::StandardPaths::without_org(env!("CARGO_PKG_NAME"))
};
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LocationType {
HomeLocation,
DesktopLocation,
DocumentsLocation,
DownloadLocation,
MoviesLocation,
MusicLocation,
PicturesLocation,
ApplicationsLocation,
FontsLocation,
RuntimeLocation,
TempLocation,
GenericDataLocation,
AppDataLocation,
AppLocalDataLocation,
GenericCacheLocation,
AppCacheLocation,
ConfigLocation,
GenericConfigLocation,
AppConfigLocation,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LocateOption {
LocateBoth,
LocateFile,
LocateDirectory,
}
pub struct StandardPaths {
app_name: String,
org_name: String,
}
impl StandardPaths {
pub fn new<S>(app: S, org: S) -> StandardPaths
where
S: Into<String>,
{
StandardPaths {
app_name: app.into(),
org_name: org.into(),
}
}
pub fn without_org<S>(app: S) -> StandardPaths
where
S: Into<String>,
{
StandardPaths {
app_name: app.into(),
org_name: Default::default(),
}
}
fn append_organization_and_app(&self, path: &mut PathBuf) {
if !self.org_name.is_empty() {
path.push(&self.org_name);
}
if !self.app_name.is_empty() {
path.push(&self.app_name);
}
}
pub fn writable_location(&self, location: LocationType) -> Result<PathBuf, Error> {
self.writable_location_impl(location)
}
pub fn standard_locations(&self, location: LocationType) -> Result<Vec<PathBuf>, Error> {
self.standard_locations_impl(location)
}
pub fn find_executable<S>(name: S) -> Option<Vec<PathBuf>>
where
S: Into<String>,
{
let path_var = match env::var("PATH") {
Ok(var) => var,
_ => return None,
};
let paths: Vec<PathBuf> = env::split_paths(&path_var).collect();
StandardPaths::find_executable_in_paths(name, paths)
}
pub fn find_executable_in_paths<S, P>(name: S, paths: P) -> Option<Vec<PathBuf>>
where
S: Into<String>,
P: AsRef<Vec<PathBuf>>,
{
find_executable_in_paths_impl(name, &paths)
}
pub fn locate<P>(
&self,
location: LocationType,
name: P,
option: LocateOption,
) -> Result<Option<PathBuf>, Error>
where
P: AsRef<Path>,
{
let paths = self.standard_locations(location)?;
for mut path in paths {
path.push(&name);
match option {
LocateOption::LocateBoth => {
if path.exists() {
return Ok(Some(path));
}
}
LocateOption::LocateFile => {
if path.is_file() {
return Ok(Some(path));
}
}
LocateOption::LocateDirectory => {
if path.is_dir() {
return Ok(Some(path));
}
}
}
}
Ok(None)
}
pub fn locate_all<P>(
&self,
location: LocationType,
name: P,
option: LocateOption,
) -> Result<Option<Vec<PathBuf>>, Error>
where
P: AsRef<Path>,
{
let paths = self.standard_locations(location)?;
let mut res = Vec::new();
for mut path in paths {
path.push(&name);
match option {
LocateOption::LocateBoth => {
if path.exists() {
res.push(path);
}
}
LocateOption::LocateFile => {
if path.is_file() {
res.push(path);
}
}
LocateOption::LocateDirectory => {
if path.is_dir() {
res.push(path);
}
}
}
}
if res.is_empty() {
Ok(None)
} else {
Ok(Some(res))
}
}
#[inline]
fn home_dir_err() -> Error {
Error::new(ErrorKind::Other, "Error getting HOME directory")
}
}