use std::path::{Path, PathBuf};
use crate::error::{self, Error, Result};
pub const HOME_ENV: &str = "ONEVCS_HOME";
pub fn root() -> Result<PathBuf> {
match std::env::var_os(HOME_ENV) {
Some(value) if value.is_empty() => Err(Error::Invalid {
reason: format!("{HOME_ENV} is set but empty; unset it or give it a directory"),
}),
Some(value) => Ok(PathBuf::from(value)),
None => home_directory()
.map(|home| home.join(".onevcs"))
.ok_or_else(|| Error::Invalid {
reason: format!("cannot find a home directory; set {HOME_ENV} to a directory"),
}),
}
}
#[cfg(unix)]
fn home_directory() -> Option<PathBuf> {
std::env::var_os("HOME")
.filter(|v| !v.is_empty())
.map(PathBuf::from)
}
#[cfg(windows)]
fn home_directory() -> Option<PathBuf> {
std::env::var_os("USERPROFILE")
.filter(|v| !v.is_empty())
.map(PathBuf::from)
}
pub fn registry_path() -> Result<PathBuf> {
Ok(root()?.join("registry.json"))
}
pub fn locks_dir() -> Result<PathBuf> {
Ok(root()?.join("locks"))
}
pub fn sessions_dir() -> Result<PathBuf> {
Ok(root()?.join("sessions"))
}
pub fn workspaces_dir() -> Result<PathBuf> {
Ok(root()?.join("workspaces"))
}
pub fn streams_dir() -> Result<PathBuf> {
Ok(root()?.join("streams"))
}
pub fn artifacts_dir() -> Result<PathBuf> {
Ok(root()?.join("artifacts"))
}
pub fn expand_tilde(value: &str) -> PathBuf {
match (value.strip_prefix("~/"), home_directory()) {
(Some(rest), Some(home)) => home.join(rest),
_ => PathBuf::from(value),
}
}
pub fn ensure_dir(path: &Path) -> Result<()> {
std::fs::create_dir_all(path).map_err(error::at("create", path))
}
pub fn atomic_write(path: &Path, contents: &str) -> Result<()> {
let parent = path.parent().unwrap_or(Path::new("."));
ensure_dir(parent)?;
let temporary = parent.join(format!(".{}.{}", file_name(path), crate::ids::unique()));
std::fs::write(&temporary, contents).map_err(error::at("write", &temporary))?;
let replaced = std::fs::rename(&temporary, path).map_err(error::at("replace", path));
let _ = std::fs::remove_file(&temporary);
replaced
}
fn file_name(path: &Path) -> String {
path.file_name()
.map(|name| name.to_string_lossy().into_owned())
.unwrap_or_else(|| "file".to_owned())
}