use anyhow::Result;
use std::env;
use std::path::{Path, PathBuf};
#[derive(Debug, Clone)]
#[allow(clippy::struct_field_names)]
pub struct Config {
pub root_path: PathBuf,
pub versions_path: PathBuf,
pub cache_path: PathBuf,
}
impl Config {
pub fn new() -> Result<Self> {
let root_path = Self::resolve_root_path()?;
let versions_path = Self::resolve_versions_path(&root_path);
let cache_path = Self::resolve_cache_path(&root_path);
Ok(Config { root_path, versions_path, cache_path })
}
fn resolve_root_path() -> Result<PathBuf> {
if let Ok(env_path) = env::var("GVM_ROOT_PATH") {
return Ok(PathBuf::from(env_path));
}
let home_dir = std::env::var("USERPROFILE")
.or_else(|_| std::env::var("HOME"))
.map(PathBuf::from)
.map_err(|_| anyhow::anyhow!("Unable to get user home directory"))?;
Ok(home_dir.join(".gvm"))
}
fn resolve_versions_path(root_path: &Path) -> PathBuf {
if let Ok(env_path) = env::var("GVM_VERSIONS_PATH") {
return PathBuf::from(env_path);
}
root_path.join("versions")
}
fn resolve_cache_path(root_path: &Path) -> PathBuf {
if let Ok(env_path) = env::var("GVM_CACHE_PATH") {
return PathBuf::from(env_path);
}
root_path.join("cache")
}
#[must_use]
pub fn versions(&self) -> &PathBuf {
&self.versions_path
}
#[must_use]
pub fn cache(&self) -> &PathBuf {
&self.cache_path
}
pub fn ensure_directories(&self) -> Result<()> {
use std::fs;
if !self.root_path.exists() {
fs::create_dir_all(&self.root_path).map_err(|e| {
anyhow::anyhow!(
"Failed to create root directory {}: {}",
self.root_path.display(),
e
)
})?;
}
if !self.versions_path.exists() {
fs::create_dir_all(&self.versions_path).map_err(|e| {
anyhow::anyhow!(
"Failed to create versions directory {}: {}",
self.versions_path.display(),
e
)
})?;
}
if !self.cache_path.exists() {
fs::create_dir_all(&self.cache_path).map_err(|e| {
anyhow::anyhow!(
"Failed to create cache directory {}: {}",
self.cache_path.display(),
e
)
})?;
}
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::env;
#[test]
fn test_config_initialization() {
env::remove_var("GVM_ROOT");
let config = Config::new().unwrap();
assert!(config.root_path.to_string_lossy().contains(".gvm"));
assert!(config.versions().to_string_lossy().contains("versions"));
assert!(config.cache().to_string_lossy().contains("cache"));
}
#[test]
fn test_env_var_priority() {
let test_root = if cfg!(windows) { "C:\\test\\root" } else { "/test/root" };
unsafe {
env::set_var("GVM_ROOT_PATH", test_root);
}
let config = Config::new().unwrap();
assert_eq!(config.root_path, PathBuf::from(test_root));
unsafe {
env::remove_var("GVM_ROOT_PATH");
}
let test_versions = if cfg!(windows) { "C:\\env\\versions" } else { "/env/versions" };
unsafe {
env::set_var("GVM_VERSIONS_PATH", test_versions);
}
let config = Config::new().unwrap();
assert_eq!(config.versions(), &PathBuf::from(test_versions));
unsafe {
env::remove_var("GVM_VERSIONS_PATH");
}
}
#[test]
fn test_config_paths() {
unsafe {
env::remove_var("GVM_ROOT_PATH");
env::remove_var("GVM_VERSIONS_PATH");
env::remove_var("GVM_CACHE_PATH");
}
let config = Config::new().unwrap(); assert!(
config.root_path.is_absolute(),
"Config root path should be absolute, got: {}",
config.root_path.display()
);
assert!(config.versions().to_string_lossy().contains("versions"));
assert!(config.cache().to_string_lossy().contains("cache"));
}
}