use std::ffi::OsString;
use std::path::{Path, PathBuf};
use thiserror::Error;
const APP_DIR: &str = "openlogi";
#[derive(Debug, Error)]
pub enum PathsError {
#[error("could not resolve a home directory for the current user")]
HomeNotFound,
}
fn home() -> Result<PathBuf, PathsError> {
std::env::var_os("HOME")
.or_else(|| std::env::var_os("USERPROFILE"))
.filter(|h| !h.is_empty())
.map(PathBuf::from)
.ok_or(PathsError::HomeNotFound)
}
fn xdg_base(env_value: Option<OsString>, fallback: &[&str]) -> Result<PathBuf, PathsError> {
match env_value {
Some(v) if Path::new(&v).is_absolute() => Ok(PathBuf::from(v).join(APP_DIR)),
_ => {
let mut dir = home()?;
dir.extend(fallback);
dir.push(APP_DIR);
Ok(dir)
}
}
}
pub fn xdg_config_home() -> Result<PathBuf, PathsError> {
match std::env::var_os("XDG_CONFIG_HOME") {
Some(v) if Path::new(&v).is_absolute() => Ok(PathBuf::from(v)),
_ => Ok(home()?.join(".config")),
}
}
pub fn config_dir() -> Result<PathBuf, PathsError> {
Ok(xdg_config_home()?.join(APP_DIR))
}
pub fn config_path() -> Result<PathBuf, PathsError> {
Ok(config_dir()?.join("config.toml"))
}
pub fn data_dir() -> Result<PathBuf, PathsError> {
xdg_base(std::env::var_os("XDG_DATA_HOME"), &[".local", "share"])
}
fn runtime_base(env_value: Option<OsString>) -> Result<PathBuf, PathsError> {
match env_value {
Some(v) if Path::new(&v).is_absolute() => Ok(PathBuf::from(v).join(APP_DIR)),
_ => config_dir(),
}
}
pub fn runtime_dir() -> Result<PathBuf, PathsError> {
runtime_base(std::env::var_os("XDG_RUNTIME_DIR"))
}
pub fn agent_socket_path() -> Result<PathBuf, PathsError> {
Ok(runtime_dir()?.join("agent.sock"))
}
#[cfg(all(test, unix))]
#[allow(clippy::expect_used, reason = "expect/unwrap are idiomatic in tests")]
mod tests {
use super::*;
#[test]
fn absolute_xdg_override_is_used_verbatim() {
let dir = xdg_base(Some("/tmp/xdg-config".into()), &[".config"])
.expect("absolute override needs no home dir");
assert_eq!(dir, PathBuf::from("/tmp/xdg-config/openlogi"));
}
#[test]
fn relative_xdg_value_is_ignored_per_spec() {
let dir = xdg_base(Some("relative/dir".into()), &[".config"]).expect("home dir resolves");
assert!(dir.ends_with("openlogi"));
assert!(!dir.to_string_lossy().contains("relative"));
}
#[test]
fn absolute_runtime_dir_is_used_verbatim() {
let dir = runtime_base(Some("/run/user/501".into())).expect("absolute override");
assert_eq!(dir, PathBuf::from("/run/user/501/openlogi"));
}
#[test]
fn relative_runtime_dir_falls_back_to_config() {
let dir = runtime_base(Some("relative/run".into())).expect("config dir resolves");
assert!(dir.ends_with("openlogi"));
assert!(!dir.to_string_lossy().contains("relative"));
}
}