use std::collections::HashMap;
use std::path::{Path, PathBuf};
use crate::error::{Error, Result};
#[derive(Debug, Clone)]
pub struct SystemContext {
home_dir: PathBuf,
env_vars: HashMap<String, String>,
}
impl SystemContext {
pub fn detect() -> Result<Self> {
let home_dir = dirs::home_dir().ok_or(Error::HomeDirNotFound)?;
let mut env_vars = HashMap::new();
for key in ["SHELL", "ZDOTDIR", "XDG_CONFIG_HOME"] {
if let Ok(val) = std::env::var(key) {
env_vars.insert(key.to_owned(), val);
}
}
Ok(Self { home_dir, env_vars })
}
#[must_use]
pub fn with_home(home_dir: PathBuf) -> Self {
Self {
home_dir,
env_vars: HashMap::new(),
}
}
#[must_use]
pub fn with_home_and_env(home_dir: PathBuf, env_vars: HashMap<String, String>) -> Self {
Self { home_dir, env_vars }
}
#[must_use]
pub fn home_dir(&self) -> &Path {
&self.home_dir
}
pub fn env_var(&self, key: &str) -> Option<&str> {
self.env_vars.get(key).map(String::as_str)
}
pub fn xdg_config_home(&self) -> PathBuf {
self.env_vars
.get("XDG_CONFIG_HOME")
.map_or_else(|| self.home_dir.join(".config"), PathBuf::from)
}
pub fn user_shell_name(&self) -> Option<&str> {
self.env_vars
.get("SHELL")
.map(String::as_str)
.map(|s| s.rsplit('/').next().unwrap_or(s))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn with_home_sets_home_dir() {
let ctx = SystemContext::with_home(PathBuf::from("/fake/home"));
assert_eq!(ctx.home_dir(), Path::new("/fake/home"));
}
#[test]
fn xdg_config_home_defaults_to_dot_config() {
let ctx = SystemContext::with_home(PathBuf::from("/home/user"));
assert_eq!(ctx.xdg_config_home(), PathBuf::from("/home/user/.config"));
}
#[test]
fn xdg_config_home_uses_env_var() {
let mut env = HashMap::new();
env.insert("XDG_CONFIG_HOME".to_owned(), "/custom/config".to_owned());
let ctx = SystemContext::with_home_and_env(PathBuf::from("/home/user"), env);
assert_eq!(ctx.xdg_config_home(), PathBuf::from("/custom/config"));
}
#[test]
fn user_shell_name_extracts_basename() {
let mut env = HashMap::new();
env.insert("SHELL".to_owned(), "/usr/bin/zsh".to_owned());
let ctx = SystemContext::with_home_and_env(PathBuf::from("/home/user"), env);
assert_eq!(ctx.user_shell_name(), Some("zsh"));
}
#[test]
fn user_shell_name_returns_none_when_unset() {
let ctx = SystemContext::with_home(PathBuf::from("/home/user"));
assert_eq!(ctx.user_shell_name(), None);
}
}