use std::env;
use std::ffi::OsString;
use std::path::Path;
type PrependedPath = Result<OsString, env::JoinPathsError>;
pub(crate) fn prepend_to_path(dir: &Path, path: Option<OsString>) -> PrependedPath {
Ok(match path {
None => env::join_paths([dir])?,
Some(path) => {
let mut paths = vec![dir.to_path_buf()];
paths.extend(env::split_paths(&path).filter(|path| path != dir));
env::join_paths(paths)?
}
})
}
#[derive(thiserror::Error, Debug)]
pub enum CurrentUserError {
#[error("User name in {0:?} environment variable cannot be decoded: {1:?}")]
NotUnicode(&'static str, std::ffi::OsString),
#[error("System error")]
System(#[from] nix::Error),
#[error("User unknown")]
Unknown,
}
pub fn current_user() -> Result<String, CurrentUserError> {
use nix::unistd::{getuid, User};
use std::env::{var, VarError::*};
match var("PGUSER") {
Ok(user) if !user.trim().is_empty() => Ok(user),
Err(NotUnicode(value)) => Err(CurrentUserError::NotUnicode("PGUSER", value)),
Ok(_) | Err(NotPresent) => match var("USER") {
Ok(user) if !user.trim().is_empty() => Ok(user),
Err(NotUnicode(value)) => Err(CurrentUserError::NotUnicode("USER", value)),
Ok(_) | Err(NotPresent) => User::from_uid(getuid())?
.map(|user| user.name)
.ok_or(CurrentUserError::Unknown),
},
}
}
pub fn percent(numerator: u64, denominator: u64) -> Option<u64> {
(0..=7).find_map(|shift| {
(numerator >> shift)
.checked_mul(100)
.and_then(|numerator| match denominator >> shift {
0 => None,
1 => Some(numerator),
d if (d >> 1) > numerator.rem_euclid(d) => Some(numerator.div_euclid(d)),
d => Some(numerator.div_euclid(d) + 1),
})
})
}
#[cfg(test)]
mod tests {
use std::env;
type TestResult = Result<(), Box<dyn std::error::Error>>;
#[test]
fn test_prepend_to_path_prepends_given_dir_to_path() -> TestResult {
let path = env::join_paths([tempfile::tempdir()?.path(), tempfile::tempdir()?.path()])?;
let tempdir = tempfile::tempdir()?;
let expected = {
let mut tmp = vec![tempdir.path().to_path_buf()];
tmp.extend(env::split_paths(&path));
env::join_paths(tmp)?
};
let observed = { super::prepend_to_path(tempdir.path(), Some(path))? };
assert_eq!(expected, observed);
Ok(())
}
#[test]
fn test_prepend_to_path_moves_dir_to_front_of_path() -> TestResult {
let tempdir = tempfile::tempdir()?;
let path = env::join_paths([
tempfile::tempdir()?.path(),
tempfile::tempdir()?.path(),
tempdir.path(),
])?;
let expected = {
let mut tmp = vec![tempdir.path().to_path_buf()];
tmp.extend(env::split_paths(&path).take(2));
env::join_paths(tmp)?
};
let observed = { super::prepend_to_path(tempdir.path(), Some(path))? };
assert_eq!(expected, observed);
Ok(())
}
#[test]
fn test_prepend_to_path_returns_given_dir_if_path_is_empty() -> TestResult {
let tempdir = tempfile::tempdir()?;
let expected = tempdir.path();
let observed = super::prepend_to_path(tempdir.path(), None)?;
assert_eq!(expected, observed);
Ok(())
}
}