/*! All functions related to `pacdef`'s internal paths. */ use std::path::PathBuf; use std::{env, path::Path}; use anyhow::{Context, Result}; use path_absolutize::Absolutize; const CONFIG_FILE_NAME: &str = "pacdef.yaml"; const CONFIG_FILE_NAME_OLD: &str = "pacdef.conf"; /// Get the group directory where all group files are located. This is /// `$XDG_CONFIG_HOME/pacdef/groups`, which defaults to `$HOME/.config/pacdef/groups`. /// /// # Errors /// /// This function returns an error if both `$XDG_CONFIG_HOME` and `$HOME` are undefined. pub fn get_group_dir() -> Result { let mut result = get_pacdef_base_dir().context("getting pacdef base dir")?; result.push("groups"); Ok(result) } /// Get the base directory for `pacdef`'s config files. /// /// # Errors /// /// This function will return an error if `$XDG_CONFIG_HOME` cannot be determined. pub(crate) fn get_pacdef_base_dir() -> Result { let mut dir = get_xdg_config_home().context("getting XDG_CONFIG_HOME")?; dir.push("pacdef"); Ok(dir) } /// Get the path to the cargo home directory. /// /// # Errors /// /// This function will return an error if neither the `$CARGO_HOME` nor /// the `$HOME` environment variables are set. pub(crate) fn get_cargo_home() -> Result { if let Ok(config) = env::var("CARGO_HOME") { Ok(config.into()) } else { let mut config = get_home_dir().context("falling back to $HOME/.cargo")?; config.push(".cargo"); Ok(config) } } /// Get the path to the XDG config directory. /// /// # Errors /// /// This function will return an error if neither the `$XDG_CONFIG_HOME` nor /// the `$HOME` environment variables are set. fn get_xdg_config_home() -> Result { if let Ok(config) = env::var("XDG_CONFIG_HOME") { Ok(config.into()) } else { let mut config = get_home_dir().context("falling back to $HOME/.config")?; config.push(".config"); Ok(config) } } /// Get the home directory of the current user from the `$HOME` environment /// variable. /// /// # Errors /// /// This function will return an error if the `$HOME` variable is not set. pub(crate) fn get_home_dir() -> Result { Ok(env::var("HOME").context("getting $HOME variable")?.into()) } /// Get the path to the pacdef config file. This is `$XDG_CONFIG_HOME/pacdef/pacdef.yaml`. /// /// # Errors /// /// This function returns an error if both `$XDG_CONFIG_HOME` and `$HOME` are /// undefined. pub fn get_config_path() -> Result { let mut file = get_pacdef_base_dir().context("getting pacdef base dir for config file")?; file.push(CONFIG_FILE_NAME); Ok(file) } /// Get the path to the pacdef config file from version 0.x. This is /// `$XDG_CONFIG_HOME/pacdef/pacdef.conf`. /// /// # Errors /// /// This function returns an error if both `$XDG_CONFIG_HOME` and `$HOME` are /// undefined. pub fn get_config_path_old_version() -> Result { let mut file = get_pacdef_base_dir().context("getting pacdef base dir for config file")?; file.push(CONFIG_FILE_NAME_OLD); Ok(file) } /// Determine if a program `name` exists in the folders in the `$PATH` variable. /// /// # Errors /// /// This function returns an error if `$PATH` is not set. pub(crate) fn binary_in_path(name: &str) -> Result { let paths = env::var_os("PATH").context("getting $PATH")?; for dir in env::split_paths(&paths) { let full_path = dir.join(name); if full_path.is_file() { return Ok(true); } } Ok(false) } /// Determine the relative path of `full_path` in relation to `base_path`. /// /// # Panics /// /// Panics if at least one element in `base_path` does not match the corresponding /// element in `full_path`. pub(crate) fn get_relative_path

(full_path: P, base_path: P) -> PathBuf where P: AsRef, { let mut file_iter = full_path.as_ref().iter(); base_path .as_ref() .iter() .zip(&mut file_iter) .for_each(|(a, b)| assert_eq!(a, b)); let relative_path: PathBuf = file_iter.collect(); relative_path } /// For each file argument, return the absolute path to the file. pub(crate) fn get_absolutized_file_paths(arg_match: &[String]) -> Result> { let mut result = vec![]; for item in arg_match { let path: PathBuf = item.into(); let absolute = path .absolutize() .with_context(|| format!("absolutizing {path:?}"))? .into_owned(); result.push(absolute); } Ok(result) } #[cfg(test)] mod tests { use std::path::PathBuf; use super::get_relative_path; #[test] fn relative_path() { let full = PathBuf::from("/a/b/c/d/e"); let base = PathBuf::from("/a/b/c"); let relative = get_relative_path(full, base); assert_eq!(relative, PathBuf::from("d/e")); } #[test] #[should_panic] fn relative_path_panic() { let full = PathBuf::from("/a/b/z/d/e"); let base = PathBuf::from("/a/b/c"); get_relative_path(full, base); } }