use anyhow::{Context, Result};
use std::path::PathBuf;
pub fn spm_home() -> Result<PathBuf> {
if let Ok(custom) = std::env::var("SPM_HOME") {
return Ok(PathBuf::from(custom));
}
let base = directories::BaseDirs::new().context("could not determine home directory")?;
Ok(base.home_dir().join(".spm"))
}
pub fn store_dir() -> Result<PathBuf> {
Ok(spm_home()?.join("store"))
}
#[cfg(test)]
pub(crate) static ENV_LOCK: std::sync::Mutex<()> = std::sync::Mutex::new(());
#[cfg(test)]
pub(crate) struct SpmHomeGuard {
_lock: std::sync::MutexGuard<'static, ()>,
saved: Option<String>,
}
#[cfg(test)]
impl SpmHomeGuard {
pub(crate) fn set(value: &std::path::Path) -> Self {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let saved = std::env::var("SPM_HOME").ok();
std::env::set_var("SPM_HOME", value);
Self { _lock, saved }
}
pub(crate) fn unset() -> Self {
let _lock = ENV_LOCK.lock().unwrap_or_else(|e| e.into_inner());
let saved = std::env::var("SPM_HOME").ok();
std::env::remove_var("SPM_HOME");
Self { _lock, saved }
}
}
#[cfg(test)]
impl Drop for SpmHomeGuard {
fn drop(&mut self) {
match &self.saved {
Some(v) => std::env::set_var("SPM_HOME", v),
None => std::env::remove_var("SPM_HOME"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn spm_home_and_store_dir_default_when_env_unset() {
let _guard = SpmHomeGuard::unset();
let home = spm_home().expect("must fall back to the real home dir");
assert!(
home.ends_with(".spm"),
"expected a `.spm` suffix, got {home:?}"
);
let store = store_dir().expect("store_dir must derive from spm_home");
assert!(store.ends_with(".spm/store") || store.ends_with(".spm\\store"));
}
#[test]
fn spm_home_honors_custom_env_var() {
let custom = std::env::temp_dir().join("custom-spm-home-for-test");
let _guard = SpmHomeGuard::set(&custom);
let home = spm_home().unwrap();
assert_eq!(home, custom);
}
}