1pub mod error;
2
3pub use error::*;
4
5use std::path::PathBuf;
6
7pub fn get_config_dir() -> Result<PathBuf> {
9 let config_dir = dirs::config_dir()
10 .ok_or(ConfigError::ConfigDirNotFound)?
11 .join("fleetflow");
12
13 if !config_dir.exists() {
14 std::fs::create_dir_all(&config_dir)?;
15 }
16
17 Ok(config_dir)
18}
19
20pub fn find_flow_file() -> Result<PathBuf> {
28 if let Ok(config_path) = std::env::var("FLOW_CONFIG_PATH") {
30 let path = PathBuf::from(config_path);
31 if path.exists() {
32 return Ok(path);
33 }
34 }
35
36 let current_dir = std::env::current_dir()?;
37 let candidates = ["flow.local.kdl", ".flow.local.kdl", "flow.kdl", ".flow.kdl"];
38
39 for filename in &candidates {
41 let path = current_dir.join(filename);
42 if path.exists() {
43 return Ok(path);
44 }
45 }
46
47 let flow_dir = current_dir.join(".fleetflow");
49 if flow_dir.is_dir() {
50 for filename in &candidates {
51 let path = flow_dir.join(filename);
52 if path.exists() {
53 return Ok(path);
54 }
55 }
56 }
57
58 if let Some(config_dir) = dirs::config_dir() {
60 let global_config = config_dir.join("fleetflow").join("flow.kdl");
61 if global_config.exists() {
62 return Ok(global_config);
63 }
64 }
65
66 Err(ConfigError::FlowFileNotFound)
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73 use std::fs;
74
75 #[test]
76 fn test_get_config_dir() {
77 let result = get_config_dir();
78 assert!(result.is_ok());
79
80 let config_dir = result.unwrap();
81 assert!(config_dir.ends_with("fleetflow"));
82 assert!(config_dir.exists());
83 }
84
85 #[test]
86 fn test_find_flow_file_in_current_dir() {
87 let temp_dir = tempfile::tempdir().unwrap();
88 let original_dir = std::env::current_dir().unwrap();
89
90 fs::write(temp_dir.path().join("flow.kdl"), "// test").unwrap();
92
93 std::env::set_current_dir(&temp_dir).unwrap();
95
96 let result = find_flow_file();
97 assert!(result.is_ok());
98
99 let flow_file = result.unwrap();
100 assert!(flow_file.ends_with("flow.kdl"));
101
102 std::env::set_current_dir(original_dir).unwrap();
104 }
105
106 #[test]
107 fn test_find_flow_file_local_priority() {
108 let temp_dir = tempfile::tempdir().unwrap();
109 let original_dir = std::env::current_dir().unwrap();
110
111 fs::write(temp_dir.path().join("flow.kdl"), "// global").unwrap();
113 fs::write(temp_dir.path().join("flow.local.kdl"), "// local").unwrap();
114
115 std::env::set_current_dir(&temp_dir).unwrap();
116
117 let result = find_flow_file().unwrap();
118
119 assert!(result.ends_with("flow.local.kdl"));
121
122 std::env::set_current_dir(original_dir).unwrap();
123 }
124
125 #[test]
126 fn test_find_flow_file_in_flow_dir() {
127 let temp_dir = tempfile::tempdir().unwrap();
128 let original_dir = std::env::current_dir().unwrap();
129
130 let flow_dir = temp_dir.path().join(".fleetflow");
132 fs::create_dir(&flow_dir).unwrap();
133 fs::write(flow_dir.join("flow.kdl"), "// in flow dir").unwrap();
134
135 std::env::set_current_dir(&temp_dir).unwrap();
136
137 let result = find_flow_file().unwrap();
138 assert!(result.ends_with(".fleetflow/flow.kdl"));
139
140 std::env::set_current_dir(original_dir).unwrap();
141 }
142
143 #[test]
144 fn test_find_flow_file_env_var() {
145 let temp_dir = tempfile::tempdir().unwrap();
146 let config_path = temp_dir.path().join("custom.kdl");
147 fs::write(&config_path, "// custom").unwrap();
148
149 unsafe {
151 std::env::set_var("FLOW_CONFIG_PATH", config_path.to_str().unwrap());
152 }
153
154 let result = find_flow_file().unwrap();
155 assert_eq!(result, config_path);
156
157 unsafe {
159 std::env::remove_var("FLOW_CONFIG_PATH");
160 }
161 }
162
163 #[test]
164 fn test_find_flow_file_not_found() {
165 let temp_dir = tempfile::tempdir().unwrap();
166 let original_dir = std::env::current_dir().unwrap();
167
168 std::env::set_current_dir(&temp_dir).unwrap();
170
171 let result = find_flow_file();
172 assert!(result.is_err());
173
174 if let Err(ConfigError::FlowFileNotFound) = result {
175 } else {
177 panic!("Expected FlowFileNotFound error");
178 }
179
180 std::env::set_current_dir(original_dir).unwrap();
181 }
182
183 #[test]
184 fn test_hidden_file_priority() {
185 let temp_dir = tempfile::tempdir().unwrap();
186 let original_dir = std::env::current_dir().unwrap();
187
188 fs::write(temp_dir.path().join(".flow.local.kdl"), "// hidden local").unwrap();
190 fs::write(temp_dir.path().join("flow.kdl"), "// visible").unwrap();
191
192 std::env::set_current_dir(&temp_dir).unwrap();
193
194 let result = find_flow_file().unwrap();
195
196 assert!(result.ends_with(".flow.local.kdl"));
198
199 std::env::set_current_dir(original_dir).unwrap();
200 }
201}