use anyhow::Context;
use std::path::PathBuf;
pub fn data_dir() -> anyhow::Result<PathBuf> {
if let Ok(custom) = std::env::var("TASK_JOURNAL_DATA_DIR") {
if !custom.is_empty() {
return Ok(PathBuf::from(custom));
}
}
if let Ok(xdg) = std::env::var("XDG_DATA_HOME") {
if !xdg.is_empty() {
return Ok(PathBuf::from(xdg).join("task-journal"));
}
}
let dirs = directories::ProjectDirs::from("", "", "task-journal")
.context("could not resolve OS data directories")?;
Ok(dirs.data_local_dir().to_path_buf())
}
pub fn events_dir() -> anyhow::Result<PathBuf> {
Ok(data_dir()?.join("events"))
}
pub fn state_dir() -> anyhow::Result<PathBuf> {
Ok(data_dir()?.join("state"))
}
pub fn metrics_dir() -> anyhow::Result<PathBuf> {
Ok(data_dir()?.join("metrics"))
}
pub fn project_storage_dir(project_hash: &str) -> anyhow::Result<PathBuf> {
Ok(data_dir()?.join(project_hash))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_dir_returns_a_path_containing_task_journal() {
let p = data_dir().expect("data_dir");
let s = p.to_string_lossy();
assert!(s.contains("task-journal"), "got: {s}");
}
#[test]
fn project_dir_appends_subdir() {
let p = project_storage_dir("abc123").expect("project dir");
assert!(p.ends_with("abc123"), "got: {p:?}");
}
#[test]
#[cfg_attr(
not(unix),
ignore = "env semantics differ on Windows test runners; covered by integration tests"
)]
fn env_overrides_take_precedence() {
let prev_tjdd = std::env::var("TASK_JOURNAL_DATA_DIR").ok();
let prev_xdg = std::env::var("XDG_DATA_HOME").ok();
unsafe { std::env::remove_var("TASK_JOURNAL_DATA_DIR") };
unsafe { std::env::set_var("XDG_DATA_HOME", "/tmp/tj-paths-test-xdg") };
assert_eq!(
data_dir().unwrap(),
PathBuf::from("/tmp/tj-paths-test-xdg/task-journal")
);
unsafe { std::env::set_var("TASK_JOURNAL_DATA_DIR", "/tmp/tj-paths-test-explicit") };
assert_eq!(
data_dir().unwrap(),
PathBuf::from("/tmp/tj-paths-test-explicit")
);
unsafe { std::env::remove_var("TASK_JOURNAL_DATA_DIR") };
unsafe { std::env::remove_var("XDG_DATA_HOME") };
if let Some(v) = prev_tjdd {
unsafe { std::env::set_var("TASK_JOURNAL_DATA_DIR", v) };
}
if let Some(v) = prev_xdg {
unsafe { std::env::set_var("XDG_DATA_HOME", v) };
}
}
}