Jesús Pérez 9cef9b8d57 refactor: consolidate configuration directories
Merge _configs/ into config/ for single configuration directory.
Update all path references.

Changes:
- Move _configs/* to config/
- Update .gitignore for new patterns
- No code references to _configs/ found

Impact: -1 root directory (layout_conventions.md compliance)
2025-12-26 18:36:23 +00:00

152 lines
4.5 KiB
Rust

//! XDG Base Directory support for Tools ecosystem
//!
//! Provides standard directories for tool-specific configuration and data storage.
//! Follows XDG Base Directory Specification on Linux and macOS.
//!
//! # Examples
//! ```no_run
//! use tools_shared::tool_data_dir;
//!
//! let data_dir = tool_data_dir("my-tool");
//! println!("Data directory: {}", data_dir.display());
//! // Outputs: ~/.local/share/tools/my-tool or $XDG_DATA_HOME/tools/my-tool
//! ```
use std::path::PathBuf;
/// Get XDG data directory for a tool
///
/// Returns: `$XDG_DATA_HOME/tools/{tool_name}/` or `~/.local/share/tools/{tool_name}/`
///
/// # Arguments
/// * `tool_name` - Name of the tool (e.g., "project-lifecycle")
///
/// # Panics
/// Panics if home directory cannot be determined
pub fn tool_data_dir(tool_name: &str) -> PathBuf {
dirs::data_dir()
.map(|d| d.join("tools").join(tool_name))
.unwrap_or_else(|| {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
PathBuf::from(home)
.join(".local/share/tools")
.join(tool_name)
})
}
/// Get XDG config directory for a tool
///
/// Returns: `$XDG_CONFIG_HOME/tools/{tool_name}/` or `~/.config/tools/{tool_name}/`
///
/// # Arguments
/// * `tool_name` - Name of the tool (e.g., "project-lifecycle")
///
/// # Panics
/// Panics if home directory cannot be determined
pub fn tool_config_dir(tool_name: &str) -> PathBuf {
dirs::config_dir()
.map(|d| d.join("tools").join(tool_name))
.unwrap_or_else(|| {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
PathBuf::from(home).join(".config/tools").join(tool_name)
})
}
/// Get XDG cache directory for a tool
///
/// Returns: `$XDG_CACHE_HOME/tools/{tool_name}/` or `~/.cache/tools/{tool_name}/`
///
/// # Arguments
/// * `tool_name` - Name of the tool (e.g., "project-lifecycle")
///
/// # Panics
/// Panics if home directory cannot be determined
pub fn tool_cache_dir(tool_name: &str) -> PathBuf {
dirs::cache_dir()
.map(|d| d.join("tools").join(tool_name))
.unwrap_or_else(|| {
let home = std::env::var("HOME").unwrap_or_else(|_| ".".to_string());
PathBuf::from(home).join(".cache/tools").join(tool_name)
})
}
/// Ensure directory exists, creating it if necessary
///
/// # Arguments
/// * `path` - Path to directory
///
/// # Errors
/// Returns error if directory cannot be created
///
/// # Examples
/// ```no_run
/// use tools_shared::{tool_data_dir, ensure_dir};
///
/// let dir = tool_data_dir("my-tool");
/// ensure_dir(&dir).expect("Failed to create directory");
/// ```
pub fn ensure_dir(path: &PathBuf) -> std::io::Result<PathBuf> {
if !path.exists() {
std::fs::create_dir_all(path)?;
}
Ok(path.clone())
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_tool_data_dir() {
let data_dir = tool_data_dir("test-tool");
assert!(data_dir.to_string_lossy().contains("test-tool"));
assert!(
data_dir.to_string_lossy().contains("tools")
|| data_dir.to_string_lossy().contains(".local")
);
}
#[test]
fn test_tool_config_dir() {
let config_dir = tool_config_dir("test-tool");
assert!(config_dir.to_string_lossy().contains("test-tool"));
assert!(
config_dir.to_string_lossy().contains("tools")
|| config_dir.to_string_lossy().contains(".config")
);
}
#[test]
fn test_tool_cache_dir() {
let cache_dir = tool_cache_dir("test-tool");
assert!(cache_dir.to_string_lossy().contains("test-tool"));
assert!(
cache_dir.to_string_lossy().contains("tools")
|| cache_dir.to_string_lossy().contains(".cache")
);
}
#[test]
fn test_ensure_dir_creates_directory() {
use tempfile::TempDir;
let temp = TempDir::new().expect("Failed to create temp dir");
let test_dir = temp.path().join("test").join("nested").join("dir");
ensure_dir(&test_dir).expect("Failed to ensure dir");
assert!(test_dir.exists());
}
#[test]
fn test_ensure_dir_existing() {
use tempfile::TempDir;
let temp = TempDir::new().expect("Failed to create temp dir");
let test_dir = temp.path().join("test");
std::fs::create_dir(&test_dir).expect("Failed to create dir");
ensure_dir(&test_dir).expect("Failed to ensure dir");
assert!(test_dir.exists());
}
}