fleetflow_config/
lib.rs

1pub mod error;
2
3pub use error::*;
4
5use std::path::PathBuf;
6
7/// FleetFlowの設定ファイルパスを取得
8pub 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
20/// プロジェクトのflow.kdlファイルを探す
21///
22/// 以下の優先順位で設定ファイルを検索:
23/// 1. 環境変数 FLOW_CONFIG_PATH (直接パス指定)
24/// 2. カレントディレクトリ: flow.local.kdl, .flow.local.kdl, flow.kdl, .flow.kdl
25/// 3. ./.fleetflow/ ディレクトリ内: 同様の順序
26/// 4. ~/.config/fleetflow/flow.kdl (グローバル設定)
27pub fn find_flow_file() -> Result<PathBuf> {
28    // 1. 環境変数で直接指定
29    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    // 2. カレントディレクトリで検索
40    for filename in &candidates {
41        let path = current_dir.join(filename);
42        if path.exists() {
43            return Ok(path);
44        }
45    }
46
47    // 3. ./.fleetflow/ ディレクトリで検索
48    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    // 4. グローバル設定ファイル (~/.config/fleetflow/flow.kdl)
59    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    // どの設定ファイルも見つからなかった
67    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        // flow.kdlを作成
91        fs::write(temp_dir.path().join("flow.kdl"), "// test").unwrap();
92
93        // テンポラリディレクトリに移動
94        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        // 元のディレクトリに戻る
103        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        // flow.kdl と flow.local.kdl の両方を作成
112        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        // flow.local.kdl が優先される
120        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        // .fleetflow/ ディレクトリを作成
131        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        // 環境変数を設定
150        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        // クリーンアップ
158        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        // 空のディレクトリに移動
169        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            // 期待通りのエラー
176        } 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        // .flow.local.kdl と flow.kdl を作成
189        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        // .flow.local.kdl が優先される
197        assert!(result.ends_with(".flow.local.kdl"));
198
199        std::env::set_current_dir(original_dir).unwrap();
200    }
201}