use anyhow::{Context, Result};
use std::path::PathBuf;
#[derive(Debug)]
pub struct Config {
pub root: PathBuf,
}
impl Config {
pub fn load() -> Result<Self> {
let root = match std::env::var("GVSN_DIR") {
Ok(dir) => PathBuf::from(dir),
Err(_) => dirs::home_dir()
.context("Cannot determine home directory")?
.join(".gvsn"),
};
Ok(Self { root })
}
pub fn versions_dir(&self) -> PathBuf {
self.root.join("versions")
}
pub fn tmp_dir(&self) -> PathBuf {
self.root.join("tmp")
}
pub fn version_file(&self) -> PathBuf {
self.root.join("version")
}
pub fn version_dir(&self, tag: &str) -> PathBuf {
self.versions_dir().join(tag)
}
pub fn version_bin_dir(&self, tag: &str) -> PathBuf {
self.version_dir(tag).join("bin")
}
pub fn current_dir(&self) -> PathBuf {
self.root.join("current")
}
pub fn default_packages_file(&self) -> PathBuf {
self.root.join("default-packages")
}
}
pub trait ConfigMut {
fn ensure_dirs(&self) -> anyhow::Result<()>;
}
impl ConfigMut for Config {
fn ensure_dirs(&self) -> anyhow::Result<()> {
std::fs::create_dir_all(self.versions_dir())
.context("Failed to create versions directory")?;
std::fs::create_dir_all(self.tmp_dir()).context("Failed to create tmp directory")?;
Ok(())
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::tempdir;
#[test]
fn config_load_uses_gvsn_dir() {
let dir = tempdir().unwrap();
std::env::set_var("GVSN_DIR", dir.path());
let config = Config::load().unwrap();
assert_eq!(config.root, dir.path());
std::env::remove_var("GVSN_DIR");
}
#[test]
fn config_load_defaults_to_home_gvsn() {
std::env::remove_var("GVSN_DIR");
let config = Config::load().unwrap();
assert!(config.root.ends_with(".gvsn"));
}
#[test]
fn config_paths_are_derived_from_root() {
let dir = tempdir().unwrap();
let config = Config {
root: dir.path().to_path_buf(),
};
assert_eq!(config.versions_dir(), dir.path().join("versions"));
assert_eq!(config.tmp_dir(), dir.path().join("tmp"));
assert_eq!(config.version_file(), dir.path().join("version"));
assert_eq!(
config.version_dir("go1.22.4"),
dir.path().join("versions/go1.22.4")
);
assert_eq!(
config.version_bin_dir("go1.22.4"),
dir.path().join("versions/go1.22.4/bin")
);
assert_eq!(config.current_dir(), dir.path().join("current"));
assert_eq!(
config.default_packages_file(),
dir.path().join("default-packages")
);
}
#[test]
fn config_ensure_dirs_creates_directories() {
let dir = tempdir().unwrap();
let config = Config {
root: dir.path().to_path_buf(),
};
config.ensure_dirs().unwrap();
assert!(dir.path().join("versions").exists());
assert!(dir.path().join("tmp").exists());
}
#[test]
fn config_ensure_dirs_is_idempotent() {
let dir = tempdir().unwrap();
let config = Config {
root: dir.path().to_path_buf(),
};
config.ensure_dirs().unwrap();
config.ensure_dirs().unwrap();
assert!(dir.path().join("versions").exists());
assert!(dir.path().join("tmp").exists());
}
}