use std::path::{Path, PathBuf};
use std::sync::OnceLock;
pub(crate) fn data_home() -> PathBuf {
std::env::var("XDG_DATA_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| user_home().join(".local/share"))
}
pub(crate) fn cache_home() -> PathBuf {
std::env::var("XDG_CACHE_HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| user_home().join(".cache"))
}
pub(crate) fn runtime_dir() -> PathBuf {
std::env::var("XDG_RUNTIME_DIR")
.map(PathBuf::from)
.unwrap_or_else(|_| {
PathBuf::from(format!(
"/tmp/plasmoid-updater-{}",
nix::unistd::Uid::effective()
))
})
}
pub(crate) fn knewstuff_dir() -> PathBuf {
data_home().join("knewstuff3")
}
pub(crate) fn is_kde() -> bool {
knewstuff_dir().exists()
}
static USER_HOME: OnceLock<PathBuf> = OnceLock::new();
fn user_home() -> &'static Path {
USER_HOME.get_or_init(resolve_user_home)
}
fn resolve_user_home() -> PathBuf {
if let Ok(sudo_home) = std::env::var("SUDO_USER_HOME") {
return PathBuf::from(sudo_home);
}
if let Ok(sudo_user) = std::env::var("SUDO_USER") {
if let Ok(output) = std::process::Command::new("getent")
.args(["passwd", &sudo_user])
.output()
&& let Ok(line) = String::from_utf8(output.stdout)
&& let Some(home) = line.split(':').nth(5)
{
return PathBuf::from(home);
}
return PathBuf::from(format!("/home/{}", sudo_user));
}
std::env::var("HOME")
.map(PathBuf::from)
.unwrap_or_else(|_| {
dirs::home_dir().unwrap_or_else(|| {
log::warn!(
target: "paths",
"could not determine home directory; \
defaulting to /tmp/plasmoid-updater-fallback"
);
PathBuf::from("/tmp/plasmoid-updater-fallback")
})
})
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn user_home_returns_consistent_path() {
let first = user_home();
let second = user_home();
assert_eq!(first, second);
assert!(std::ptr::eq(first, second), "should return same &'static ref");
}
#[test]
fn data_home_is_under_user_home_or_xdg() {
let dh = data_home();
if std::env::var("XDG_DATA_HOME").is_ok() {
assert!(!dh.as_os_str().is_empty());
} else {
assert!(dh.starts_with(user_home()));
}
}
#[test]
fn cache_home_is_under_user_home_or_xdg() {
let ch = cache_home();
if std::env::var("XDG_CACHE_HOME").is_ok() {
assert!(!ch.as_os_str().is_empty());
} else {
assert!(ch.starts_with(user_home()));
}
}
}