1use crate::{PathManager, VxPaths};
4use anyhow::Result;
5use serde::{Deserialize, Serialize};
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct PathConfig {
11 pub base_dir: Option<PathBuf>,
13 pub tools_dir: Option<PathBuf>,
15 pub cache_dir: Option<PathBuf>,
17 pub config_dir: Option<PathBuf>,
19 pub tmp_dir: Option<PathBuf>,
21}
22
23impl PathConfig {
24 pub fn new() -> Self {
26 Self {
27 base_dir: None,
28 tools_dir: None,
29 cache_dir: None,
30 config_dir: None,
31 tmp_dir: None,
32 }
33 }
34
35 pub fn with_base_dir<P: AsRef<Path>>(base_dir: P) -> Self {
37 Self {
38 base_dir: Some(base_dir.as_ref().to_path_buf()),
39 tools_dir: None,
40 cache_dir: None,
41 config_dir: None,
42 tmp_dir: None,
43 }
44 }
45
46 pub fn create_path_manager(&self) -> Result<PathManager> {
48 let paths = self.create_vx_paths()?;
49 paths.ensure_dirs()?;
50 Ok(PathManager::from_paths(paths))
51 }
52
53 pub fn create_vx_paths(&self) -> Result<VxPaths> {
55 let default_paths = if let Some(base_dir) = &self.base_dir {
56 VxPaths::with_base_dir(base_dir)
57 } else {
58 VxPaths::new()?
59 };
60
61 Ok(VxPaths {
62 base_dir: self.base_dir.clone().unwrap_or(default_paths.base_dir),
63 tools_dir: self.tools_dir.clone().unwrap_or(default_paths.tools_dir),
64 cache_dir: self.cache_dir.clone().unwrap_or(default_paths.cache_dir),
65 config_dir: self.config_dir.clone().unwrap_or(default_paths.config_dir),
66 tmp_dir: self.tmp_dir.clone().unwrap_or(default_paths.tmp_dir),
67 })
68 }
69
70 pub fn from_env() -> Self {
72 Self {
73 base_dir: std::env::var("VX_BASE_DIR").ok().map(PathBuf::from),
74 tools_dir: std::env::var("VX_TOOLS_DIR").ok().map(PathBuf::from),
75 cache_dir: std::env::var("VX_CACHE_DIR").ok().map(PathBuf::from),
76 config_dir: std::env::var("VX_CONFIG_DIR").ok().map(PathBuf::from),
77 tmp_dir: std::env::var("VX_TMP_DIR").ok().map(PathBuf::from),
78 }
79 }
80
81 pub fn merge(&mut self, other: &PathConfig) {
83 if other.base_dir.is_some() {
84 self.base_dir = other.base_dir.clone();
85 }
86 if other.tools_dir.is_some() {
87 self.tools_dir = other.tools_dir.clone();
88 }
89 if other.cache_dir.is_some() {
90 self.cache_dir = other.cache_dir.clone();
91 }
92 if other.config_dir.is_some() {
93 self.config_dir = other.config_dir.clone();
94 }
95 if other.tmp_dir.is_some() {
96 self.tmp_dir = other.tmp_dir.clone();
97 }
98 }
99}
100impl Default for PathConfig {
101 fn default() -> Self {
102 Self::new()
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use super::*;
109 use tempfile::TempDir;
110
111 #[test]
112 fn test_path_config_creation() {
113 let config = PathConfig::new();
114 assert!(config.base_dir.is_none());
115 assert!(config.tools_dir.is_none());
116
117 let temp_dir = TempDir::new().unwrap();
118 let config = PathConfig::with_base_dir(temp_dir.path());
119 assert_eq!(config.base_dir, Some(temp_dir.path().to_path_buf()));
120 }
121
122 #[test]
123 fn test_path_config_merge() {
124 let mut config1 = PathConfig::new();
125 let config2 = PathConfig::with_base_dir("/custom/path");
126
127 config1.merge(&config2);
128 assert_eq!(config1.base_dir, Some(PathBuf::from("/custom/path")));
129 }
130
131 #[test]
132 fn test_create_path_manager() {
133 let temp_dir = TempDir::new().unwrap();
134 let config = PathConfig::with_base_dir(temp_dir.path());
135 let manager = config.create_path_manager().unwrap();
136
137 assert!(manager.tools_dir().exists());
138 assert!(manager.cache_dir().exists());
139 }
140
141 #[test]
142 fn test_from_env() {
143 std::env::set_var("VX_BASE_DIR", "/test/base");
144 std::env::set_var("VX_TOOLS_DIR", "/test/tools");
145
146 let config = PathConfig::from_env();
147 assert_eq!(config.base_dir, Some(PathBuf::from("/test/base")));
148 assert_eq!(config.tools_dir, Some(PathBuf::from("/test/tools")));
149
150 std::env::remove_var("VX_BASE_DIR");
151 std::env::remove_var("VX_TOOLS_DIR");
152 }
153}