1use std::env;
2use std::ffi::OsString;
3use std::path::Path;
4
5type PrependedPath = Result<OsString, env::JoinPathsError>;
6
7pub(crate) fn prepend_to_path(dir: &Path, path: Option<OsString>) -> PrependedPath {
12 Ok(match path {
13 None => env::join_paths([dir])?,
14 Some(path) => {
15 let mut paths = vec![dir.to_path_buf()];
16 paths.extend(env::split_paths(&path).filter(|path| path != dir));
17 env::join_paths(paths)?
18 }
19 })
20}
21
22#[derive(thiserror::Error, Debug)]
23pub enum CurrentUserError {
24 #[error("User name in {0:?} environment variable cannot be decoded: {1:?}")]
25 NotUnicode(&'static str, std::ffi::OsString),
26 #[error("System error")]
27 System(#[from] nix::Error),
28 #[error("User unknown")]
29 Unknown,
30}
31
32pub fn current_user() -> Result<String, CurrentUserError> {
38 use nix::unistd::{getuid, User};
39 use std::env::{var, VarError::*};
40 match var("PGUSER") {
41 Ok(user) if !user.trim().is_empty() => Ok(user),
42 Err(NotUnicode(value)) => Err(CurrentUserError::NotUnicode("PGUSER", value)),
43 Ok(_) | Err(NotPresent) => match var("USER") {
44 Ok(user) if !user.trim().is_empty() => Ok(user),
45 Err(NotUnicode(value)) => Err(CurrentUserError::NotUnicode("USER", value)),
46 Ok(_) | Err(NotPresent) => User::from_uid(getuid())?
47 .map(|user| user.name)
48 .ok_or(CurrentUserError::Unknown),
49 },
50 }
51}
52
53pub fn percent(numerator: u64, denominator: u64) -> Option<u64> {
75 (0..=7).find_map(|shift| {
77 (numerator >> shift)
78 .checked_mul(100)
79 .and_then(|numerator| match denominator >> shift {
80 0 => None,
81 1 => Some(numerator),
82 d if (d >> 1) > numerator.rem_euclid(d) => Some(numerator.div_euclid(d)),
83 d => Some(numerator.div_euclid(d) + 1),
84 })
85 })
86}
87
88#[cfg(test)]
89mod tests {
90 use std::env;
91
92 type TestResult = Result<(), Box<dyn std::error::Error>>;
93
94 #[test]
95 fn test_prepend_to_path_prepends_given_dir_to_path() -> TestResult {
96 let path = env::join_paths([tempfile::tempdir()?.path(), tempfile::tempdir()?.path()])?;
97 let tempdir = tempfile::tempdir()?;
98 let expected = {
99 let mut tmp = vec![tempdir.path().to_path_buf()];
100 tmp.extend(env::split_paths(&path));
101 env::join_paths(tmp)?
102 };
103 let observed = { super::prepend_to_path(tempdir.path(), Some(path))? };
104 assert_eq!(expected, observed);
105 Ok(())
106 }
107
108 #[test]
109 fn test_prepend_to_path_moves_dir_to_front_of_path() -> TestResult {
110 let tempdir = tempfile::tempdir()?;
111 let path = env::join_paths([
112 tempfile::tempdir()?.path(),
113 tempfile::tempdir()?.path(),
114 tempdir.path(),
115 ])?;
116 let expected = {
117 let mut tmp = vec![tempdir.path().to_path_buf()];
118 tmp.extend(env::split_paths(&path).take(2));
119 env::join_paths(tmp)?
120 };
121 let observed = { super::prepend_to_path(tempdir.path(), Some(path))? };
122 assert_eq!(expected, observed);
123 Ok(())
124 }
125
126 #[test]
127 fn test_prepend_to_path_returns_given_dir_if_path_is_empty() -> TestResult {
128 let tempdir = tempfile::tempdir()?;
129 let expected = tempdir.path();
130 let observed = super::prepend_to_path(tempdir.path(), None)?;
131 assert_eq!(expected, observed);
132 Ok(())
133 }
134}